rhei-cli 0.3.0

Command-line driver for the Rhei agent runtime.
Documentation
    fn poll_machine(body: &str) -> String {
        format!(
            r#"
name: poll-test
version: 1.0
states:
{body}
profiles:
  default:
    initial: ci-wait
    allowed: [ci-wait, done]
node_policy:
  root: default
  default: default
"#
        )
    }

    #[test]
    fn accepts_well_formed_poll_state() {
        let yaml = poll_machine(
            r#"  ci-wait:
    description: Wait for CI
    program: "./check.sh"
    poll:
      interval: 5m
      max_attempts: 12
  done:
    description: Done
    final: true
transitions:
  - from: ci-wait
    to: ci-wait
    exit_code: 75
  - from: ci-wait
    to: done
    exit_code: 0"#,
        );
        let sm = StateMachine::from_yaml_str(&yaml).expect("valid poll state");
        let poll = sm.states.get("ci-wait").and_then(|s| s.poll.as_ref()).expect("poll present");
        assert_eq!(poll.max_attempts, 12);
        assert_eq!(poll.interval, "5m");
    }

    #[test]
    fn rejects_poll_with_invalid_interval() {
        let yaml = poll_machine(
            r#"  ci-wait:
    description: Wait
    program: "./check.sh"
    poll:
      interval: sometimes
      max_attempts: 3
  done: { description: Done, final: true }
transitions:
  - from: ci-wait
    to: ci-wait
    exit_code: 75"#,
        );
        let err = StateMachine::from_yaml_str(&yaml).expect_err("bad interval");
        assert!(err.to_string().contains("poll.interval"));
    }

    #[test]
    fn rejects_poll_with_zero_max_attempts() {
        let yaml = poll_machine(
            r#"  ci-wait:
    description: Wait
    program: "./check.sh"
    poll:
      interval: 1m
      max_attempts: 0
  done: { description: Done, final: true }
transitions:
  - from: ci-wait
    to: ci-wait
    exit_code: 75"#,
        );
        let err = StateMachine::from_yaml_str(&yaml).expect_err("bad max_attempts");
        assert!(err.to_string().contains("poll.max_attempts"));
    }

    #[test]
    fn rejects_poll_with_visits() {
        let yaml = poll_machine(
            r#"  ci-wait:
    description: Wait
    program: "./check.sh"
    visits: 5
    poll:
      interval: 1m
      max_attempts: 3
  done: { description: Done, final: true }
transitions:
  - from: ci-wait
    to: ci-wait
    exit_code: 75"#,
        );
        let err = StateMachine::from_yaml_str(&yaml).expect_err("visits conflict");
        assert!(err.to_string().contains("'poll' and 'visits'"));
    }

    #[test]
    fn rejects_poll_on_gating_state() {
        let yaml = poll_machine(
            r#"  ci-wait:
    description: Wait
    gating: true
    poll:
      interval: 1m
      max_attempts: 3
  done: { description: Done, final: true }
transitions:
  - from: ci-wait
    to: ci-wait"#,
        );
        let err = StateMachine::from_yaml_str(&yaml).expect_err("gating conflict");
        assert!(err.to_string().contains("gating"));
    }

    #[test]
    fn rejects_poll_without_self_loop() {
        let yaml = poll_machine(
            r#"  ci-wait:
    description: Wait
    program: "./check.sh"
    poll:
      interval: 1m
      max_attempts: 3
  done: { description: Done, final: true }
transitions:
  - from: ci-wait
    to: done
    exit_code: 0"#,
        );
        let err = StateMachine::from_yaml_str(&yaml).expect_err("missing self-loop");
        assert!(err.to_string().contains("self-loop"));
    }

    #[test]
    fn rejects_poll_with_snapshot_inherit() {
        let yaml = poll_machine(
            r#"  ci-wait:
    description: Wait
    program: "./check.sh"
    poll:
      interval: 1m
      max_attempts: 3
    snapshot:
      inherit:
        name: build
  done: { description: Done, final: true }
transitions:
  - from: ci-wait
    to: ci-wait
    exit_code: 75"#,
        );
        let err = StateMachine::from_yaml_str(&yaml).expect_err("snapshot inherit conflict");
        assert!(err.to_string().contains("snapshot.inherit"));
    }