use super::*;
use parlov_core::OracleVerdict;
use crate::signals::tests::{diff_set_with_statuses, fake_exchange, status_code_diff_technique};
fn diff_set_1(baseline_status: u16, probe_status: u16) -> DifferentialSet {
DifferentialSet {
baseline: vec![fake_exchange(baseline_status)],
probe: vec![fake_exchange(probe_status)],
technique: status_code_diff_technique(),
}
}
fn diff_set_n(baseline_statuses: &[u16], probe_statuses: &[u16]) -> DifferentialSet {
diff_set_with_statuses(baseline_statuses, probe_statuses)
}
#[test]
fn evaluate_1_sample_diff_returns_need_more() {
let ds = diff_set_1(403, 404);
assert!(matches!(
ExistenceAnalyzer.evaluate(&ds),
SampleDecision::NeedMore
));
}
#[test]
fn evaluate_2_samples_diff_returns_need_more() {
let ds = diff_set_n(&[403, 403], &[404, 404]);
assert!(matches!(
ExistenceAnalyzer.evaluate(&ds),
SampleDecision::NeedMore
));
}
#[test]
fn evaluate_3_samples_stable_diff_confirmed() {
let ds = diff_set_n(&[403, 403, 403], &[404, 404, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert_eq!(result.verdict, OracleVerdict::Confirmed);
assert!(result.severity.is_some());
}
#[test]
fn evaluate_3_samples_unstable_probe_not_present() {
let ds = diff_set_n(&[403, 403, 403], &[404, 200, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert_eq!(result.verdict, OracleVerdict::NotPresent);
assert_eq!(result.severity, None);
}
#[test]
fn evaluate_3_samples_unstable_baseline_not_present() {
let ds = diff_set_n(&[403, 200, 403], &[404, 404, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert_eq!(result.verdict, OracleVerdict::NotPresent);
assert_eq!(result.severity, None);
}
#[test]
fn evaluate_complete_not_present_on_same_status() {
let ds = diff_set_1(404, 404);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert_eq!(result.verdict, OracleVerdict::NotPresent);
assert_eq!(result.severity, None);
}
#[test]
fn evaluate_complete_confirmed_on_403_vs_404() {
let ds = diff_set_n(&[403, 403, 403], &[404, 404, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert_eq!(result.verdict, OracleVerdict::Confirmed);
assert!(result.severity.is_some());
}
#[test]
fn analyze_provided_method_delegates_to_evaluate() {
let ds = diff_set_n(&[200, 200, 200], &[404, 404, 404]);
let result = ExistenceAnalyzer.analyze(&ds);
assert_eq!(result.verdict, OracleVerdict::Confirmed);
assert!(result.severity.is_some());
}
#[test]
fn need_more_variant_is_constructible() {
assert!(matches!(SampleDecision::NeedMore, SampleDecision::NeedMore));
}
#[test]
fn result_carries_technique_metadata() {
let ds = diff_set_n(&[403, 403, 403], &[404, 404, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert!(result.technique_id.is_some());
assert!(result.vector.is_some());
assert!(result.normative_strength.is_some());
}
#[test]
fn result_carries_confidence_and_impact() {
let ds = diff_set_n(&[403, 403, 403], &[404, 404, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert!(result.confidence > 0);
assert!(result.impact_class.is_some());
assert!(!result.reasons.is_empty());
}
#[test]
fn result_carries_status_code_diff_signal() {
let ds = diff_set_n(&[403, 403, 403], &[404, 404, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert!(
result
.signals
.iter()
.any(|s| s.kind == parlov_core::SignalKind::StatusCodeDiff),
"expected StatusCodeDiff signal"
);
}
#[test]
fn unstable_result_carries_signal() {
let ds = diff_set_n(&[403, 403, 403], &[404, 200, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert_eq!(result.verdict, OracleVerdict::NotPresent);
assert!(
result.primary_evidence().contains("unstable"),
"unstable result should carry unstable signal"
);
}
fn redirect_diff_technique() -> parlov_core::Technique {
use parlov_core::{NormativeStrength, OracleClass, Technique, Vector};
Technique {
id: "test-redirect-diff",
name: "Test redirect diff",
oracle_class: OracleClass::Existence,
vector: Vector::RedirectDiff,
strength: NormativeStrength::Should,
}
}
fn redirect_diff_set_n(baseline_statuses: &[u16], probe_statuses: &[u16]) -> DifferentialSet {
use crate::signals::tests::fake_exchange;
DifferentialSet {
baseline: baseline_statuses
.iter()
.map(|&s| fake_exchange(s))
.collect(),
probe: probe_statuses
.iter()
.map(|&s| fake_exchange(s))
.collect(),
technique: redirect_diff_technique(),
}
}
#[test]
fn redirect_diff_no_3xx_on_either_side_is_not_present() {
let ds = redirect_diff_set_n(&[200, 200, 200], &[412, 412, 412]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert_eq!(
result.verdict,
OracleVerdict::NotPresent,
"stable 200 vs 412 under RedirectDiff must be NotPresent: technique did not fire"
);
assert_eq!(result.severity, None);
assert!(
result.primary_evidence().contains("did not fire"),
"signal evidence must explain dismissal; got: {:?}",
result.primary_evidence()
);
}
#[test]
fn redirect_diff_baseline_3xx_proceeds_to_scoring() {
let ds = redirect_diff_set_n(&[301, 301, 301], &[404, 404, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert_ne!(
result.verdict,
OracleVerdict::NotPresent,
"301 vs 404 under RedirectDiff is a real differential; must not be dismissed"
);
}
#[test]
fn redirect_diff_probe_3xx_proceeds_to_scoring() {
let ds = redirect_diff_set_n(&[302, 302, 302], &[404, 404, 404]);
let SampleDecision::Complete(result) = ExistenceAnalyzer.evaluate(&ds) else {
panic!("expected Complete");
};
assert_ne!(
result.verdict,
OracleVerdict::NotPresent,
"302 vs 404 under RedirectDiff is a real differential; must not be dismissed"
);
}