//! Integration tests for the `asmref` command. use assert_cmd::Command; use predicates::prelude::*; use serde_json::Value; use std::path::PathBuf; fn cargo_command() -> Command { Command::cargo_bin("asmref").expect("binary") } fn workspace_root() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("..") .join("..") .canonicalize() .expect("workspace root") } fn managed_fixture_dir() -> PathBuf { workspace_root() .join("fixtures") .join("managed") .join("bin") } fn fixture_assembly() -> PathBuf { managed_fixture_dir().join("GameAssembly.dll") } fn diagnose_fixture_root() -> PathBuf { workspace_root() .join("fixtures") .join("managed") .join("diagnose-bin") .join("root") .join("RootPlugin.dll") } fn diagnose_server_a_dir() -> PathBuf { workspace_root() .join("fixtures") .join("managed") .join("diagnose-bin") .join("server-a") } fn diagnose_server_b_dir() -> PathBuf { workspace_root() .join("fixtures") .join("managed") .join("diagnose-bin") .join("server-b") } fn framework_reference_free_assembly() -> PathBuf { PathBuf::from(r"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorlib.dll") } #[test] fn help_includes_examples_and_resolve_dir_usage() { let mut command = cargo_command(); command .arg("--help") .assert() .success() .stdout(predicate::str::contains("--resolve-dir")) .stdout(predicate::str::contains("ConvertFrom-Json")); } #[test] fn emits_reference_resolution_as_json() { let fixture_dir = managed_fixture_dir(); let mut command = cargo_command(); let output = command .arg(fixture_assembly()) .arg("--resolve-dir") .arg(&fixture_dir) .arg("--json") .assert() .success() .get_output() .stdout .clone(); let payload = serde_json::from_slice::(&output).expect("json payload"); let refs = payload["references"].as_array().expect("references array"); assert!( refs.iter().any(|entry| { entry["name"] == "FixtureSupport" && entry["resolved"] == true && entry["resolved_path"] .as_str() .is_some_and(|value: &str| value.ends_with("FixtureSupport.dll")) }), "expected FixtureSupport reference to resolve" ); } #[test] fn text_mode_reports_zero_reference_empty_state() { let mut command = cargo_command(); command .arg(framework_reference_free_assembly()) .assert() .success() .stdout(predicate::str::contains("references=0")); } #[test] fn supports_path_input_from_powershell_pipeline() { let binary = assert_cmd::cargo::cargo_bin("asmref"); let input = fixture_assembly(); let fixture_dir = managed_fixture_dir(); let script = format!( "'{}' | & '{}' --input-format lines --resolve-dir '{}' --json", input.display(), binary.display(), fixture_dir.display() ); let mut command = Command::new("pwsh"); command .arg("-NoProfile") .arg("-Command") .arg(script) .assert() .success() .stdout(predicate::str::contains("\"name\":\"FixtureSupport\"")); } #[test] fn diagnose_help_mentions_closure_options() { let mut command = cargo_command(); command .args(["diagnose", "--help"]) .assert() .success() .stdout(predicate::str::contains("asmref diagnose")) .stdout(predicate::str::contains("--resolve-dir")) .stdout(predicate::str::contains("--test-only-pattern")) .stdout(predicate::str::contains("--no-default-test-patterns")) .stdout(predicate::str::contains("--format ")) .stdout(predicate::str::contains("--toon")); } #[test] fn diagnose_errors_show_diagnose_usage() { let mut command = cargo_command(); command .arg("diagnose") .assert() .code(2) .stderr(predicate::str::contains( "asmref diagnose [OPTIONS] [ASSEMBLY...]", )); } #[test] fn diagnose_reports_missing_conflicts_winners_and_risks() { let mut command = cargo_command(); let output = command .arg("diagnose") .arg(diagnose_fixture_root()) .arg("--resolve-dir") .arg(diagnose_server_a_dir()) .arg("--resolve-dir") .arg(diagnose_server_b_dir()) .arg("--json") .assert() .code(1) .get_output() .stdout .clone(); let payload = serde_json::from_slice::(&output).expect("json payload"); assert_eq!(payload["summary"]["root_count"], 1); assert!( payload["summary"]["error_count"] .as_u64() .is_some_and(|count| count > 0) ); let references = payload["references"].as_array().expect("references array"); assert!(references.iter().any(|entry| { entry["reference_name"] == "MissingOnly" && entry["resolution_status"] == "missing" })); let candidates = payload["candidates"].as_array().expect("candidates array"); assert!(candidates.iter().any(|entry| { entry["assembly"]["assembly_name"] == "RuntimeDependency" && entry["assembly"]["assembly_version"] == "2.0.0.0" })); let winners = payload["winners"].as_array().expect("winners array"); assert!(winners.iter().any(|entry| { entry["reference_name"] == "0Harmony" && entry["winner"]["assembly"]["assembly_version"] == "2.2.2.0" })); let conflicts = payload["conflicts"].as_array().expect("conflicts array"); assert!( conflicts .iter() .any(|entry| entry["reference_name"] == "RuntimeDependency") ); let test_only = payload["test_only"].as_array().expect("test_only array"); assert!( test_only .iter() .any(|entry| entry["assembly"]["assembly_name"] == "TestOnlySupport") ); let risks = payload["risks"].as_array().expect("risks array"); for expected in [ "missing_reference", "version_mismatch", "test_only_dependency", "missing_method", "missing_type", ] { assert!( risks.iter().any(|entry| entry["kind"] == expected), "expected risk kind {expected} in {risks:#?}" ); } }