use bytes::Bytes;
use http::{HeaderMap, HeaderName, HeaderValue, StatusCode};
use parlov_core::{
always_applicable, DifferentialSet, NormativeStrength, OracleClass, ProbeDefinition,
ProbeExchange, ResponseSurface, SignalSurface, Technique, Vector,
};
use proptest::prelude::*;
use super::{body_diff_ratio, header_diff_ratio, surface_relevance, SurfaceDecision};
fn technique_with_surface(surface: SignalSurface) -> Technique {
Technique {
id: "test-surface",
name: "Test surface technique",
oracle_class: OracleClass::Existence,
vector: Vector::StatusCodeDiff,
strength: NormativeStrength::Must,
normalization_weight: Some(0.2),
inverted_signal_weight: None,
method_relevant: false,
parser_relevant: false,
applicability: always_applicable,
contradiction_surface: surface,
}
}
fn make_exchange(status: u16, headers: HeaderMap, body: Bytes) -> ProbeExchange {
ProbeExchange {
request: ProbeDefinition {
url: "https://example.com/r/1".into(),
method: http::Method::GET,
headers: HeaderMap::new(),
body: None,
},
response: ResponseSurface {
status: StatusCode::from_u16(status).expect("valid status"),
headers,
body,
timing_ns: 0,
},
}
}
fn diff_set(
technique: Technique,
baseline: ProbeExchange,
probe: ProbeExchange,
) -> DifferentialSet {
DifferentialSet {
baseline: vec![baseline],
probe: vec![probe],
canonical: None,
technique,
}
}
fn header(name: &'static str, value: &'static str) -> (HeaderName, HeaderValue) {
(
HeaderName::from_static(name),
HeaderValue::from_static(value),
)
}
fn headers_from(pairs: &[(HeaderName, HeaderValue)]) -> HeaderMap {
let mut m = HeaderMap::new();
for (n, v) in pairs {
m.insert(n.clone(), v.clone());
}
m
}
#[test]
fn empty_differential_set_returns_reached_one() {
let t = technique_with_surface(SignalSurface::Status);
let ds = DifferentialSet {
baseline: vec![],
probe: vec![],
canonical: None,
technique: t,
};
let decision = surface_relevance(&t, &ds);
assert_eq!(decision, SurfaceDecision::Reached(1.0));
}
#[test]
fn empty_baseline_returns_reached_one() {
let t = technique_with_surface(SignalSurface::Status);
let ds = DifferentialSet {
baseline: vec![],
probe: vec![make_exchange(200, HeaderMap::new(), Bytes::new())],
canonical: None,
technique: t,
};
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn status_surface_identical_body_and_headers_reaches_one() {
let t = technique_with_surface(SignalSurface::Status);
let body = Bytes::from_static(b"{\"ok\":true}");
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), body.clone()),
make_exchange(200, HeaderMap::new(), body),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn status_surface_body_length_diff_above_threshold_blocks() {
let t = technique_with_surface(SignalSurface::Status);
let small = Bytes::from_static(b"{\"ok\":true}");
let large = Bytes::from_static(
b"{\"id\":1,\"name\":\"Alice\",\"email\":\"alice@example.com\",\"role\":\"admin\"}",
);
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), large),
make_exchange(200, HeaderMap::new(), small),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Blocked);
}
#[test]
fn status_surface_body_length_diff_below_threshold_reaches() {
let t = technique_with_surface(SignalSurface::Status);
let a = Bytes::from(vec![b'x'; 100]);
let b = Bytes::from(vec![b'x'; 95]);
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), a),
make_exchange(200, HeaderMap::new(), b),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn status_surface_body_length_diff_at_threshold_does_not_block() {
let t = technique_with_surface(SignalSurface::Status);
let a = Bytes::from(vec![b'x'; 100]);
let b = Bytes::from(vec![b'x'; 90]);
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), a),
make_exchange(200, HeaderMap::new(), b),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn status_surface_equal_length_divergent_body_blocks() {
let t = technique_with_surface(SignalSurface::Status);
let a = Bytes::from_static(br#"{"id":1,"data":"abc"}"#); let b = Bytes::from_static(br#"{"id":2,"data":"xyz"}"#); assert_eq!(a.len(), b.len(), "fixture invariant: equal-length bodies");
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), a),
make_exchange(200, HeaderMap::new(), b),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Blocked);
}
#[test]
fn status_surface_majority_header_keys_differ_blocks() {
let t = technique_with_surface(SignalSurface::Status);
let baseline_h = headers_from(&[header("a", "1"), header("b", "1")]);
let probe_h = headers_from(&[header("c", "1"), header("d", "1"), header("e", "1")]);
let ds = diff_set(
t,
make_exchange(200, baseline_h, Bytes::new()),
make_exchange(200, probe_h, Bytes::new()),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Blocked);
}
#[test]
fn status_surface_minor_header_diff_reaches() {
let t = technique_with_surface(SignalSurface::Status);
let baseline_h = headers_from(&[
header("a", "1"),
header("b", "1"),
header("c", "1"),
header("d", "1"),
]);
let probe_h = headers_from(&[
header("a", "1"),
header("b", "1"),
header("c", "1"),
header("e", "1"),
]);
let ds = diff_set(
t,
make_exchange(200, baseline_h, Bytes::new()),
make_exchange(200, probe_h, Bytes::new()),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn status_surface_header_diff_at_threshold_does_not_block() {
let t = technique_with_surface(SignalSurface::Status);
let baseline_h = headers_from(&[header("a", "1"), header("b", "1"), header("c", "1")]);
let probe_h = headers_from(&[header("a", "1"), header("b", "1"), header("d", "1")]);
let ds = diff_set(
t,
make_exchange(200, baseline_h, Bytes::new()),
make_exchange(200, probe_h, Bytes::new()),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn body_surface_returns_reached_one_even_with_body_diff() {
let t = technique_with_surface(SignalSurface::Body);
let large = Bytes::from(vec![b'x'; 1000]);
let small = Bytes::from_static(b"x");
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), large),
make_exchange(200, HeaderMap::new(), small),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn headers_surface_returns_reached_one_even_with_header_diff() {
let t = technique_with_surface(SignalSurface::Headers);
let baseline_h = headers_from(&[header("a", "1"), header("b", "1")]);
let probe_h = headers_from(&[header("c", "1"), header("d", "1"), header("e", "1")]);
let ds = diff_set(
t,
make_exchange(200, baseline_h, Bytes::new()),
make_exchange(200, probe_h, Bytes::new()),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn timing_surface_returns_reached_one() {
let t = technique_with_surface(SignalSurface::Timing);
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), Bytes::from_static(b"a")),
make_exchange(200, HeaderMap::new(), Bytes::from_static(b"b")),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn composite_surface_returns_reached_one() {
let t = technique_with_surface(SignalSurface::Composite);
let large = Bytes::from(vec![b'x'; 1000]);
let small = Bytes::from_static(b"x");
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), large),
make_exchange(200, HeaderMap::new(), small),
);
assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn body_diff_ratio_identical_bytes_is_zero() {
let a = Bytes::from_static(b"hello world");
assert!(body_diff_ratio(&a, &a).abs() < f64::EPSILON);
}
#[test]
fn body_diff_ratio_identical_long_random_bytes_is_zero() {
let a = Bytes::from(
(0u16..256)
.map(|i| u8::try_from(i & 0xff).unwrap())
.collect::<Vec<u8>>(),
);
assert!(body_diff_ratio(&a, &a).abs() < f64::EPSILON);
}
#[test]
fn body_diff_ratio_one_empty_is_one() {
let empty = Bytes::new();
let big = Bytes::from(vec![b'x'; 100]);
assert!((body_diff_ratio(&empty, &big) - 1.0).abs() < f64::EPSILON);
assert!((body_diff_ratio(&big, &empty) - 1.0).abs() < f64::EPSILON);
}
#[test]
fn body_diff_ratio_both_empty_is_zero() {
let empty = Bytes::new();
assert!(body_diff_ratio(&empty, &empty).abs() < f64::EPSILON);
}
#[test]
fn body_diff_ratio_same_length_all_bytes_differ_is_one() {
let a = Bytes::from(vec![b'a'; 50]);
let b = Bytes::from(vec![b'b'; 50]);
assert!((body_diff_ratio(&a, &b) - 1.0).abs() < f64::EPSILON);
}
#[test]
fn body_diff_ratio_same_length_half_bytes_differ_is_half() {
let mut a = vec![b'x'; 100];
let mut b = vec![b'x'; 100];
for byte in b.iter_mut().take(100).skip(50) {
*byte = b'y';
}
a[..50].fill(b'x');
let ar = body_diff_ratio(&Bytes::from(a), &Bytes::from(b));
assert!((ar - 0.5).abs() < f64::EPSILON, "expected 0.5, got {ar}");
}
#[test]
fn body_diff_ratio_different_lengths_identical_overlap() {
let a = Bytes::from(vec![b'x'; 100]);
let b = Bytes::from(vec![b'x'; 75]);
assert!((body_diff_ratio(&a, &b) - 0.25).abs() < f64::EPSILON);
}
#[test]
fn body_diff_ratio_equal_length_divergent_content_is_above_threshold() {
let a = Bytes::from_static(br#"{"id":1,"data":"abc"}"#);
let b = Bytes::from_static(br#"{"id":2,"data":"xyz"}"#);
let r = body_diff_ratio(&a, &b);
assert!(r > 0.10, "expected > 0.10, got {r}");
}
#[test]
fn header_diff_ratio_identical_keys_and_values_is_zero() {
let h = headers_from(&[header("a", "1"), header("b", "1")]);
assert!(header_diff_ratio(&h, &h).abs() < f64::EPSILON);
}
#[test]
fn header_diff_ratio_completely_disjoint_keys_is_one() {
let a = headers_from(&[header("a", "1"), header("b", "1")]);
let b = headers_from(&[header("c", "1"), header("d", "1")]);
assert!((header_diff_ratio(&a, &b) - 1.0).abs() < f64::EPSILON);
}
#[test]
fn header_diff_ratio_both_empty_is_zero() {
let a = HeaderMap::new();
let b = HeaderMap::new();
assert!(header_diff_ratio(&a, &b).abs() < f64::EPSILON);
}
#[test]
fn header_diff_ratio_same_key_different_value_counts_as_diverged() {
let a = headers_from(&[header("cache-control", "public")]);
let b = headers_from(&[header("cache-control", "private")]);
let r = header_diff_ratio(&a, &b);
assert!(r > 0.0, "expected > 0.0 for differing values, got {r}");
assert!(
(r - 1.0).abs() < f64::EPSILON,
"1 diverged of 1 union → 1.0"
);
}
#[test]
fn header_diff_ratio_key_only_in_one_side_counts_as_diverged() {
let a = headers_from(&[header("a", "1"), header("b", "1")]);
let b = headers_from(&[header("a", "1")]);
let r = header_diff_ratio(&a, &b);
assert!((r - 0.5).abs() < f64::EPSILON, "expected 0.5, got {r}");
}
#[test]
fn header_diff_ratio_multi_value_headers_full_sequence_compared() {
let mut a = HeaderMap::new();
a.append(
HeaderName::from_static("set-cookie"),
HeaderValue::from_static("session=abc"),
);
a.append(
HeaderName::from_static("set-cookie"),
HeaderValue::from_static("csrf=xyz"),
);
let mut b = HeaderMap::new();
b.append(
HeaderName::from_static("set-cookie"),
HeaderValue::from_static("session=abc"),
);
b.append(
HeaderName::from_static("set-cookie"),
HeaderValue::from_static("csrf=xyz"),
);
assert!(header_diff_ratio(&a, &b).abs() < f64::EPSILON);
let mut c = HeaderMap::new();
c.append(
HeaderName::from_static("set-cookie"),
HeaderValue::from_static("session=abc"),
);
c.append(
HeaderName::from_static("set-cookie"),
HeaderValue::from_static("csrf=DIFFERENT"),
);
let r = header_diff_ratio(&a, &c);
assert!(r > 0.0, "expected > 0.0 for differing multi-value, got {r}");
}
#[test]
fn confidence_of_reached_returns_inner() {
assert!((SurfaceDecision::Reached(0.7).confidence() - 0.7).abs() < f64::EPSILON);
}
#[test]
fn confidence_of_blocked_is_zero() {
assert!(SurfaceDecision::Blocked.confidence().abs() < f64::EPSILON);
}
proptest! {
#[test]
fn surface_relevance_referentially_transparent(
body_a_len in 0usize..=200,
body_b_len in 0usize..=200,
) {
let t = technique_with_surface(SignalSurface::Status);
let a = Bytes::from(vec![b'x'; body_a_len]);
let b = Bytes::from(vec![b'x'; body_b_len]);
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), a),
make_exchange(200, HeaderMap::new(), b),
);
let d1 = surface_relevance(&t, &ds);
let d2 = surface_relevance(&t, &ds);
prop_assert_eq!(d1, d2);
}
#[test]
fn status_surface_identical_always_reaches(body_len in 0usize..=500) {
let t = technique_with_surface(SignalSurface::Status);
let body = Bytes::from(vec![b'x'; body_len]);
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), body.clone()),
make_exchange(200, HeaderMap::new(), body),
);
prop_assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn non_status_surface_always_reaches(
surface_idx in 0usize..4,
body_a_len in 0usize..=500,
body_b_len in 0usize..=500,
) {
let surface = match surface_idx {
0 => SignalSurface::Body,
1 => SignalSurface::Headers,
2 => SignalSurface::Timing,
_ => SignalSurface::Composite,
};
let t = technique_with_surface(surface);
let a = Bytes::from(vec![b'x'; body_a_len]);
let b = Bytes::from(vec![b'y'; body_b_len]);
let ds = diff_set(
t,
make_exchange(200, HeaderMap::new(), a),
make_exchange(200, HeaderMap::new(), b),
);
prop_assert_eq!(surface_relevance(&t, &ds), SurfaceDecision::Reached(1.0));
}
#[test]
fn body_diff_ratio_bounded(
a in proptest::collection::vec(any::<u8>(), 0..=500),
b in proptest::collection::vec(any::<u8>(), 0..=500),
) {
let r = body_diff_ratio(&Bytes::from(a), &Bytes::from(b));
prop_assert!(r >= 0.0);
prop_assert!(r <= 1.0);
}
#[test]
fn body_diff_ratio_symmetric(
a in proptest::collection::vec(any::<u8>(), 0..=500),
b in proptest::collection::vec(any::<u8>(), 0..=500),
) {
let ab = body_diff_ratio(&Bytes::from(a.clone()), &Bytes::from(b.clone()));
let ba = body_diff_ratio(&Bytes::from(b), &Bytes::from(a));
prop_assert!((ab - ba).abs() < f64::EPSILON);
}
#[test]
fn body_diff_ratio_self_is_zero(a in proptest::collection::vec(any::<u8>(), 0..=500)) {
let bytes = Bytes::from(a);
prop_assert!(body_diff_ratio(&bytes, &bytes).abs() < f64::EPSILON);
}
#[test]
fn header_diff_ratio_bounded(seed in 0u32..=10) {
let mut a = HeaderMap::new();
let mut b = HeaderMap::new();
for i in 0..(seed % 5) {
let name = HeaderName::from_lowercase(format!("a-{i}").as_bytes()).unwrap();
a.insert(name, HeaderValue::from_static("1"));
}
for i in 0..((seed * 3) % 7) {
let name = HeaderName::from_lowercase(format!("b-{i}").as_bytes()).unwrap();
b.insert(name, HeaderValue::from_static("1"));
}
let r = header_diff_ratio(&a, &b);
prop_assert!(r >= 0.0);
prop_assert!(r <= 1.0);
}
#[test]
fn header_diff_ratio_symmetric(seed in 0u32..=10) {
let mut a = HeaderMap::new();
let mut b = HeaderMap::new();
for i in 0..(seed % 5) {
let name = HeaderName::from_lowercase(format!("h-{i}").as_bytes()).unwrap();
let val = if i % 2 == 0 { "x" } else { "y" };
a.insert(name, HeaderValue::from_str(val).unwrap());
}
for i in 0..((seed * 3) % 7) {
let name = HeaderName::from_lowercase(format!("h-{i}").as_bytes()).unwrap();
let val = if i % 3 == 0 { "x" } else { "z" };
b.insert(name, HeaderValue::from_str(val).unwrap());
}
let ab = header_diff_ratio(&a, &b);
let ba = header_diff_ratio(&b, &a);
prop_assert!((ab - ba).abs() < f64::EPSILON);
}
#[test]
fn header_diff_ratio_self_is_zero(seed in 0u32..=10) {
let mut m = HeaderMap::new();
for i in 0..(seed % 6) {
let name = HeaderName::from_lowercase(format!("k-{i}").as_bytes()).unwrap();
let val = format!("v-{i}");
m.insert(name, HeaderValue::from_str(&val).unwrap());
}
prop_assert!(header_diff_ratio(&m, &m).abs() < f64::EPSILON);
}
}