//! Integration tests for the `diagpick` command. use std::fs; use std::path::PathBuf; use tempfile::tempdir; use assert_cmd::Command; use predicates::prelude::*; fn cargo_command() -> Command { Command::cargo_bin("diagpick").expect("binary") } fn workspace_root() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("..") .join("..") .canonicalize() .expect("workspace root") } fn fixture_relative(path: &str) -> PathBuf { PathBuf::from("fixtures").join(path) } fn fixture(path: &str) -> PathBuf { workspace_root().join(fixture_relative(path)) } fn relative_to_workspace(path: &str) -> String { fixture_relative(path).display().to_string() } fn pwsh_command(script: impl AsRef) -> Command { let mut command = Command::new("pwsh"); command .arg("-NoProfile") .arg("-Command") .arg(script.as_ref()); command } #[test] fn extracts_rust_diagnostics_in_text_mode() { let mut command = cargo_command(); command .current_dir(workspace_root()) .arg(fixture("diag/rust-errors.txt")) .assert() .success() .stdout(predicate::str::contains("severity=error code=E0425")) .stdout(predicate::str::contains("sample.rs line=25 column=11")) .stdout(predicate::str::contains("severity=warning code=-")) .stdout(predicate::str::contains("sample.rs line=33 column=4")); } #[test] fn filters_unity_diagnostics_and_attaches_source_as_json() { let mut command = cargo_command(); command .current_dir(workspace_root()) .arg("--json") .arg("--severity") .arg("error") .arg("--with-source") .arg("--context") .arg("1") .arg(fixture("diag/unity-errors.txt")) .assert() .success() .stdout(predicate::str::contains("\"severity\":\"error\"")) .stdout(predicate::str::contains("\"code\":\"CS0103\"")) .stdout(predicate::str::contains("\"path\":\"")) .stdout(predicate::str::contains("sample.cs")) .stdout(predicate::str::contains("\"start_line\":8")) .stdout(predicate::str::contains("ComputeScore")) .stdout(predicate::str::contains("CS0168").not()); } #[test] fn supports_powershell_pipeline_input() { let binary = assert_cmd::cargo::cargo_bin("diagpick"); let input = relative_to_workspace("diag/unity-errors.txt"); let script = format!( "[System.IO.File]::ReadLines('{}') | & '{}' --json", input, binary.display() ); let mut command = pwsh_command(script); command .current_dir(workspace_root()) .assert() .success() .stdout(predicate::str::contains("\"code\":\"CS0103\"")) .stdout(predicate::str::contains("\"severity\":\"warning\"")); } #[test] fn utf8_bom_stdin_still_extracts_first_diagnostic() { let mut command = cargo_command(); command .write_stdin( "\u{feff}error[E0425]: cannot find value `missing` in this scope\n --> sample.rs:25:11\n", ) .assert() .success() .stdout(predicate::str::contains("severity=error code=E0425")) .stdout(predicate::str::contains("sample.rs line=25 column=11")); } #[test] fn utf8_bom_log_file_still_extracts_diagnostics() { let dir = tempdir().expect("tempdir"); let log_path = dir.path().join("bom-rust-errors.txt"); let mut bytes = vec![0xEF, 0xBB, 0xBF]; bytes.extend(fs::read(fixture("diag/rust-errors.txt")).expect("fixture")); fs::write(&log_path, bytes).expect("log fixture"); let mut command = cargo_command(); command .current_dir(workspace_root()) .arg(&log_path) .assert() .success() .stdout(predicate::str::contains("severity=error code=E0425")) .stdout(predicate::str::contains("sample.rs line=25 column=11")); } #[test] fn invalid_utf8_log_file_reports_read_error() { let dir = tempdir().expect("tempdir"); let log_path = dir.path().join("invalid-utf8.log"); fs::write(&log_path, [0x66, 0x6F, 0x80, 0x6F]).expect("log fixture"); let mut command = cargo_command(); command .current_dir(workspace_root()) .arg(&log_path) .assert() .failure() .stderr(predicate::str::contains("failed to read")) .stderr(predicate::str::contains("invalid-utf8.log")); } #[test] fn help_includes_diagnostic_examples() { let mut command = cargo_command(); command .arg("--help") .assert() .success() .stdout(predicate::str::contains("--with-source")) .stdout(predicate::str::contains("ConvertFrom-Json")) .stdout(predicate::str::contains( "diagpick .\\fixtures\\diag\\rust-errors.txt", )); } #[test] fn emits_tool_fallback_for_actionable_cargo_errors() { let temp = tempdir().expect("tempdir"); let log = temp.path().join("cargo.log"); fs::write( &log, "error: package ID specification `missing-crate` did not match any packages\n", ) .expect("fixture"); let mut command = cargo_command(); command .arg("--json") .arg(&log) .assert() .success() .stdout(predicate::str::contains("\"tool_hint\":\"cargo\"")) .stdout(predicate::str::contains("\"path\":\"\"")); }