parlov-analysis 0.6.0

Analysis engine trait and signal detection for parlov.
Documentation
//! Status code differential signal extractor.

use parlov_core::{DifferentialSet, Signal, SignalKind};

/// Extracts a status code differential signal from baseline vs probe responses.
///
/// Compares the most recent baseline status against the most recent probe status. Produces a
/// single `StatusCodeDiff` signal when they differ; returns an empty vec when identical.
#[must_use]
pub fn extract(data: &DifferentialSet) -> Vec<Signal> {
    let Some(baseline) = data.baseline.last() else {
        return vec![];
    };
    let Some(probe) = data.probe.last() else {
        return vec![];
    };

    let b_status = baseline.response.status;
    let p_status = probe.response.status;

    if b_status == p_status {
        return vec![];
    }

    vec![Signal {
        kind: SignalKind::StatusCodeDiff,
        evidence: format!(
            "{} (baseline) vs {} (probe)",
            b_status.as_u16(),
            p_status.as_u16(),
        ),
        rfc_basis: None,
    }]
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::signals::tests::{diff_set_with_statuses, single_diff_set};

    #[test]
    fn different_status_codes_produce_signal() {
        let ds = single_diff_set(403, 404);
        let signals = extract(&ds);
        assert_eq!(signals.len(), 1);
        assert_eq!(signals[0].kind, SignalKind::StatusCodeDiff);
        assert!(signals[0].evidence.contains("403"));
        assert!(signals[0].evidence.contains("404"));
    }

    #[test]
    fn identical_status_codes_produce_no_signal() {
        let ds = single_diff_set(404, 404);
        let signals = extract(&ds);
        assert!(signals.is_empty());
    }

    #[test]
    fn uses_most_recent_exchange() {
        let ds = diff_set_with_statuses(&[200, 403], &[200, 404]);
        let signals = extract(&ds);
        assert_eq!(signals.len(), 1);
        assert!(signals[0].evidence.contains("403"));
        assert!(signals[0].evidence.contains("404"));
    }

    #[test]
    fn empty_baseline_produces_no_signal() {
        let ds = diff_set_with_statuses(&[], &[404]);
        assert!(extract(&ds).is_empty());
    }

    #[test]
    fn empty_probe_produces_no_signal() {
        let ds = diff_set_with_statuses(&[200], &[]);
        assert!(extract(&ds).is_empty());
    }
}