captchaforge 0.2.36

[DO NOT USE — UNDER ACTIVE DEVELOPMENT, NOT PRODUCTION-READY] Captcha solver scaffolding for chromiumoxide-driven browsers. The architecture is in place (vendor solvers, retry-loop iframe walking, VLM provider abstraction, real-WAF bench harness) but the live-vendor success rate is still 0% — Cloudflare Turnstile / hCaptcha / reCAPTCHA detect us at a TLS / CDP fingerprint layer that no flag-based stealth has cleared. Watch the repo; do not depend on this for any real workload.
Documentation
//! Tests that the local scorecard (`scorecard/local.json`) is
//! present + structurally valid + reflects the current state of
//! the workspace. The scorecard JSON is regenerated by
//! `python3 tools/local_scorecard.py`; this test asserts the
//! checked-in copy is well-formed + the underlying counts have
//! not regressed below the documented floors.

use std::path::PathBuf;

fn scorecard_path() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("scorecard")
        .join("local.json")
}

#[test]
fn local_scorecard_exists() {
    assert!(
        scorecard_path().is_file(),
        "scorecard/local.json must exist — run `python3 tools/local_scorecard.py`"
    );
}

#[test]
fn local_scorecard_has_expected_schema() {
    let body = std::fs::read_to_string(scorecard_path()).expect("read scorecard");
    let v: serde_json::Value = serde_json::from_str(&body).expect("scorecard is valid JSON");
    assert_eq!(v["schema"], "captchaforge-bench-report-1");
    assert!(v["summary"].is_object());
    assert!(v["aggregates"].is_array());
}

#[test]
fn local_scorecard_meets_minimum_floors() {
    let body = std::fs::read_to_string(scorecard_path()).expect("read scorecard");
    let v: serde_json::Value = serde_json::from_str(&body).expect("scorecard parses");
    let s = &v["summary"];
    let solvers = s["solvers_in_default_chain"].as_u64().unwrap_or(0);
    let rules = s["community_rules"].as_u64().unwrap_or(0);
    let fixtures = s["bench_fixtures"].as_u64().unwrap_or(0);
    let fps = s["challenge_fingerprints"].as_u64().unwrap_or(0);
    let tests = s["integration_test_files"].as_u64().unwrap_or(0);
    assert!(solvers >= 17, "solver chain must have ≥17 solvers, got {solvers}");
    assert!(rules >= 158, "community rules must have ≥158, got {rules}");
    assert!(fixtures >= 35, "bench fixtures must have ≥35, got {fixtures}");
    assert!(fps >= 12, "challenge fingerprints must have ≥12, got {fps}");
    assert!(tests >= 40, "integration test files must have ≥40, got {tests}");
}

#[test]
fn local_scorecard_aggregates_have_real_success_rates() {
    let body = std::fs::read_to_string(scorecard_path()).expect("read");
    let v: serde_json::Value = serde_json::from_str(&body).unwrap();
    let aggregates = v["aggregates"].as_array().expect("aggregates is array");
    assert!(!aggregates.is_empty(), "scorecard must have aggregates");
    for agg in aggregates {
        let rate = agg["success_rate"].as_f64().unwrap_or(-1.0);
        assert!(
            (0.0..=1.0).contains(&rate),
            "success_rate must be in [0,1], got {rate} for {agg:?}"
        );
    }
}