use bytes::Bytes;
use http::{HeaderMap, StatusCode};
use parlov_core::{
always_applicable, DifferentialSet, NormativeStrength, OracleClass, ProbeDefinition,
ProbeExchange, ResponseSurface, SignalSurface, Technique, Vector,
};
use proptest::prelude::*;
use super::{control_integrity, ControlDecision};
fn test_technique() -> Technique {
Technique {
id: "test-control",
name: "Test control technique",
oracle_class: OracleClass::Existence,
vector: Vector::StatusCodeDiff,
strength: NormativeStrength::May,
normalization_weight: Some(0.05),
inverted_signal_weight: None,
method_relevant: false,
parser_relevant: false,
applicability: always_applicable,
contradiction_surface: SignalSurface::Status,
}
}
fn make_exchange(status: u16) -> 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: HeaderMap::new(),
body: Bytes::new(),
timing_ns: 0,
},
}
}
fn diff_set(
baseline_status: u16,
probe_status: u16,
canonical_status: Option<u16>,
) -> DifferentialSet {
DifferentialSet {
baseline: vec![make_exchange(baseline_status)],
probe: vec![make_exchange(probe_status)],
canonical: canonical_status.map(make_exchange),
technique: test_technique(),
}
}
#[test]
fn no_canonical_returns_reached_one() {
let ds = diff_set(404, 404, None);
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn no_canonical_with_status_diff_returns_reached_one() {
let ds = diff_set(200, 404, None);
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn empty_baseline_with_canonical_returns_reached_one() {
let ds = DifferentialSet {
baseline: vec![],
probe: vec![],
canonical: Some(make_exchange(200)),
technique: test_technique(),
};
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn canonical_200_mutated_200_reaches_one() {
let ds = diff_set(200, 404, Some(200));
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn canonical_201_mutated_200_reaches_one() {
let ds = diff_set(200, 404, Some(201));
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn canonical_404_mutated_404_reaches_one() {
let ds = diff_set(404, 404, Some(404));
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn canonical_401_mutated_401_reaches_one() {
let ds = diff_set(401, 401, Some(401));
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn canonical_500_mutated_500_reaches_one() {
let ds = diff_set(500, 500, Some(500));
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn canonical_200_mutated_404_blocks() {
let ds = diff_set(404, 404, Some(200));
assert_eq!(control_integrity(&ds), ControlDecision::Blocked);
}
#[test]
fn canonical_204_mutated_404_blocks() {
let ds = diff_set(404, 404, Some(204));
assert_eq!(control_integrity(&ds), ControlDecision::Blocked);
}
#[test]
fn canonical_200_mutated_500_blocks() {
let ds = diff_set(500, 500, Some(200));
assert_eq!(control_integrity(&ds), ControlDecision::Blocked);
}
#[test]
fn canonical_301_blocks() {
let ds = diff_set(200, 200, Some(301));
assert_eq!(control_integrity(&ds), ControlDecision::Blocked);
}
#[test]
fn canonical_308_blocks() {
let ds = diff_set(200, 200, Some(308));
assert_eq!(control_integrity(&ds), ControlDecision::Blocked);
}
#[test]
fn canonical_302_does_not_block() {
let ds = diff_set(200, 200, Some(302));
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn canonical_303_does_not_block() {
let ds = diff_set(200, 200, Some(303));
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn canonical_307_does_not_block() {
let ds = diff_set(200, 200, Some(307));
assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn confidence_of_reached_returns_inner() {
assert!((ControlDecision::Reached(0.7).confidence() - 0.7).abs() < f64::EPSILON);
}
#[test]
fn confidence_of_reached_one() {
assert!((ControlDecision::Reached(1.0).confidence() - 1.0).abs() < f64::EPSILON);
}
#[test]
fn confidence_of_blocked_is_zero() {
assert!(ControlDecision::Blocked.confidence().abs() < f64::EPSILON);
}
proptest! {
#[test]
fn control_integrity_referentially_transparent(
baseline_status in 200u16..=599,
probe_status in 200u16..=599,
canonical_present in any::<bool>(),
canonical_status in 200u16..=599,
) {
let canonical = if canonical_present { Some(canonical_status) } else { None };
let ds = diff_set(baseline_status, probe_status, canonical);
let d1 = control_integrity(&ds);
let d2 = control_integrity(&ds);
prop_assert_eq!(d1, d2);
}
#[test]
fn no_canonical_always_reaches_one(
baseline_status in 200u16..=599,
probe_status in 200u16..=599,
) {
let ds = diff_set(baseline_status, probe_status, None);
prop_assert_eq!(control_integrity(&ds), ControlDecision::Reached(1.0));
}
#[test]
fn confidence_is_bounded(
baseline_status in 200u16..=599,
probe_status in 200u16..=599,
canonical_status in 200u16..=599,
) {
let ds = diff_set(baseline_status, probe_status, Some(canonical_status));
let c = control_integrity(&ds).confidence();
prop_assert!(c >= 0.0);
prop_assert!(c <= 1.0);
}
#[test]
fn canonical_301_or_308_always_blocks(
baseline_status in 200u16..=599,
probe_status in 200u16..=599,
select_308 in any::<bool>(),
) {
let canonical_status = if select_308 { 308 } else { 301 };
let ds = diff_set(baseline_status, probe_status, Some(canonical_status));
prop_assert_eq!(control_integrity(&ds), ControlDecision::Blocked);
}
}