use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn prints_help() {
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("Scan software projects"));
}
#[test]
fn scan_prints_markdown_to_stdout_by_default() {
let fixture = ProjectFixture::new();
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("scan")
.arg(fixture.path())
.assert()
.success()
.stdout(predicate::str::contains("# Project Report"));
}
#[test]
fn scan_prints_json_to_stdout() {
let fixture = ProjectFixture::new();
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("scan")
.arg(fixture.path())
.arg("--format")
.arg("json")
.assert()
.success()
.stdout(predicate::str::contains("\"identity\""));
}
#[test]
fn scan_prints_terminal_dashboard_when_requested() {
let fixture = ProjectFixture::new();
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("scan")
.arg(fixture.path())
.arg("--format")
.arg("terminal")
.assert()
.success()
.stdout(
predicate::str::contains("Projd Scan Report")
.and(predicate::str::contains("Health"))
.and(predicate::str::contains("Languages"))
.and(predicate::str::contains("Rust"))
.and(predicate::str::contains("files scanned"))
.and(predicate::str::contains("█"))
.and(predicate::str::contains("Risks")),
);
}
#[test]
fn scan_terminal_dashboard_supports_ascii_output() {
let fixture = ProjectFixture::new();
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("scan")
.arg(fixture.path())
.arg("--format")
.arg("terminal")
.arg("--no-unicode")
.assert()
.success()
.stdout(predicate::str::contains("Projd Scan Report"))
.stdout(predicate::str::contains("#"))
.stdout(predicate::str::contains("OK"))
.stdout(predicate::str::contains("█").not());
}
#[test]
fn scan_writes_markdown_output_file() {
let fixture = ProjectFixture::new();
let output = fixture.path().join("report.md");
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("scan")
.arg(fixture.path())
.arg("--output")
.arg(&output)
.assert()
.success()
.stdout(predicate::str::is_empty());
let content = fs::read_to_string(output).expect("read report");
assert!(content.contains("# Project Report"));
}
#[test]
fn scan_infers_json_output_from_extension() {
let fixture = ProjectFixture::new();
let output = fixture.path().join("scan.json");
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("scan")
.arg(fixture.path())
.arg("--output")
.arg(&output)
.assert()
.success()
.stdout(predicate::str::is_empty());
let content = fs::read_to_string(output).expect("read json");
assert!(content.contains("\"identity\""));
}
#[test]
fn scan_refuses_to_overwrite_existing_output_file() {
let fixture = ProjectFixture::new();
let output = fixture.path().join("report.md");
fs::write(&output, "existing").expect("write existing output");
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("scan")
.arg(fixture.path())
.arg("--output")
.arg(&output)
.assert()
.failure()
.stderr(predicate::str::contains("refusing to overwrite"));
assert_eq!(fs::read_to_string(output).expect("read output"), "existing");
}
#[test]
fn scan_overwrites_existing_output_file_when_requested() {
let fixture = ProjectFixture::new();
let output = fixture.path().join("report.md");
fs::write(&output, "existing").expect("write existing output");
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("scan")
.arg(fixture.path())
.arg("--output")
.arg(&output)
.arg("--overwrite")
.assert()
.success();
let content = fs::read_to_string(output).expect("read output");
assert!(content.contains("# Project Report"));
assert_ne!(content, "existing");
}
#[test]
fn explicit_format_overrides_output_extension() {
let fixture = ProjectFixture::new();
let output = fixture.path().join("report.txt");
let mut cmd = Command::cargo_bin("projd").expect("binary exists");
cmd.arg("scan")
.arg(fixture.path())
.arg("--format")
.arg("json")
.arg("--output")
.arg(&output)
.assert()
.success();
let content = fs::read_to_string(output).expect("read output");
assert!(content.contains("\"identity\""));
}
struct ProjectFixture {
temp_dir: TempDir,
}
impl ProjectFixture {
fn new() -> Self {
let temp_dir = tempfile::tempdir().expect("create temp dir");
fs::write(
temp_dir.path().join("Cargo.toml"),
"[package]\nname = \"fixture\"\nversion = \"0.1.0\"\n",
)
.expect("write manifest");
fs::create_dir(temp_dir.path().join("src")).expect("create src dir");
fs::write(temp_dir.path().join("src/lib.rs"), "pub fn fixture() {}\n")
.expect("write rust source");
fs::write(temp_dir.path().join("README.md"), "# Fixture\n").expect("write readme");
fs::write(temp_dir.path().join("LICENSE"), "MIT\n").expect("write license");
Self { temp_dir }
}
fn path(&self) -> &std::path::Path {
self.temp_dir.path()
}
}