captchaforge 0.2.39

Captcha detection and solving for Firefox and BiDi-driven browsers. Detection, vendor solver scaffolding, trusted cross-origin click delivery into nested OOPIFs, and stealth personas are implemented and tested; broad live-vendor solve rates are not yet benchmarked.
Documentation
//! Lock the Helm chart structure so a refactor can't accidentally
//! drop a required template file or break the values.yaml schema.

use std::fs;
use std::path::PathBuf;

fn chart_dir() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("deploy")
        .join("charts")
        .join("captchaforge")
}

#[test]
fn chart_yaml_declares_required_fields() {
    let path = chart_dir().join("Chart.yaml");
    let body = fs::read_to_string(&path).expect("Chart.yaml exists");
    for required in [
        "apiVersion: v2",
        "name: captchaforge",
        "type: application",
        "version:",
    ] {
        assert!(body.contains(required), "Chart.yaml missing {required:?}");
    }
}

#[test]
fn ships_every_required_template() {
    let templates = chart_dir().join("templates");
    for name in [
        "deployment.yaml",
        "service.yaml",
        "secret.yaml",
        "servicemonitor.yaml",
        "ingress.yaml",
        "_helpers.tpl",
    ] {
        let p = templates.join(name);
        assert!(p.is_file(), "missing template {name} at {}", p.display());
    }
}

#[test]
fn values_yaml_declares_every_field_templates_read() {
    let values = fs::read_to_string(chart_dir().join("values.yaml")).expect("values.yaml");
    for key in [
        "replicaCount",
        "image:",
        "service:",
        "auth:",
        "rateLimitRps:",
        "allowOrigins",
        "resources:",
        "livenessProbe:",
        "readinessProbe:",
        "serviceMonitor:",
        "ingress:",
        "podSecurityContext:",
        "securityContext:",
        "extraArgs:",
        "extraEnv:",
        "nodeSelector",
        "tolerations",
        "affinity",
    ] {
        assert!(values.contains(key), "values.yaml missing key {key:?}");
    }
}

#[test]
fn deployment_wires_serve_token_from_secret() {
    let dep = fs::read_to_string(chart_dir().join("templates/deployment.yaml")).unwrap();
    assert!(dep.contains("CAPTCHAFORGE_SERVE_TOKEN"));
    assert!(dep.contains("secretKeyRef"));
}

#[test]
fn deployment_passes_rate_limit_env() {
    let dep = fs::read_to_string(chart_dir().join("templates/deployment.yaml")).unwrap();
    assert!(dep.contains("CAPTCHAFORGE_SERVE_RPS"));
}

#[test]
fn deployment_uses_prometheus_scrape_annotations() {
    let dep = fs::read_to_string(chart_dir().join("templates/deployment.yaml")).unwrap();
    assert!(dep.contains("prometheus.io/scrape"));
    assert!(dep.contains("prometheus.io/path"));
}