use super::*;
use parlov_core::{ImpactClass, Signal, SignalKind};
#[test]
fn payment_required_vs_not_found() {
assert_pattern(
402,
404,
OracleVerdict::Likely,
Some(Severity::Low),
Some("Payment-gate differential"),
true,
Some("RFC 9110 §15.5.3"),
);
}
#[test]
fn bad_request_vs_created() {
assert_pattern(
400,
201,
OracleVerdict::Likely,
Some(Severity::Low),
Some("Client-error creation differential"),
true,
Some("RFC 9110 §15.5.1"),
);
}
#[test]
fn bad_request_vs_ok() {
assert_pattern(
400,
200,
OracleVerdict::Likely,
Some(Severity::Low),
Some("Client-error differential"),
true,
Some("RFC 9110 §15.5.1"),
);
}
#[test]
fn too_many_requests_vs_not_found() {
assert_pattern(
429,
404,
OracleVerdict::Likely,
Some(Severity::Low),
Some("Rate-limit-based differential"),
true,
Some("RFC 6585 §4"),
);
}
#[test]
fn multiple_choices_vs_not_found() {
assert_pattern(
300,
404,
OracleVerdict::Likely,
Some(Severity::Low),
Some("Multiple-choices differential"),
true,
Some("RFC 9110 §15.4.1"),
);
}
#[test]
fn same_status_not_present() {
assert_pattern(404, 404, OracleVerdict::NotPresent, None, None, false, None);
}
#[test]
fn same_status_200_not_present() {
assert_pattern(200, 200, OracleVerdict::NotPresent, None, None, false, None);
}
#[test]
fn unrecognised_diff_not_present() {
assert_pattern(418, 404, OracleVerdict::NotPresent, None, None, false, None);
}
#[test]
fn unrecognised_diff_503_vs_200() {
assert_pattern(503, 200, OracleVerdict::NotPresent, None, None, false, None);
}
#[test]
fn classify_populates_confidence() {
let b = StatusCode::from_u16(403).expect("valid status");
let p = StatusCode::from_u16(404).expect("valid status");
let technique = status_code_diff_technique();
let r = classify(b, p, vec![], &technique);
assert!(r.confidence >= 80);
assert!(r.impact_class.is_some());
assert!(!r.reasons.is_empty());
}
#[test]
fn classify_populates_technique_metadata() {
let b = StatusCode::from_u16(403).expect("valid status");
let p = StatusCode::from_u16(404).expect("valid status");
let technique = status_code_diff_technique();
let r = classify(b, p, vec![], &technique);
assert_eq!(r.technique_id.as_deref(), Some("test-status-diff"));
assert!(r.vector.is_some());
assert!(r.normative_strength.is_some());
}
#[test]
fn may_strength_reduces_signal_confidence() {
use parlov_core::{
always_applicable, NormativeStrength, OracleClass, SignalSurface, Technique, Vector,
};
let b = StatusCode::from_u16(403).expect("valid status");
let p = StatusCode::from_u16(404).expect("valid status");
let technique = Technique {
id: "test-may",
name: "May-level test",
oracle_class: OracleClass::Existence,
vector: Vector::StatusCodeDiff,
strength: NormativeStrength::May,
normalization_weight: Some(0.2),
inverted_signal_weight: None,
method_relevant: false,
parser_relevant: false,
applicability: always_applicable,
contradiction_surface: SignalSurface::Status,
};
let r = classify(b, p, vec![], &technique);
assert_eq!(r.verdict, OracleVerdict::Likely);
}
#[test]
fn signals_can_push_likely_to_confirmed() {
let b = StatusCode::from_u16(410).expect("valid status");
let p = StatusCode::from_u16(404).expect("valid status");
let technique = status_code_diff_technique();
let r = classify(b, p, vec![], &technique);
assert_eq!(r.verdict, OracleVerdict::Confirmed);
let signals = vec![Signal {
kind: SignalKind::HeaderPresence,
evidence: "etag present in baseline, absent in probe".into(),
rfc_basis: None,
}];
let r2 = classify(b, p, signals, &technique);
assert_eq!(r2.verdict, OracleVerdict::Confirmed);
assert!(r2.confidence > r.confidence);
}
#[test]
fn content_range_size_leak_produces_high_impact() {
let b = StatusCode::from_u16(206).expect("valid status");
let p = StatusCode::from_u16(404).expect("valid status");
let technique = status_code_diff_technique();
let signals = vec![Signal {
kind: SignalKind::MetadataLeak,
evidence: "Content-Range leaks total resource size: 1024 bytes".into(),
rfc_basis: Some("RFC 9110 §14.4".into()),
}];
let r = classify(b, p, signals, &technique);
assert_eq!(r.impact_class, Some(ImpactClass::High));
assert_eq!(r.severity, Some(Severity::High));
}
#[test]
fn etag_metadata_produces_medium_impact() {
let b = StatusCode::from_u16(200).expect("valid status");
let p = StatusCode::from_u16(404).expect("valid status");
let technique = status_code_diff_technique();
let signals = vec![Signal {
kind: SignalKind::MetadataLeak,
evidence: "ETag value \"v2\" leaks resource version identifier".into(),
rfc_basis: None,
}];
let r = classify(b, p, signals, &technique);
assert_eq!(r.impact_class, Some(ImpactClass::Medium));
}
#[test]
fn multiple_signal_families_add_corroboration() {
let b = StatusCode::from_u16(206).expect("valid status");
let p = StatusCode::from_u16(404).expect("valid status");
let technique = status_code_diff_technique();
let single = vec![Signal {
kind: SignalKind::HeaderPresence,
evidence: "content-range present in baseline".into(),
rfc_basis: None,
}];
let r1 = classify(b, p, single, &technique);
let multi = vec![
Signal {
kind: SignalKind::HeaderPresence,
evidence: "content-range present in baseline".into(),
rfc_basis: None,
},
Signal {
kind: SignalKind::HeaderPresence,
evidence: "etag present in baseline".into(),
rfc_basis: None,
},
];
let r2 = classify(b, p, multi, &technique);
assert!(r2.confidence >= r1.confidence);
}