Files
wifi-densepose/vendor/ruvector/crates/cognitum-gate-tilezero/tests/decision_tests.rs

507 lines
15 KiB
Rust

//! Comprehensive tests for PERMIT/DEFER/DENY decision logic
//!
//! Tests cover:
//! - Three-filter decision pipeline
//! - Threshold configurations
//! - Edge cases and boundary conditions
//! - Security scenarios (policy violations, replay detection)
use cognitum_gate_tilezero::decision::{EvidenceDecision, GateDecision, GateThresholds};
#[cfg(test)]
mod gate_decision {
use super::*;
#[test]
fn test_decision_display() {
assert_eq!(GateDecision::Permit.to_string(), "permit");
assert_eq!(GateDecision::Defer.to_string(), "defer");
assert_eq!(GateDecision::Deny.to_string(), "deny");
}
#[test]
fn test_decision_equality() {
assert_eq!(GateDecision::Permit, GateDecision::Permit);
assert_eq!(GateDecision::Defer, GateDecision::Defer);
assert_eq!(GateDecision::Deny, GateDecision::Deny);
assert_ne!(GateDecision::Permit, GateDecision::Defer);
assert_ne!(GateDecision::Permit, GateDecision::Deny);
assert_ne!(GateDecision::Defer, GateDecision::Deny);
}
}
#[cfg(test)]
mod evidence_decision {
use super::*;
#[test]
fn test_evidence_values() {
let accept = EvidenceDecision::Accept;
let cont = EvidenceDecision::Continue;
let reject = EvidenceDecision::Reject;
assert_eq!(accept, EvidenceDecision::Accept);
assert_eq!(cont, EvidenceDecision::Continue);
assert_eq!(reject, EvidenceDecision::Reject);
}
}
#[cfg(test)]
mod threshold_configuration {
use super::*;
#[test]
fn test_default_thresholds() {
let thresholds = GateThresholds::default();
assert_eq!(thresholds.tau_deny, 0.01);
assert_eq!(thresholds.tau_permit, 100.0);
assert_eq!(thresholds.min_cut, 5.0);
assert_eq!(thresholds.max_shift, 0.5);
assert_eq!(thresholds.permit_ttl_ns, 60_000_000_000);
}
#[test]
fn test_custom_thresholds() {
let thresholds = GateThresholds {
tau_deny: 0.05,
tau_permit: 50.0,
min_cut: 10.0,
max_shift: 0.3,
permit_ttl_ns: 30_000_000_000,
theta_uncertainty: 15.0,
theta_confidence: 3.0,
};
assert_eq!(thresholds.tau_deny, 0.05);
assert_eq!(thresholds.tau_permit, 50.0);
assert_eq!(thresholds.min_cut, 10.0);
}
#[test]
fn test_threshold_ordering() {
let thresholds = GateThresholds::default();
// tau_deny < 1 < tau_permit (typical e-process thresholds)
assert!(thresholds.tau_deny < 1.0);
assert!(thresholds.tau_permit > 1.0);
assert!(thresholds.tau_deny < thresholds.tau_permit);
}
#[test]
fn test_conformal_thresholds() {
let thresholds = GateThresholds::default();
// theta_confidence < theta_uncertainty (smaller set = more confident)
assert!(thresholds.theta_confidence < thresholds.theta_uncertainty);
}
}
#[cfg(test)]
mod three_filter_logic {
use super::*;
/// Test the structural filter (min-cut check)
#[test]
fn test_structural_filter_deny() {
// If min-cut is below threshold, should DENY
let thresholds = GateThresholds::default();
// Low min-cut (below threshold of 5.0)
let min_cut = 3.0;
let shift_pressure = 0.1; // OK
let e_aggregate = 150.0; // OK
let decision = apply_three_filters(min_cut, shift_pressure, e_aggregate, &thresholds);
assert_eq!(decision, GateDecision::Deny);
}
/// Test the shift filter (coherence check)
#[test]
fn test_shift_filter_defer() {
let thresholds = GateThresholds::default();
// OK min-cut, high shift pressure
let min_cut = 10.0; // OK
let shift_pressure = 0.8; // Above threshold of 0.5
let e_aggregate = 150.0; // OK
let decision = apply_three_filters(min_cut, shift_pressure, e_aggregate, &thresholds);
assert_eq!(decision, GateDecision::Defer);
}
/// Test the evidence filter (e-value check)
#[test]
fn test_evidence_filter_deny() {
let thresholds = GateThresholds::default();
// OK min-cut, OK shift, low e-value (evidence against coherence)
let min_cut = 10.0;
let shift_pressure = 0.1;
let e_aggregate = 0.005; // Below tau_deny of 0.01
let decision = apply_three_filters(min_cut, shift_pressure, e_aggregate, &thresholds);
assert_eq!(decision, GateDecision::Deny);
}
#[test]
fn test_evidence_filter_defer() {
let thresholds = GateThresholds::default();
// OK min-cut, OK shift, moderate e-value (insufficient evidence)
let min_cut = 10.0;
let shift_pressure = 0.1;
let e_aggregate = 50.0; // Between tau_deny (0.01) and tau_permit (100)
let decision = apply_three_filters(min_cut, shift_pressure, e_aggregate, &thresholds);
assert_eq!(decision, GateDecision::Defer);
}
#[test]
fn test_all_filters_pass_permit() {
let thresholds = GateThresholds::default();
// Everything OK
let min_cut = 10.0;
let shift_pressure = 0.1;
let e_aggregate = 150.0; // Above tau_permit of 100
let decision = apply_three_filters(min_cut, shift_pressure, e_aggregate, &thresholds);
assert_eq!(decision, GateDecision::Permit);
}
// Helper function to simulate the three-filter logic
fn apply_three_filters(
min_cut: f64,
shift_pressure: f64,
e_aggregate: f64,
thresholds: &GateThresholds,
) -> GateDecision {
// 1. Structural filter
if min_cut < thresholds.min_cut {
return GateDecision::Deny;
}
// 2. Shift filter
if shift_pressure >= thresholds.max_shift {
return GateDecision::Defer;
}
// 3. Evidence filter
if e_aggregate < thresholds.tau_deny {
return GateDecision::Deny;
}
if e_aggregate < thresholds.tau_permit {
return GateDecision::Defer;
}
GateDecision::Permit
}
}
#[cfg(test)]
mod boundary_conditions {
use super::*;
#[test]
fn test_min_cut_at_threshold() {
let thresholds = GateThresholds::default();
// Exactly at threshold
let decision = decide_structural(5.0, &thresholds);
assert_eq!(decision, GateDecision::Permit); // >= threshold is OK
}
#[test]
fn test_min_cut_just_below() {
let thresholds = GateThresholds::default();
let decision = decide_structural(4.999, &thresholds);
assert_eq!(decision, GateDecision::Deny);
}
#[test]
fn test_e_value_at_deny_threshold() {
let thresholds = GateThresholds::default();
let decision = decide_evidence(0.01, &thresholds);
assert_eq!(decision, EvidenceDecision::Continue); // Exactly at threshold continues
}
#[test]
fn test_e_value_at_permit_threshold() {
let thresholds = GateThresholds::default();
let decision = decide_evidence(100.0, &thresholds);
assert_eq!(decision, EvidenceDecision::Accept);
}
#[test]
fn test_zero_values() {
let thresholds = GateThresholds::default();
assert_eq!(decide_structural(0.0, &thresholds), GateDecision::Deny);
assert_eq!(decide_evidence(0.0, &thresholds), EvidenceDecision::Reject);
}
// Helper functions
fn decide_structural(min_cut: f64, thresholds: &GateThresholds) -> GateDecision {
if min_cut >= thresholds.min_cut {
GateDecision::Permit
} else {
GateDecision::Deny
}
}
fn decide_evidence(e_aggregate: f64, thresholds: &GateThresholds) -> EvidenceDecision {
if e_aggregate < thresholds.tau_deny {
EvidenceDecision::Reject
} else if e_aggregate >= thresholds.tau_permit {
EvidenceDecision::Accept
} else {
EvidenceDecision::Continue
}
}
}
#[cfg(test)]
mod filter_priority {
use super::*;
/// Structural filter has highest priority (checked first)
#[test]
fn test_structural_overrides_evidence() {
let thresholds = GateThresholds::default();
// Low min-cut but high e-value
let min_cut = 1.0; // Fail structural
let e_aggregate = 1000.0; // Would pass evidence
// Structural failure should result in DENY
let decision = if min_cut < thresholds.min_cut {
GateDecision::Deny
} else if e_aggregate >= thresholds.tau_permit {
GateDecision::Permit
} else {
GateDecision::Defer
};
assert_eq!(decision, GateDecision::Deny);
}
/// Shift filter checked after structural
#[test]
fn test_shift_overrides_evidence() {
let thresholds = GateThresholds::default();
// Good min-cut, high shift, high e-value
let min_cut = 10.0; // Pass structural
let shift_pressure = 0.9; // Fail shift
let e_aggregate = 1000.0; // Would pass evidence
let decision = if min_cut < thresholds.min_cut {
GateDecision::Deny
} else if shift_pressure >= thresholds.max_shift {
GateDecision::Defer
} else if e_aggregate >= thresholds.tau_permit {
GateDecision::Permit
} else {
GateDecision::Defer
};
assert_eq!(decision, GateDecision::Defer);
}
}
#[cfg(test)]
mod ttl_scenarios {
use super::*;
#[test]
fn test_permit_ttl() {
let thresholds = GateThresholds::default();
assert_eq!(thresholds.permit_ttl_ns, 60_000_000_000); // 60 seconds
}
#[test]
fn test_custom_short_ttl() {
let thresholds = GateThresholds {
permit_ttl_ns: 1_000_000_000, // 1 second
..Default::default()
};
assert_eq!(thresholds.permit_ttl_ns, 1_000_000_000);
}
#[test]
fn test_custom_long_ttl() {
let thresholds = GateThresholds {
permit_ttl_ns: 3600_000_000_000, // 1 hour
..Default::default()
};
assert_eq!(thresholds.permit_ttl_ns, 3600_000_000_000);
}
}
#[cfg(test)]
mod extreme_values {
use super::*;
#[test]
fn test_very_high_e_value() {
let thresholds = GateThresholds::default();
let decision = decide_evidence_full(1e10, &thresholds);
assert_eq!(decision, EvidenceDecision::Accept);
}
#[test]
fn test_very_low_e_value() {
let thresholds = GateThresholds::default();
let decision = decide_evidence_full(1e-10, &thresholds);
assert_eq!(decision, EvidenceDecision::Reject);
}
#[test]
fn test_very_high_min_cut() {
let thresholds = GateThresholds::default();
let decision = decide_structural_full(1000.0, &thresholds);
assert_eq!(decision, GateDecision::Permit);
}
// Helper
fn decide_evidence_full(e_aggregate: f64, thresholds: &GateThresholds) -> EvidenceDecision {
if e_aggregate < thresholds.tau_deny {
EvidenceDecision::Reject
} else if e_aggregate >= thresholds.tau_permit {
EvidenceDecision::Accept
} else {
EvidenceDecision::Continue
}
}
fn decide_structural_full(min_cut: f64, thresholds: &GateThresholds) -> GateDecision {
if min_cut >= thresholds.min_cut {
GateDecision::Permit
} else {
GateDecision::Deny
}
}
}
#[cfg(test)]
mod serialization {
use super::*;
#[test]
fn test_decision_serialization() {
let decisions = [
GateDecision::Permit,
GateDecision::Defer,
GateDecision::Deny,
];
for decision in &decisions {
let json = serde_json::to_string(decision).unwrap();
let restored: GateDecision = serde_json::from_str(&json).unwrap();
assert_eq!(*decision, restored);
}
}
#[test]
fn test_decision_json_values() {
assert_eq!(
serde_json::to_string(&GateDecision::Permit).unwrap(),
"\"permit\""
);
assert_eq!(
serde_json::to_string(&GateDecision::Defer).unwrap(),
"\"defer\""
);
assert_eq!(
serde_json::to_string(&GateDecision::Deny).unwrap(),
"\"deny\""
);
}
#[test]
fn test_thresholds_serialization() {
let thresholds = GateThresholds::default();
let json = serde_json::to_string(&thresholds).unwrap();
let restored: GateThresholds = serde_json::from_str(&json).unwrap();
assert_eq!(thresholds.tau_deny, restored.tau_deny);
assert_eq!(thresholds.tau_permit, restored.tau_permit);
assert_eq!(thresholds.min_cut, restored.min_cut);
}
}
// Property-based tests
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_permit_requires_all_pass(
min_cut in 0.0f64..100.0,
shift in 0.0f64..1.0,
e_val in 0.001f64..1000.0
) {
let thresholds = GateThresholds::default();
let structural_ok = min_cut >= thresholds.min_cut;
let shift_ok = shift < thresholds.max_shift;
let evidence_ok = e_val >= thresholds.tau_permit;
let decision = apply_filters(min_cut, shift, e_val, &thresholds);
if decision == GateDecision::Permit {
assert!(structural_ok && shift_ok && evidence_ok);
}
}
#[test]
fn prop_structural_fail_is_deny(min_cut in 0.0f64..4.9) {
let thresholds = GateThresholds::default();
// Any structural failure (min_cut < 5.0) should result in Deny
let decision = apply_filters(min_cut, 0.0, 1000.0, &thresholds);
assert_eq!(decision, GateDecision::Deny);
}
#[test]
fn prop_evidence_deny_threshold(e_val in 0.0f64..0.009) {
let thresholds = GateThresholds::default();
// E-value below tau_deny should result in Deny (if structural passes)
let decision = apply_filters(100.0, 0.0, e_val, &thresholds);
assert_eq!(decision, GateDecision::Deny);
}
}
fn apply_filters(
min_cut: f64,
shift_pressure: f64,
e_aggregate: f64,
thresholds: &GateThresholds,
) -> GateDecision {
if min_cut < thresholds.min_cut {
return GateDecision::Deny;
}
if shift_pressure >= thresholds.max_shift {
return GateDecision::Defer;
}
if e_aggregate < thresholds.tau_deny {
return GateDecision::Deny;
}
if e_aggregate < thresholds.tau_permit {
return GateDecision::Defer;
}
GateDecision::Permit
}
}