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
//! Snapshot tests for key data structures.
//!
//! These ensure that serialization formats remain stable across releases.

use captchaforge::detect::{CaptchaInfo, DetectedCaptcha};
use captchaforge::solver::{
    CaptchaPattern, CaptchaSolveResult, CaptchaType, SolveConfig, SolveMethod,
};
#[test]
fn snap_captcha_info_turnstile() {
    let info = CaptchaInfo {
        kind: DetectedCaptcha::Turnstile,
        site_key: Some("1x00000000000000000000AA".to_string()),
        page_url: "https://example.com/login".to_string(),
        container_selector: Some(".cf-turnstile".to_string()),
    };
    insta::assert_json_snapshot!(info);
}

#[test]
fn snap_captcha_info_recaptcha_v2() {
    let info = CaptchaInfo {
        kind: DetectedCaptcha::RecaptchaV2,
        site_key: Some("6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI".to_string()),
        page_url: "https://example.com/signup".to_string(),
        container_selector: Some(".g-recaptcha".to_string()),
    };
    insta::assert_json_snapshot!(info);
}

#[test]
fn snap_captcha_info_none() {
    let info = CaptchaInfo {
        kind: DetectedCaptcha::None,
        site_key: None,
        page_url: "https://example.com/".to_string(),
        container_selector: None,
    };
    insta::assert_json_snapshot!(info);
}

#[test]
fn snap_solve_result_success() {
    let result = CaptchaSolveResult {
        solution: "03AFcWeA...".to_string(),
        confidence: 0.95,
        method: SolveMethod::BehavioralBypass,
        time_ms: 2340,
        success: true,
        screenshot: None,
        cookies: Vec::new(),
        verified_outcome: None,
    };
    insta::assert_json_snapshot!(result);
}

#[test]
fn snap_solve_result_failure() {
    let result = CaptchaSolveResult::failure(SolveMethod::VisionLLM, 5000);
    insta::assert_json_snapshot!(result);
}

#[test]
fn snap_captcha_pattern() {
    let mut pattern = CaptchaPattern::new(
        "example.com".to_string(),
        CaptchaType::CloudflareTurnstile,
        SolveMethod::BehavioralBypass,
    );
    pattern.record(true, 1200, SolveMethod::BehavioralBypass);
    pattern.record(true, 1100, SolveMethod::BehavioralBypass);
    pattern.record(false, 3000, SolveMethod::VisionLLM);
    insta::assert_json_snapshot!(pattern);
}

#[test]
fn snap_solve_config_default() {
    insta::assert_json_snapshot!(SolveConfig::default());
}

#[test]
fn snap_detected_captcha_variants() {
    let variants = vec![
        DetectedCaptcha::Turnstile,
        DetectedCaptcha::RecaptchaV2,
        DetectedCaptcha::RecaptchaV3,
        DetectedCaptcha::HCaptcha,
        DetectedCaptcha::ImageCaptcha,
        DetectedCaptcha::AudioCaptcha,
        DetectedCaptcha::PowCaptcha,
        DetectedCaptcha::SliderCaptcha,
        DetectedCaptcha::CanvasCaptcha,
        DetectedCaptcha::ShadowDomCaptcha,
        DetectedCaptcha::MultiStepCaptcha,
        DetectedCaptcha::None,
    ];
    insta::assert_json_snapshot!(variants);
}

#[test]
fn snap_captcha_type_variants() {
    let variants = vec![
        CaptchaType::RecaptchaV2,
        CaptchaType::RecaptchaV3,
        CaptchaType::HCaptcha,
        CaptchaType::CloudflareTurnstile,
        CaptchaType::ImageGrid,
        CaptchaType::TextCaptcha,
        CaptchaType::AudioCaptcha,
        CaptchaType::Slider,
        CaptchaType::Custom("my_captcha".to_string()),
    ];
    insta::assert_json_snapshot!(variants);
}

// ------------------------------------------------------------------
// Snapshot tests for new DetectedCaptcha variants
// ------------------------------------------------------------------

#[test]
fn snap_captcha_info_pow_captcha() {
    let info = CaptchaInfo {
        kind: DetectedCaptcha::PowCaptcha,
        site_key: None,
        page_url: "https://example.com/pow".to_string(),
        container_selector: Some(".altcha".to_string()),
    };
    insta::assert_json_snapshot!(info);
}

#[test]
fn snap_captcha_info_slider_captcha() {
    let info = CaptchaInfo {
        kind: DetectedCaptcha::SliderCaptcha,
        site_key: None,
        page_url: "https://example.com/slider".to_string(),
        container_selector: Some(".slider-captcha".to_string()),
    };
    insta::assert_json_snapshot!(info);
}

#[test]
fn snap_captcha_info_canvas_captcha() {
    let info = CaptchaInfo {
        kind: DetectedCaptcha::CanvasCaptcha,
        site_key: None,
        page_url: "https://example.com/canvas".to_string(),
        container_selector: Some("canvas.captcha".to_string()),
    };
    insta::assert_json_snapshot!(info);
}

#[test]
fn snap_captcha_info_shadow_dom_captcha() {
    let info = CaptchaInfo {
        kind: DetectedCaptcha::ShadowDomCaptcha,
        site_key: None,
        page_url: "https://example.com/shadow".to_string(),
        container_selector: Some("captcha-widget".to_string()),
    };
    insta::assert_json_snapshot!(info);
}

#[test]
fn snap_captcha_info_multi_step_captcha() {
    let info = CaptchaInfo {
        kind: DetectedCaptcha::MultiStepCaptcha,
        site_key: None,
        page_url: "https://example.com/multistep".to_string(),
        container_selector: Some(".multi-step-captcha".to_string()),
    };
    insta::assert_json_snapshot!(info);
}