auv-cli-invoke 0.0.22

Application Use Via... script-able OS automation library
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::Arc;

use auv_cli_invoke::{InvokeCancellation, InvokeCommandInput, InvokeNamespace, default_registry, render_command_help};
use auv_tracing::{Context, MemoryTracingStore, RunId, TraceRecord, configure, dispatcher};

fn fixture(kind: &str, name: &str) -> PathBuf {
  PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..").join("auv-scan").join("tests").join("testdata").join("scan").join(kind).join(name)
}

#[test]
fn scan_commands_expose_public_metadata_and_help() {
  let registry = default_registry();
  let frame = registry.resolve("scan.frame").expect("scan.frame");
  let coverage = registry.resolve("scan.coverage").expect("scan.coverage");

  assert_eq!(frame.namespace, InvokeNamespace::Scan);
  assert_eq!(coverage.namespace, InvokeNamespace::Scan);
  let help = render_command_help(coverage);
  assert!(help.contains("coverage scenario manifest"));
  assert!(help.contains("frame_fixture cross-reference"));
}

#[test]
fn scan_commands_return_direct_values_and_record_owned_artifacts() {
  futures_executor::block_on(async {
    let registry = default_registry();
    for (command_id, fixture_dir, expected_purposes) in [
      ("scan.frame", fixture("temporal", "single_frame_v0"), vec!["auv.scan.frame", "auv.scan.frame_image"]),
      ("scan.coverage", fixture("coverage", "coverage_stable_v0"), vec!["auv.runtime.scan_coverage"]),
    ] {
      let store = Arc::new(MemoryTracingStore::new());
      let dispatch = configure().tracing_store(store.clone()).build().expect("memory tracing dispatch");
      let root = dispatcher::with_default(&dispatch, || Context::root(RunId::new()));
      let command = registry.resolve(command_id).expect("registered scan command").clone();
      let future = root.in_scope(|| {
        command.invoke(InvokeCommandInput {
          command_id: command_id.to_string(),
          target: None,
          inputs: BTreeMap::from([("fixture-dir".to_string(), fixture_dir.to_string_lossy().into_owned())]),
          typed_args: None,
          dry_run: false,
          cancellation: InvokeCancellation::new(),
        })
      });
      let output = root.instrument(future).await.expect("scan command");
      dispatch.flush().await.expect("flush tracing");

      assert!(output.report.is_some());
      let mut purposes = store
        .records()
        .into_iter()
        .filter_map(|record| match record {
          TraceRecord::Artifact { metadata, .. } => Some(metadata.purpose().as_str().to_string()),
          _ => None,
        })
        .collect::<Vec<_>>();
      purposes.sort();
      let mut expected_purposes = expected_purposes;
      expected_purposes.sort();
      assert_eq!(purposes, expected_purposes);
    }
  });
}