Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
535
vendor/ruvector/crates/ruQu/tests/integration_tests.rs
vendored
Normal file
535
vendor/ruvector/crates/ruQu/tests/integration_tests.rs
vendored
Normal file
@@ -0,0 +1,535 @@
|
||||
//! End-to-end integration tests for ruQu coherence gate
|
||||
//!
|
||||
//! Tests full fabric initialization, syndrome ingestion through gate decision,
|
||||
//! and receipt generation with verification.
|
||||
|
||||
use ruqu::{
|
||||
filters::{
|
||||
EvidenceConfig, FilterConfig, FilterPipeline, ShiftConfig, StructuralConfig, Verdict,
|
||||
},
|
||||
prelude::*,
|
||||
syndrome::{DetectorBitmap, SyndromeBuffer, SyndromeDelta, SyndromeRound},
|
||||
tile::{
|
||||
GateDecision, GateThresholds, PermitToken, ReceiptLog, SyndromeDelta as TileSyndromeDelta,
|
||||
TileReport, TileZero, WorkerTile,
|
||||
},
|
||||
TILE_COUNT, WORKER_TILE_COUNT,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Full Fabric Initialization Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_fabric_initialization_all_tiles() {
|
||||
// Create all 255 worker tiles
|
||||
let workers: Vec<WorkerTile> = (1..=255).map(WorkerTile::new).collect();
|
||||
|
||||
assert_eq!(workers.len(), WORKER_TILE_COUNT);
|
||||
|
||||
for (i, worker) in workers.iter().enumerate() {
|
||||
assert_eq!(worker.tile_id, (i + 1) as u8);
|
||||
assert_eq!(worker.tick, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fabric_initialization_with_tilezero() {
|
||||
let thresholds = GateThresholds::default();
|
||||
let tilezero = TileZero::new(thresholds);
|
||||
|
||||
// Verify default thresholds
|
||||
assert!(tilezero.thresholds.structural_min_cut > 0.0);
|
||||
assert!(tilezero.thresholds.shift_max > 0.0);
|
||||
assert!(tilezero.thresholds.tau_deny > 0.0);
|
||||
assert!(tilezero.thresholds.tau_permit > tilezero.thresholds.tau_deny);
|
||||
assert!(tilezero.receipt_log.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fabric_tile_count_matches_constants() {
|
||||
assert_eq!(TILE_COUNT, 256);
|
||||
assert_eq!(WORKER_TILE_COUNT, 255);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Syndrome Ingestion Through Gate Decision Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_syndrome_ingestion_single_round() {
|
||||
let mut worker = WorkerTile::new(1);
|
||||
|
||||
// Ingest a syndrome
|
||||
let delta = TileSyndromeDelta::new(0, 1, 50);
|
||||
let report = worker.tick(&delta);
|
||||
|
||||
assert_eq!(report.tile_id, 1);
|
||||
assert_eq!(report.tick, 1);
|
||||
assert!(report.status & TileReport::STATUS_VALID != 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_syndrome_ingestion_multiple_rounds() {
|
||||
let mut worker = WorkerTile::new(1);
|
||||
|
||||
// Process multiple syndrome rounds
|
||||
for i in 0..100 {
|
||||
let delta = TileSyndromeDelta::new(i as u16 % 64, (i as u16 + 1) % 64, (i % 256) as u16);
|
||||
let report = worker.tick(&delta);
|
||||
assert_eq!(report.tick, i + 1);
|
||||
}
|
||||
|
||||
assert_eq!(worker.tick, 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_pipeline_syndrome_to_decision_safe() {
|
||||
// Setup tiles
|
||||
let thresholds = GateThresholds {
|
||||
structural_min_cut: 2.0,
|
||||
shift_max: 0.7,
|
||||
tau_deny: 0.01,
|
||||
tau_permit: 50.0,
|
||||
permit_ttl_ns: 4_000_000,
|
||||
};
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
|
||||
// Create workers and process syndromes
|
||||
let mut workers: Vec<WorkerTile> = (1..=10).map(WorkerTile::new).collect();
|
||||
|
||||
// Build graph with good connectivity
|
||||
for worker in &mut workers {
|
||||
// Add edges to create a well-connected graph
|
||||
worker.patch_graph.add_edge(0, 1, 100);
|
||||
worker.patch_graph.add_edge(1, 2, 100);
|
||||
worker.patch_graph.add_edge(2, 3, 100);
|
||||
worker.patch_graph.add_edge(3, 0, 100);
|
||||
worker.patch_graph.recompute_components();
|
||||
}
|
||||
|
||||
// Process syndromes with low values (indicating stability)
|
||||
for _ in 0..50 {
|
||||
for worker in &mut workers {
|
||||
let delta = TileSyndromeDelta::new(0, 1, 50); // Low syndrome value
|
||||
worker.tick(&delta);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect reports
|
||||
let reports: Vec<TileReport> = workers
|
||||
.iter()
|
||||
.map(|w| {
|
||||
let mut report = TileReport::new(w.tile_id);
|
||||
report.local_cut = 10.0; // Good cut value
|
||||
report.shift_score = 0.1; // Low shift
|
||||
report.e_value = 200.0; // Strong evidence
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
let decision = tilezero.merge_reports(reports);
|
||||
assert_eq!(decision, GateDecision::Permit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_pipeline_syndrome_to_decision_unsafe() {
|
||||
let thresholds = GateThresholds::default();
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
|
||||
// Create reports indicating structural problems
|
||||
let reports: Vec<TileReport> = (1..=10)
|
||||
.map(|i| {
|
||||
let mut report = TileReport::new(i);
|
||||
report.local_cut = 1.0; // Below threshold (5.0)
|
||||
report.shift_score = 0.1;
|
||||
report.e_value = 200.0;
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
let decision = tilezero.merge_reports(reports);
|
||||
assert_eq!(decision, GateDecision::Deny);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_pipeline_syndrome_to_decision_cautious() {
|
||||
let thresholds = GateThresholds::default();
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
|
||||
// Create reports with high shift but good structure
|
||||
let reports: Vec<TileReport> = (1..=10)
|
||||
.map(|i| {
|
||||
let mut report = TileReport::new(i);
|
||||
report.local_cut = 10.0;
|
||||
report.shift_score = 0.8; // Above threshold (0.5)
|
||||
report.e_value = 200.0;
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
let decision = tilezero.merge_reports(reports);
|
||||
assert_eq!(decision, GateDecision::Defer);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GateDecision Variants Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_gate_decision_safe_variant() {
|
||||
let decision = GateDecision::Permit;
|
||||
assert!(decision.is_permit());
|
||||
assert!(!decision.is_deny());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gate_decision_cautious_variant() {
|
||||
let decision = GateDecision::Defer;
|
||||
assert!(!decision.is_permit());
|
||||
assert!(!decision.is_deny());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gate_decision_unsafe_variant() {
|
||||
let decision = GateDecision::Deny;
|
||||
assert!(!decision.is_permit());
|
||||
assert!(decision.is_deny());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gate_decision_all_variants_distinct() {
|
||||
let permit = GateDecision::Permit;
|
||||
let defer = GateDecision::Defer;
|
||||
let deny = GateDecision::Deny;
|
||||
|
||||
assert_ne!(permit, defer);
|
||||
assert_ne!(permit, deny);
|
||||
assert_ne!(defer, deny);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Receipt Generation and Verification Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_receipt_generation_on_decision() {
|
||||
let thresholds = GateThresholds::default();
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
|
||||
// Make a decision
|
||||
let reports: Vec<TileReport> = (1..=5)
|
||||
.map(|i| {
|
||||
let mut report = TileReport::new(i);
|
||||
report.local_cut = 10.0;
|
||||
report.shift_score = 0.1;
|
||||
report.e_value = 200.0;
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
tilezero.merge_reports(reports);
|
||||
|
||||
// Verify receipt was created
|
||||
assert_eq!(tilezero.receipt_log.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_receipt_chain_integrity() {
|
||||
let thresholds = GateThresholds::default();
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
|
||||
// Make multiple decisions
|
||||
for _ in 0..10 {
|
||||
let reports: Vec<TileReport> = (1..=3)
|
||||
.map(|i| {
|
||||
let mut report = TileReport::new(i);
|
||||
report.local_cut = 10.0;
|
||||
report.shift_score = 0.1;
|
||||
report.e_value = 200.0;
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
tilezero.merge_reports(reports);
|
||||
}
|
||||
|
||||
// Verify chain
|
||||
assert_eq!(tilezero.receipt_log.len(), 10);
|
||||
|
||||
// Check that entries are chainable by looking up sequences
|
||||
for i in 0..10 {
|
||||
let entry = tilezero.receipt_log.get(i as u64);
|
||||
assert!(entry.is_some());
|
||||
assert_eq!(entry.unwrap().sequence, i as u64);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_permit_token_issuance() {
|
||||
let thresholds = GateThresholds::default();
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
|
||||
// Create reports for permit
|
||||
let reports: Vec<TileReport> = (1..=5)
|
||||
.map(|i| {
|
||||
let mut report = TileReport::new(i);
|
||||
report.local_cut = 10.0;
|
||||
report.shift_score = 0.1;
|
||||
report.e_value = 200.0;
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
let decision = tilezero.merge_reports(reports);
|
||||
assert_eq!(decision, GateDecision::Permit);
|
||||
|
||||
let token = tilezero.issue_permit(&decision);
|
||||
assert_eq!(token.decision, GateDecision::Permit);
|
||||
assert!(token.ttl_ns > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_permit_token_validity_window() {
|
||||
let token = PermitToken {
|
||||
decision: GateDecision::Permit,
|
||||
sequence: 0,
|
||||
timestamp: 1_000_000,
|
||||
ttl_ns: 500_000,
|
||||
witness_hash: [0u8; 32],
|
||||
signature: [1u8; 64], // Non-zero placeholder
|
||||
};
|
||||
|
||||
// Within validity window
|
||||
assert!(token.is_valid(1_200_000));
|
||||
assert!(token.is_valid(1_499_999));
|
||||
|
||||
// Outside validity window
|
||||
assert!(!token.is_valid(1_500_001));
|
||||
assert!(!token.is_valid(2_000_000));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Integration with Filter Pipeline Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_filter_pipeline_integration_permit() {
|
||||
let config = FilterConfig {
|
||||
structural: StructuralConfig {
|
||||
threshold: 1.0,
|
||||
use_subpolynomial: false,
|
||||
..Default::default()
|
||||
},
|
||||
shift: ShiftConfig {
|
||||
threshold: 0.5,
|
||||
..Default::default()
|
||||
},
|
||||
evidence: EvidenceConfig {
|
||||
tau_permit: 5.0,
|
||||
tau_deny: 0.2,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
|
||||
let mut pipeline = FilterPipeline::new(config);
|
||||
|
||||
// Build strong graph
|
||||
pipeline.structural_mut().insert_edge(1, 2, 2.0).unwrap();
|
||||
pipeline.structural_mut().insert_edge(2, 3, 2.0).unwrap();
|
||||
pipeline.structural_mut().insert_edge(3, 1, 2.0).unwrap();
|
||||
|
||||
// Add stable observations
|
||||
for _ in 0..20 {
|
||||
pipeline.shift_mut().update(0, 0.5);
|
||||
}
|
||||
|
||||
// Add strong evidence
|
||||
for _ in 0..5 {
|
||||
pipeline.evidence_mut().update(2.0);
|
||||
}
|
||||
|
||||
let state = ruqu::filters::SystemState::new(3);
|
||||
let result = pipeline.evaluate(&state);
|
||||
|
||||
assert_eq!(result.verdict, Some(Verdict::Permit));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_filter_pipeline_integration_deny() {
|
||||
let config = FilterConfig {
|
||||
structural: StructuralConfig {
|
||||
threshold: 5.0,
|
||||
use_subpolynomial: false,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut pipeline = FilterPipeline::new(config);
|
||||
|
||||
// Build weak graph
|
||||
pipeline.structural_mut().insert_edge(1, 2, 1.0).unwrap();
|
||||
|
||||
let state = ruqu::filters::SystemState::new(2);
|
||||
let result = pipeline.evaluate(&state);
|
||||
|
||||
assert_eq!(result.verdict, Some(Verdict::Deny));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// End-to-End Workflow Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_complete_workflow_healthy_system() {
|
||||
// 1. Initialize fabric
|
||||
let thresholds = GateThresholds::default();
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
let mut workers: Vec<WorkerTile> = (1..=5).map(WorkerTile::new).collect();
|
||||
|
||||
// 2. Build graph structure in each worker
|
||||
for worker in &mut workers {
|
||||
worker.patch_graph.add_edge(0, 1, 200);
|
||||
worker.patch_graph.add_edge(1, 2, 200);
|
||||
worker.patch_graph.add_edge(2, 0, 200);
|
||||
worker.patch_graph.recompute_components();
|
||||
}
|
||||
|
||||
// 3. Simulate syndrome stream
|
||||
for cycle in 0..50 {
|
||||
for worker in &mut workers {
|
||||
let delta = TileSyndromeDelta::new(
|
||||
(cycle % 3) as u16,
|
||||
((cycle + 1) % 3) as u16,
|
||||
50, // Low syndrome value
|
||||
);
|
||||
worker.tick(&delta);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Collect reports and make decision
|
||||
let reports: Vec<TileReport> = workers
|
||||
.iter()
|
||||
.map(|w| {
|
||||
let mut report = TileReport::new(w.tile_id);
|
||||
report.local_cut = w.local_cut_state.cut_value.max(10.0);
|
||||
report.shift_score = 0.1;
|
||||
report.e_value = 200.0;
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
let decision = tilezero.merge_reports(reports);
|
||||
|
||||
// 5. Verify outcome
|
||||
assert_eq!(decision, GateDecision::Permit);
|
||||
assert_eq!(tilezero.receipt_log.len(), 1);
|
||||
|
||||
// 6. Issue and verify permit
|
||||
let token = tilezero.issue_permit(&decision);
|
||||
assert_eq!(token.decision, GateDecision::Permit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_complete_workflow_degrading_system() {
|
||||
let thresholds = GateThresholds::default();
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
|
||||
// Simulate degradation over time
|
||||
for cycle in 0..20 {
|
||||
let cut_value = 10.0 - (cycle as f64 * 0.5); // Degrading cut
|
||||
|
||||
let reports: Vec<TileReport> = (1..=5)
|
||||
.map(|i| {
|
||||
let mut report = TileReport::new(i);
|
||||
report.local_cut = cut_value;
|
||||
report.shift_score = 0.1 + (cycle as f64 * 0.02);
|
||||
report.e_value = 200.0 / (cycle as f64 + 1.0);
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
let decision = tilezero.merge_reports(reports);
|
||||
|
||||
// Eventually should transition from Permit -> Defer -> Deny
|
||||
if cut_value < thresholds.structural_min_cut {
|
||||
assert_eq!(decision, GateDecision::Deny);
|
||||
}
|
||||
}
|
||||
|
||||
// Should have logged all decisions
|
||||
assert_eq!(tilezero.receipt_log.len(), 20);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Proptest Property-Based Tests
|
||||
// ============================================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod proptest_integration {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
proptest! {
|
||||
#![proptest_config(ProptestConfig::with_cases(100))]
|
||||
|
||||
#[test]
|
||||
fn prop_decision_consistency(
|
||||
cut_values in prop::collection::vec(0.0f64..20.0, 1..10),
|
||||
shift_values in prop::collection::vec(0.0f64..1.0, 1..10),
|
||||
e_values in prop::collection::vec(0.01f64..500.0, 1..10),
|
||||
) {
|
||||
let thresholds = GateThresholds::default();
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
|
||||
let reports: Vec<TileReport> = cut_values
|
||||
.iter()
|
||||
.zip(shift_values.iter())
|
||||
.zip(e_values.iter())
|
||||
.enumerate()
|
||||
.map(|(i, ((cut, shift), e_val))| {
|
||||
let mut report = TileReport::new((i + 1) as u8);
|
||||
report.local_cut = *cut;
|
||||
report.shift_score = *shift;
|
||||
report.e_value = *e_val;
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
let decision = tilezero.merge_reports(reports.clone());
|
||||
|
||||
// Verify decision is consistent with filters
|
||||
let min_cut: f64 = reports.iter().map(|r| r.local_cut).filter(|c| *c > 0.0).fold(f64::MAX, |a, b| a.min(b));
|
||||
let max_shift: f64 = reports.iter().map(|r| r.shift_score).fold(0.0, |a, b| a.max(b));
|
||||
|
||||
if min_cut < thresholds.structural_min_cut {
|
||||
prop_assert_eq!(decision, GateDecision::Deny);
|
||||
} else if max_shift >= thresholds.shift_max {
|
||||
prop_assert_eq!(decision, GateDecision::Defer);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prop_receipt_log_always_grows(num_decisions in 1usize..50) {
|
||||
let thresholds = GateThresholds::default();
|
||||
let mut tilezero = TileZero::new(thresholds);
|
||||
|
||||
for _ in 0..num_decisions {
|
||||
let reports: Vec<TileReport> = (1..=3)
|
||||
.map(|i| {
|
||||
let mut report = TileReport::new(i);
|
||||
report.local_cut = 10.0;
|
||||
report.shift_score = 0.1;
|
||||
report.e_value = 200.0;
|
||||
report
|
||||
})
|
||||
.collect();
|
||||
|
||||
tilezero.merge_reports(reports);
|
||||
}
|
||||
|
||||
prop_assert_eq!(tilezero.receipt_log.len(), num_decisions);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user