//! Cross-reporter totals consistency (WOR-1317).
//!
//! The JUnit `tests`/`failures`/`skipped` attributes and the TAP plan
//! line must describe the testcases each reporter actually renders, not
//! whatever `Report::summary` claims. A summary can drift (hand-built
//! fixtures, an external JSON artifact fed to `mcptest report`), and a
//! drifted count breaks CI dashboards that cross-check attributes
//! against elements. These tests feed both reporters a deliberately
//! malformed summary and assert the rendered counts still agree with
//! the test vec, and with each other.

use mcptest_core::report::reporters::{JunitReporter, TapReporter};
use mcptest_core::report::{Report, Reporter, TestStatus};

/// Four tests covering pass, fail, skip, and inconclusive, with a
/// summary that lies about every count.
fn mixed_status_report_with_malformed_summary() -> Report {
    let mut r = Report::sample();
    r.tests[1].status = TestStatus::Skip;
    r.tests[1].cache_hit = false;
    let mut probe = r.tests[0].clone();
    probe.name = "probe neither confirms nor refutes".into();
    probe.status = TestStatus::Inconclusive;
    probe.file = Some("tests/probe.yml".into());
    r.tests.push(probe);
    // The lie: reporters must not echo any of these numbers.
    r.summary.total = 99;
    r.summary.passed = 99;
    r.summary.failed = 0;
    r.summary.skipped = 0;
    r.summary.inconclusive = 0;
    r
}

fn render(reporter: &dyn Reporter, report: &Report) -> String {
    let mut buf = Vec::new();
    reporter.render(report, &mut buf).expect("render succeeds");
    String::from_utf8(buf).expect("UTF-8 output")
}

#[test]
fn junit_attributes_and_tap_plan_derive_from_tests_not_from_a_malformed_summary() {
    let report = mixed_status_report_with_malformed_summary();

    let xml = render(&JunitReporter::default(), &report);
    // Root attributes reflect the four real tests: one failure, and two
    // <skipped> elements (the Skip plus the Inconclusive testcase).
    assert!(
        xml.contains(r#"<testsuites tests="4" failures="1" errors="0" skipped="2""#),
        "root attributes must derive from report.tests, got:\n{xml}"
    );
    assert!(!xml.contains(r#"tests="99""#), "summary lie leaked:\n{xml}");
    let testcase_count = xml.matches("<testcase ").count();
    assert_eq!(testcase_count, 4);
    let skipped_elements = xml.matches("<skipped").count();
    assert_eq!(skipped_elements, 2);

    let tap = render(&TapReporter::default(), &report);
    assert!(
        tap.contains("\n1..4\n"),
        "TAP plan must count report.tests, got:\n{tap}"
    );
    let tap_result_lines = tap
        .lines()
        .filter(|l| l.starts_with("ok ") || l.starts_with("not ok "))
        .count();
    assert_eq!(tap_result_lines, 4);

    // The two formats agree with each other on the same report.
    assert_eq!(testcase_count, tap_result_lines);
}
