mod common;
use common::report_indexer::{
ArtifactIndexer, IndexerConfig, generate_html_report, generate_markdown_report, write_reports,
};
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;
fn create_sample_artifacts(base_dir: &std::path::Path) -> std::io::Result<()> {
let pass_dir = base_dir.join("e2e_basic").join("test_create_issue");
fs::create_dir_all(&pass_dir)?;
fs::write(
pass_dir.join("summary.json"),
r#"{"suite":"e2e_basic","test":"test_create_issue","passed":true,"run_count":3,"timestamp":"2026-01-17T12:00:00Z"}"#,
)?;
fs::write(
pass_dir.join("events.jsonl"),
r#"{"timestamp":"2026-01-17T12:00:00Z","event_type":"command","label":"init","binary":"br","args":["init"],"cwd":"/tmp/test1","exit_code":0,"success":true,"duration_ms":50,"stdout_len":100,"stderr_len":0}
{"timestamp":"2026-01-17T12:00:01Z","event_type":"command","label":"create","binary":"br","args":["create","--title","Test Issue"],"cwd":"/tmp/test1","exit_code":0,"success":true,"duration_ms":120,"stdout_len":200,"stderr_len":0}"#,
)?;
let pass_dir2 = base_dir.join("e2e_basic").join("test_list_issues");
fs::create_dir_all(&pass_dir2)?;
fs::write(
pass_dir2.join("summary.json"),
r#"{"suite":"e2e_basic","test":"test_list_issues","passed":true,"run_count":1,"timestamp":"2026-01-17T12:01:00Z"}"#,
)?;
fs::write(
pass_dir2.join("events.jsonl"),
r#"{"timestamp":"2026-01-17T12:01:00Z","event_type":"command","label":"list","binary":"br","args":["list","--json"],"cwd":"/tmp/test2","exit_code":0,"success":true,"duration_ms":80,"stdout_len":500,"stderr_len":0}"#,
)?;
let fail_dir = base_dir.join("e2e_sync").join("test_sync_conflict");
fs::create_dir_all(&fail_dir)?;
fs::write(
fail_dir.join("summary.json"),
r#"{"suite":"e2e_sync","test":"test_sync_conflict","passed":false,"run_count":1,"timestamp":"2026-01-17T12:02:00Z"}"#,
)?;
fs::write(
fail_dir.join("events.jsonl"),
r#"{"timestamp":"2026-01-17T12:02:00Z","event_type":"command","label":"sync","binary":"br","args":["sync","--import-only"],"cwd":"/tmp/test3","exit_code":1,"success":false,"duration_ms":250,"stdout_len":50,"stderr_len":100,"stderr_path":"0001_sync.stderr"}"#,
)?;
fs::write(
fail_dir.join("0001_sync.stderr"),
"Error: Conflict detected in beads.jsonl\nConflict markers found at lines 42-48\nPlease resolve conflicts and retry",
)?;
Ok(())
}
#[test]
fn test_report_indexer_basic() {
let temp_dir = TempDir::new().unwrap();
create_sample_artifacts(temp_dir.path()).unwrap();
let indexer = ArtifactIndexer::new(temp_dir.path());
let report = indexer.generate_report().unwrap();
assert_eq!(report.total_tests, 3, "Should have 3 tests total");
assert_eq!(report.total_passed, 2, "Should have 2 passed");
assert_eq!(report.total_failed, 1, "Should have 1 failed");
assert_eq!(report.suites.len(), 2, "Should have 2 suites");
let basic = report.tests_by_suite("e2e_basic").unwrap();
assert_eq!(basic.tests.len(), 2);
assert_eq!(basic.passed_count, 2);
let sync = report.tests_by_suite("e2e_sync").unwrap();
assert_eq!(sync.tests.len(), 1);
assert_eq!(sync.failed_count, 1);
let failed = report.failed_tests();
assert_eq!(failed.len(), 1);
assert!(failed[0].failure_reason.is_some());
assert!(
failed[0]
.failure_reason
.as_ref()
.unwrap()
.contains("Conflict")
);
}
#[test]
fn test_markdown_report() {
let temp_dir = TempDir::new().unwrap();
create_sample_artifacts(temp_dir.path()).unwrap();
let indexer = ArtifactIndexer::new(temp_dir.path());
let report = indexer.generate_report().unwrap();
let md = generate_markdown_report(&report);
assert!(md.contains("# Test Artifact Report"), "Missing header");
assert!(md.contains("## Summary"), "Missing summary");
assert!(md.contains("## Suites"), "Missing suites");
assert!(
md.contains("## Failed Tests Detail"),
"Missing failed tests"
);
assert!(md.contains("## Slowest Tests"), "Missing slowest tests");
assert!(md.contains("e2e_basic"), "Missing e2e_basic suite");
assert!(md.contains("e2e_sync"), "Missing e2e_sync suite");
assert!(
md.contains("test_sync_conflict"),
"Missing failed test name"
);
assert!(md.contains("Conflict detected"), "Missing failure reason");
assert!(md.contains("✅"), "Missing pass indicator");
assert!(md.contains("❌"), "Missing fail indicator");
}
#[test]
fn test_html_report() {
let temp_dir = TempDir::new().unwrap();
create_sample_artifacts(temp_dir.path()).unwrap();
let indexer = ArtifactIndexer::new(temp_dir.path());
let report = indexer.generate_report().unwrap();
let html = generate_html_report(&report);
assert!(html.contains("<!DOCTYPE html>"), "Missing doctype");
assert!(html.contains("<html>"), "Missing html tag");
assert!(html.contains("Test Artifact Report"), "Missing title");
assert!(html.contains("<style>"), "Missing styles");
assert!(html.contains("e2e_basic"), "Missing e2e_basic");
assert!(html.contains("e2e_sync"), "Missing e2e_sync");
assert!(html.contains("test_sync_conflict"), "Missing failed test");
assert!(html.contains("status-pass"), "Missing pass class");
assert!(html.contains("status-fail"), "Missing fail class");
assert!(
html.contains("failure-detail"),
"Missing failure detail class"
);
}
#[test]
fn test_write_reports() {
let temp_dir = TempDir::new().unwrap();
let artifacts_dir = temp_dir.path().join("artifacts");
let output_dir = temp_dir.path().join("output");
fs::create_dir_all(&artifacts_dir).unwrap();
create_sample_artifacts(&artifacts_dir).unwrap();
let indexer = ArtifactIndexer::new(&artifacts_dir);
let report = indexer.generate_report().unwrap();
let (md_path, html_path) = write_reports(&report, &output_dir).unwrap();
assert!(md_path.exists(), "Markdown report not created");
assert!(html_path.exists(), "HTML report not created");
let md_content = fs::read_to_string(&md_path).unwrap();
assert!(md_content.contains("# Test Artifact Report"));
let html_content = fs::read_to_string(&html_path).unwrap();
assert!(html_content.contains("<!DOCTYPE html>"));
}
#[test]
fn test_failures_only_filter() {
let temp_dir = TempDir::new().unwrap();
create_sample_artifacts(temp_dir.path()).unwrap();
let config = IndexerConfig {
artifact_root: temp_dir.path().to_path_buf(),
failures_only: true,
..Default::default()
};
let indexer = ArtifactIndexer::with_config(config);
let report = indexer.generate_report().unwrap();
assert_eq!(report.total_tests, 1);
assert_eq!(report.total_failed, 1);
assert!(report.tests_by_suite("e2e_basic").is_none());
}
#[test]
#[ignore = "Run manually after running tests with HARNESS_ARTIFACTS=1"]
fn generate_and_save_report() {
let artifacts_dir = std::env::var("REPORT_ARTIFACTS_DIR")
.map_or_else(|_| PathBuf::from("target/test-artifacts"), PathBuf::from);
let output_dir = std::env::var("REPORT_OUTPUT_DIR")
.map_or_else(|_| PathBuf::from("target/reports"), PathBuf::from);
let failures_only = std::env::var("REPORT_FAILURES_ONLY").is_ok_and(|v| v == "1");
println!("=== Artifact Report Generator ===");
println!("Artifacts: {}", artifacts_dir.display());
println!("Output: {}", output_dir.display());
println!("Failures only: {failures_only}");
println!();
if !artifacts_dir.exists() {
eprintln!(
"Error: Artifacts directory not found: {}",
artifacts_dir.display()
);
eprintln!();
eprintln!("Run tests with HARNESS_ARTIFACTS=1 first:");
eprintln!(" HARNESS_ARTIFACTS=1 cargo test e2e_sync");
panic!("Artifacts directory not found");
}
let config = IndexerConfig {
artifact_root: artifacts_dir,
failures_only,
..Default::default()
};
let indexer = ArtifactIndexer::with_config(config);
let report = indexer
.generate_report()
.expect("Failed to generate report");
println!("Report Summary:");
println!(" Total tests: {}", report.total_tests);
println!(
" Passed: {} ({:.1}%)",
report.total_passed,
report.pass_rate()
);
println!(" Failed: {}", report.total_failed);
let duration_secs = report.total_duration_ms / 1000;
let duration_ms = report.total_duration_ms % 1000;
println!(" Duration: {duration_secs}.{duration_ms:03}s");
println!(" Suites: {}", report.suites.len());
println!();
let (md_path, html_path) =
write_reports(&report, &output_dir).expect("Failed to write reports");
println!("Reports generated:");
println!(" Markdown: {}", md_path.display());
println!(" HTML: {}", html_path.display());
println!();
println!("Open in browser:");
println!(" open {}", html_path.display());
}