asupersync-conformance 0.3.3

Conformance test suite for async runtime specifications
//! HTTP/2 SETTINGS_ENABLE_PUSH=0 Enforcement Conformance Test Runner
//!
//! Tests RFC 7540 ยง6.5.2 compliance: when SETTINGS_ENABLE_PUSH=0 is sent,
//! the server MUST NOT send PUSH_PROMISE frames. This runner reports live
//! asupersync evidence and explicitly marks h2 reference comparison unavailable
//! unless a real peer is wired in.
//!
//! Usage:
//!   rch exec -- env CARGO_TARGET_DIR=${TMPDIR:-/tmp}/rch_target_conformance_bin_docs cargo run --bin h2_enable_push_conformance
//!   rch exec -- env CARGO_TARGET_DIR=${TMPDIR:-/tmp}/rch_target_conformance_bin_docs cargo run --bin h2_enable_push_conformance -- --format json
//!   rch exec -- env CARGO_TARGET_DIR=${TMPDIR:-/tmp}/rch_target_conformance_bin_docs cargo run --bin h2_enable_push_conformance -- --output report.md

use clap::{Parser, ValueEnum};
use std::path::PathBuf;

#[derive(Parser)]
#[command(name = "h2_enable_push_conformance")]
#[command(about = "HTTP/2 SETTINGS_ENABLE_PUSH=0 enforcement conformance tester")]
struct Args {
    /// Output format for results
    #[arg(long, default_value = "markdown")]
    format: OutputFormat,

    /// Output file path (defaults to stdout)
    #[arg(long, short)]
    output: Option<PathBuf>,

    /// Run specific test case by ID
    #[arg(long)]
    test_case: Option<String>,

    /// Verbose logging
    #[arg(long, short)]
    verbose: bool,

    /// Timeout in seconds for test execution
    #[arg(long, default_value = "30")]
    timeout: u64,
}

#[derive(Debug, Clone, ValueEnum)]
enum OutputFormat {
    Json,
    Markdown,
    Summary,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();

    // Initialize logging
    if args.verbose {
        env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
    }

    println!("๐Ÿ”ง HTTP/2 SETTINGS_ENABLE_PUSH=0 Enforcement Conformance Tester");
    println!("   Testing live asupersync SETTINGS/Frame/Connection seams");
    println!("   Focus: Server must not send PUSH_PROMISE when ENABLE_PUSH=0");
    println!();

    // Create and configure the tester
    let mut tester = asupersync_conformance::EnablePushConformanceTester::new();

    // Filter to specific test case if requested
    if let Some(test_id) = &args.test_case {
        tester.test_cases.retain(|case| case.id == *test_id);
        if tester.test_cases.is_empty() {
            eprintln!("โŒ Test case '{}' not found", test_id);
            std::process::exit(1);
        }
        println!("๐Ÿ” Running single test case: {}", test_id);
    } else {
        println!(
            "๐Ÿ“‹ Running {} conformance test cases",
            tester.test_cases.len()
        );
    }

    // Set up timeout
    let timeout_duration = std::time::Duration::from_secs(args.timeout);

    // Run the conformance tests with timeout
    let report = match tokio::time::timeout(timeout_duration, tester.run_all_tests()).await {
        Ok(report) => report,
        Err(_) => {
            eprintln!("โŒ Tests timed out after {} seconds", args.timeout);
            std::process::exit(1);
        }
    };

    // Generate output based on format
    let output = match args.format {
        OutputFormat::Json => serde_json::to_string_pretty(&report)?,
        OutputFormat::Markdown => tester.generate_markdown_report(&report),
        OutputFormat::Summary => generate_summary_output(&report),
    };

    // Write output
    match args.output {
        Some(path) => {
            std::fs::write(&path, &output)?;
            println!("๐Ÿ“ Report written to: {}", path.display());
        }
        None => {
            println!("{}", output);
        }
    }

    // Print final status
    println!();
    print_test_summary(&report);

    // Exit with appropriate code
    std::process::exit(exit_code(&report));
}

/// Generate a concise summary output
fn generate_summary_output(report: &asupersync_conformance::EnablePushComplianceReport) -> String {
    let mut output = String::new();

    output.push_str("HTTP/2 SETTINGS_ENABLE_PUSH=0 CONFORMANCE SUMMARY\n");
    output.push_str("============================================\n\n");

    output.push_str(&format!("Test Run: {}\n", report.test_run_id));
    output.push_str(&format!("Timestamp: {}\n", report.timestamp));
    output.push_str(&format!("Total Cases: {}\n\n", report.total_cases));

    output.push_str("RESULTS:\n");
    output.push_str(&format!("  โœ… Passed: {}\n", report.summary.passed));
    output.push_str(&format!("  โŒ Failed: {}\n", report.summary.failed));
    output.push_str(&format!(
        "  โš ๏ธ  Expected Failures: {}\n",
        report.summary.expected_failures
    ));
    output.push_str(&format!("  โญ๏ธ  Skipped: {}\n\n", report.summary.skipped));

    output.push_str(&format!(
        "Compliance Score: {:.1}%\n",
        report.summary.compliance_score * 100.0
    ));

    if report.summary.failed > 0 {
        output.push_str("\nFAILURES:\n");
        for result in &report.results {
            if result.verdict == asupersync_conformance::EnablePushTestVerdict::Fail {
                output.push_str(&format!(
                    "  โŒ {}: {}\n",
                    result.case_id,
                    result.error.as_deref().unwrap_or("Unknown error")
                ));
                output.push_str(&format!(
                    "     PUSH_PROMISE accepted by asupersync: {}\n",
                    result.asupersync_push_promise_count
                ));
            }
        }
    }

    // Push enforcement analysis
    output.push_str("\nPUSH_PROMISE ENFORCEMENT ANALYSIS:\n");
    for result in &report.results {
        output.push_str(&format!(
            "  ๐Ÿ“Š {}: support={}, asupersync={} PUSH_PROMISE, reference={}\n",
            result.case_id,
            result.support_class,
            result.asupersync_push_promise_count,
            result.reference_status
        ));
    }

    output
}

/// Print colorized test summary to stderr
fn print_test_summary(report: &asupersync_conformance::EnablePushComplianceReport) {
    eprintln!("โ•ญโ”€ HTTP/2 SETTINGS_ENABLE_PUSH=0 CONFORMANCE RESULTS โ”€โ•ฎ");
    eprintln!("โ”‚                                                      โ”‚");

    if report.summary.failed == 0 {
        eprintln!(
            "โ”‚  {}  โ”‚",
            final_status_line(report.summary.skipped, report.summary.expected_failures)
        );
        eprintln!(
            "โ”‚  ๐ŸŽฏ Compliance: {:.1}%                               โ”‚",
            report.summary.compliance_score * 100.0
        );
    } else {
        eprintln!(
            "โ”‚  โŒ {} TESTS FAILED                                  โ”‚",
            report.summary.failed
        );
        eprintln!(
            "โ”‚  ๐Ÿ“Š Compliance: {:.1}%                               โ”‚",
            report.summary.compliance_score * 100.0
        );
    }

    eprintln!("โ”‚                                                      โ”‚");
    eprintln!(
        "โ”‚  ๐Ÿ“‹ Total: {}                                        โ”‚",
        report.total_cases
    );
    eprintln!(
        "โ”‚  โœ… Passed: {}                                      โ”‚",
        report.summary.passed
    );
    eprintln!(
        "โ”‚  โŒ Failed: {}                                      โ”‚",
        report.summary.failed
    );
    eprintln!(
        "โ”‚  โš ๏ธ  Expected: {}                                    โ”‚",
        report.summary.expected_failures
    );
    eprintln!(
        "โ”‚  โญ๏ธ  Skipped: {}                                     โ”‚",
        report.summary.skipped
    );
    eprintln!("โ”‚                                                      โ”‚");
    eprintln!("โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ");
}

fn final_status_line(skipped_count: usize, expected_failure_count: usize) -> String {
    if skipped_count == 0 && expected_failure_count == 0 {
        "โœ… ALL TESTS PASSED".to_string()
    } else {
        format!(
            "โš ๏ธ  NO FAILURES; PARTIAL COVERAGE ({skipped_count} skipped, {expected_failure_count} expected failures)"
        )
    }
}

fn has_incomplete_coverage(report: &asupersync_conformance::EnablePushComplianceReport) -> bool {
    report.total_cases == 0 || report.summary.skipped > 0 || report.summary.expected_failures > 0
}

fn exit_code(report: &asupersync_conformance::EnablePushComplianceReport) -> i32 {
    if report.summary.failed > 0 || has_incomplete_coverage(report) {
        1
    } else {
        0
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use asupersync_conformance::EnablePushComplianceSummary;

    fn synthetic_report(
        total_cases: usize,
        failed: usize,
        expected_failures: usize,
        skipped: usize,
    ) -> asupersync_conformance::EnablePushComplianceReport {
        asupersync_conformance::EnablePushComplianceReport {
            test_run_id: "synthetic".to_string(),
            timestamp: chrono::Utc::now().to_rfc3339(),
            total_cases,
            results: Vec::new(),
            summary: EnablePushComplianceSummary {
                passed: total_cases
                    .saturating_sub(failed)
                    .saturating_sub(expected_failures)
                    .saturating_sub(skipped),
                failed,
                expected_failures,
                skipped,
                total: total_cases,
                compliance_score: 0.0,
            },
        }
    }

    #[test]
    fn final_status_does_not_claim_all_passed_for_partial_coverage() {
        let status = final_status_line(1, 0);

        assert!(status.contains("NO FAILURES; PARTIAL COVERAGE"));
        assert!(!status.contains("ALL TESTS PASSED"));
    }

    #[test]
    fn final_status_claims_all_passed_only_for_full_green_results() {
        assert_eq!(final_status_line(0, 0), "โœ… ALL TESTS PASSED");
    }

    #[test]
    fn exit_code_is_nonzero_for_expected_failures() {
        let report = synthetic_report(8, 0, 1, 0);

        assert_eq!(exit_code(&report), 1);
    }

    #[test]
    fn exit_code_is_nonzero_for_skipped_coverage() {
        let report = synthetic_report(8, 0, 0, 1);

        assert_eq!(exit_code(&report), 1);
    }

    #[test]
    fn exit_code_is_nonzero_for_zero_case_reports() {
        let report = synthetic_report(0, 0, 0, 0);

        assert_eq!(exit_code(&report), 1);
    }

    #[test]
    fn exit_code_is_zero_for_full_pass_coverage() {
        let report = synthetic_report(8, 0, 0, 0);

        assert_eq!(exit_code(&report), 0);
    }
}