//! Unit tests of the browser sink (extensions/browser-sink.md).

use super::*;
use serde_json::json;

#[test]
fn the_ua_rows_are_all_in_the_codec_registry() {
    // §4.1's forms are each a registry row's (§4.2): a string of a row's form passes the gate.
    for r in UA_ROWS {
        for f in r.forms {
            let sample = if f.ends_with('.') { format!("{f}x") } else { (*f).to_string() };
            assert!(registry_row(&sample).is_some(), "{f}");
        }
    }
}

#[test]
fn rows_match_by_prefix_with_something_after() {
    assert!(row_matches("vp09.*", "vp09.00.10.08"));
    assert!(!row_matches("vp09.*", "vp09."));
    assert!(!row_matches("vp09.*", "vp09"));
    assert!(row_matches("pcm-*", "pcm-s16"));
    assert!(!row_matches("vp8", "vp8.0"));
    for s in ["av02.0.08M.08", "vvc1.1.L123", "vvi1.1.L123", "iamf.001.001.Opus", "Opus", "VP8"] {
        assert!(registry_row(s).is_none(), "{s} matches no row");
    }
}

#[test]
fn registered_tiers_follow_the_replicate_class() {
    assert_eq!(registered_tier("wai.video.vp9"), Tier::DecodeEquivalence);
    assert_eq!(registered_tier("wai.video.int_motion"), Tier::DecodeEquivalence);
    assert_eq!(registered_tier("wai.image.jpegai"), Tier::EntropyConsistency);
    assert_eq!(registered_tier("wai.video.av1"), Tier::None);
    assert_eq!(registered_tier("wai.generate.spectral"), Tier::None, "generated, not decoded");
    assert_eq!(registered_tier("wai.video.not_registered"), Tier::None);
    assert_eq!(registered_tier("wai.live.room"), Tier::None);
}

#[test]
fn tiers_read_like_a_pin() {
    assert_eq!(Tier::read("decode-equivalence"), Tier::DecodeEquivalence);
    assert_eq!(Tier::read("Decode-Equivalence"), Tier::None);
    assert_eq!(Tier::read(""), Tier::None);
    assert!(Tier::None < Tier::EntropyConsistency && Tier::EntropyConsistency < Tier::DecodeEquivalence);
}

#[test]
fn integers_are_numbers_without_fraction_or_exponent_within_the_safe_range() {
    let p = |s: &str| serde_json::from_str::<Value>(s).unwrap();
    assert_eq!(integer(&p("9007199254740991")), Some(MAX_SAFE));
    assert_eq!(integer(&p("-9007199254740991")), Some(-MAX_SAFE));
    assert_eq!(integer(&p("9007199254740992")), None);
    assert_eq!(integer(&p("1.0")), None);
    assert_eq!(integer(&p("1e3")), None);
    assert_eq!(integer(&p("\"1\"")), None);
}

#[test]
fn a_binding_round_trips_through_its_canonical_form() {
    let b = Binding::of_object(b"WAI1....", Some([7; 32]), Tier::DecodeEquivalence);
    let back = Binding::read(&serde_json::from_str(&b.canonical()).unwrap()).unwrap();
    assert_eq!(back, b);
    assert!(b.canonical().starts_with(r#"{"alg":"jwp-object-1","digest":""#));
    assert_eq!(b.digest, crate::jwp_hash::content_hash(b"WAI1...."));
    let without = Binding { receipt: None, ..b };
    assert!(!without.canonical().contains("receipt"));
}

#[test]
fn the_frame_tier_drops_for_a_null_format_only_on_the_ua_route() {
    assert_eq!(frame_tier("ua", Tier::DecodeEquivalence, true, false), Tier::None);
    assert_eq!(frame_tier("wasm", Tier::DecodeEquivalence, true, false), Tier::DecodeEquivalence);
    assert_eq!(frame_tier("wasm", Tier::DecodeEquivalence, false, true), Tier::None);
}

#[test]
fn frame_state_refuses_tokens_outside_its_vocabulary() {
    assert!(frame_state(None, "ok", "linked", "accepted").is_err());
    assert!(frame_state(None, "verified", "already", "accepted").is_err());
    assert!(frame_state(None, "verified", "linked", "none").is_err());
}

#[test]
fn an_answer_for_an_unregistered_string_is_never_read() {
    // The same report with and without a "supported" answer for an AV2 string routes the same.
    let base = |ua: Value| {
        json!({ "browser_sink": 1, "ua": ua, "host": {}, "wasm": [], "constraints": {}, "energy": {},
                "objects": [ { "name": "av2", "capability": "wai.video.av2", "codec": "av02.0.08M.08", "need": "none", "params": {} } ] })
    };
    let a = route_report(&base(json!({}))).unwrap();
    let b = route_report(&base(json!({ "av02.0.08M.08": { "supported": true, "smooth": true, "power_efficient": true, "probe": "match" } }))).unwrap();
    assert_eq!(a, b);
    assert_eq!(a[0]["legs"]["ua"], "codec_unregistered");
    assert_eq!(a[0]["asked"], false);
}

#[test]
fn power_efficient_is_never_stated_off_the_ua_route() {
    let r = json!({ "browser_sink": 1,
        "ua": { "vp09.00.10.08": { "supported": false, "smooth": false, "power_efficient": true, "probe": "not_run" } },
        "host": { "wai.video.vp9": "none" }, "wasm": [], "constraints": {}, "energy": {},
        "objects": [ { "name": "vp9", "capability": "wai.video.vp9", "codec": "vp09.00.10.08", "need": "none", "params": {} } ] });
    let t = route_report(&r).unwrap();
    assert_eq!(t[0]["route"], "host");
    assert_eq!(t[0]["energy"]["power_efficient"], Value::Null);
}

#[test]
fn every_reason_has_a_distinct_code() {
    let mut codes: Vec<&str> = Reason::all().iter().map(|r| r.code()).collect();
    codes.sort_unstable();
    codes.dedup();
    assert_eq!(codes.len(), Reason::all().len());
}

#[test]
fn the_side_table_strikes_a_timestamp_given_twice() {
    let b = json!({ "alg": "jwp-object-1", "digest": "11".repeat(32), "tier": "none" });
    let ops = vec![
        json!({ "op": "chunk", "decoder": "d", "timestamp": 5, "binding": b }),
        json!({ "op": "chunk", "decoder": "d", "timestamp": 5, "binding": b }),
        json!({ "op": "output", "decoder": "d", "timestamp": 5, "frame": "f" }),
    ];
    let r = run_table(PENDING_BOUND, &ops).unwrap();
    assert_eq!(r["frames"]["f"], Value::Null);
    assert_eq!(r["refusals"][0]["reason"], "binding_ambiguous");
    assert!(run_table(PENDING_BOUND, &[json!({ "op": "clone", "from": "nope", "frame": "x" })]).is_err());
}
