mirror of
https://github.com/ruvnet/RuView
synced 2026-08-04 19:31:42 +00:00
feat: vendor midstream and sublinear-time-solver libraries
Add ruvnet/midstream (AIMDS real-time inference) and ruvnet/sublinear-time-solver (sublinear optimization algorithms) as vendored dependencies under vendor/. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
+723
@@ -0,0 +1,723 @@
|
||||
//! Comprehensive Integration Tests for MidStream System
|
||||
//!
|
||||
//! Tests end-to-end workflows across all crates:
|
||||
//! - temporal-compare: Sequence analysis and pattern matching
|
||||
//! - nanosecond-scheduler: Real-time scheduling with nanosecond precision
|
||||
//! - temporal-attractor-studio: Dynamical systems and attractor analysis
|
||||
//! - temporal-neural-solver: Temporal logic verification and neural reasoning
|
||||
//! - strange-loop: Meta-learning and self-reference
|
||||
//! - quic-multistream: High-performance multiplexed streaming
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
// Import from published crates
|
||||
use midstreamer_temporal_compare::{TemporalComparator, Sequence, ComparisonAlgorithm};
|
||||
use midstreamer_scheduler::{RealtimeScheduler, SchedulerConfig, Priority, Deadline};
|
||||
use midstreamer_attractor::{AttractorAnalyzer, PhasePoint, AttractorType};
|
||||
use midstreamer_neural_solver::{TemporalNeuralSolver, TemporalFormula, TemporalState, VerificationStrictness};
|
||||
use midstreamer_strange_loop::{StrangeLoop, MetaLevel, StrangeLoopConfig};
|
||||
|
||||
/// Test 1: Scheduler + Temporal Compare Integration
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Use temporal patterns to predict task priority
|
||||
/// - Schedule tasks based on historical pattern similarity
|
||||
/// - Verify scheduling order respects pattern-based priorities
|
||||
#[test]
|
||||
fn test_scheduler_temporal_integration() {
|
||||
println!("\n=== Test 1: Scheduler + Temporal Compare Integration ===");
|
||||
|
||||
let scheduler: RealtimeScheduler<String> = RealtimeScheduler::default();
|
||||
let comparator: TemporalComparator<String> = TemporalComparator::new(100, 1000);
|
||||
|
||||
// Historical execution patterns
|
||||
let mut seq1: Sequence<String> = Sequence::new();
|
||||
seq1.push("init".to_string(), 0);
|
||||
seq1.push("process".to_string(), 100);
|
||||
seq1.push("complete".to_string(), 200);
|
||||
|
||||
let mut seq2: Sequence<String> = Sequence::new();
|
||||
seq2.push("init".to_string(), 0);
|
||||
seq2.push("process".to_string(), 100);
|
||||
seq2.push("complete".to_string(), 200);
|
||||
|
||||
// Compare sequences to detect patterns
|
||||
let result = comparator.compare(&seq1, &seq2, ComparisonAlgorithm::DTW).unwrap();
|
||||
println!(" Pattern similarity (DTW): {:.4}", result.distance);
|
||||
assert!(result.distance < 1.0, "Should detect identical patterns");
|
||||
|
||||
// Schedule tasks with priority based on pattern confidence
|
||||
let priority = if result.distance < 0.5 {
|
||||
Priority::High
|
||||
} else {
|
||||
Priority::Medium
|
||||
};
|
||||
|
||||
let task_id = scheduler.schedule(
|
||||
"pattern_based_task".to_string(),
|
||||
Deadline::from_millis(100),
|
||||
priority,
|
||||
).unwrap();
|
||||
|
||||
println!(" ✓ Task {} scheduled with {:?} priority", task_id, priority);
|
||||
assert_eq!(scheduler.queue_size(), 1);
|
||||
|
||||
// Verify task retrieval
|
||||
let task = scheduler.next_task().unwrap();
|
||||
assert_eq!(task.id, task_id);
|
||||
assert_eq!(task.priority, priority);
|
||||
println!(" ✓ Task retrieved successfully with correct priority");
|
||||
|
||||
println!("=== Test 1 PASSED ===\n");
|
||||
}
|
||||
|
||||
/// Test 2: Scheduler + Attractor Analysis Integration
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Analyze system behavior dynamics while scheduling tasks
|
||||
/// - Detect attractors in task execution patterns
|
||||
/// - Adjust scheduling based on stability analysis
|
||||
#[test]
|
||||
fn test_scheduler_attractor_integration() {
|
||||
println!("\n=== Test 2: Scheduler + Attractor Analysis Integration ===");
|
||||
|
||||
let scheduler: RealtimeScheduler<f64> = RealtimeScheduler::default();
|
||||
let mut analyzer = AttractorAnalyzer::new(3, 1000);
|
||||
|
||||
// Simulate task scheduling with dynamic behavior
|
||||
for i in 0..150 {
|
||||
let t = i as f64 * 0.1;
|
||||
|
||||
// Add phase point tracking system state
|
||||
let point = PhasePoint::new(
|
||||
vec![
|
||||
t.sin(), // CPU load
|
||||
t.cos(), // Memory usage
|
||||
(-t / 10.0).exp(), // Queue depth (decaying)
|
||||
],
|
||||
i as u64 * 100,
|
||||
);
|
||||
analyzer.add_point(point).unwrap();
|
||||
|
||||
// Schedule task with priority based on queue depth
|
||||
let priority = if i < 50 {
|
||||
Priority::High
|
||||
} else if i < 100 {
|
||||
Priority::Medium
|
||||
} else {
|
||||
Priority::Low
|
||||
};
|
||||
|
||||
scheduler.schedule(
|
||||
i as f64,
|
||||
Deadline::from_millis((i + 10) as u64),
|
||||
priority,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
// Analyze attractor to understand system stability
|
||||
let attractor_info = analyzer.analyze().unwrap();
|
||||
println!(" Attractor type: {:?}", attractor_info.attractor_type);
|
||||
println!(" Stable: {}", attractor_info.is_stable);
|
||||
println!(" Confidence: {:.2}", attractor_info.confidence);
|
||||
println!(" Max Lyapunov: {:.4}", attractor_info.max_lyapunov_exponent().unwrap_or(0.0));
|
||||
|
||||
// Verify behavior analysis
|
||||
assert_eq!(attractor_info.dimension, 3);
|
||||
assert!(attractor_info.confidence > 0.5);
|
||||
|
||||
// Verify scheduler processed all tasks
|
||||
assert_eq!(scheduler.queue_size(), 150);
|
||||
println!(" ✓ Scheduled 150 tasks with attractor-aware prioritization");
|
||||
|
||||
// Get scheduler stats
|
||||
let stats = scheduler.stats();
|
||||
println!(" ✓ Scheduler stats: {} total tasks, {} in queue",
|
||||
stats.total_tasks, stats.queue_size);
|
||||
assert_eq!(stats.total_tasks, 150);
|
||||
|
||||
println!("=== Test 2 PASSED ===\n");
|
||||
}
|
||||
|
||||
/// Test 3: Attractor + Neural Solver Integration
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Detect behavioral attractors in system dynamics
|
||||
/// - Verify temporal properties using neural solver
|
||||
/// - Ensure attractor stability matches temporal invariants
|
||||
#[test]
|
||||
fn test_attractor_solver_integration() {
|
||||
println!("\n=== Test 3: Attractor + Neural Solver Integration ===");
|
||||
|
||||
let mut analyzer = AttractorAnalyzer::new(2, 1000);
|
||||
let mut solver = TemporalNeuralSolver::new(1000, 500, VerificationStrictness::High);
|
||||
|
||||
// Simulate limit cycle behavior (periodic oscillation)
|
||||
for i in 0..200 {
|
||||
let t = i as f64 * 0.1;
|
||||
|
||||
// Create periodic trajectory
|
||||
let point = PhasePoint::new(
|
||||
vec![t.sin(), t.cos()],
|
||||
i as u64 * 10,
|
||||
);
|
||||
analyzer.add_point(point).unwrap();
|
||||
|
||||
// Record temporal state
|
||||
let mut state = TemporalState::new(i, i * 10);
|
||||
state.set_proposition("oscillating", true);
|
||||
state.set_proposition("bounded", t.sin().abs() <= 1.0 && t.cos().abs() <= 1.0);
|
||||
state.set_proposition("periodic", i % 63 < 5); // Approximate period detection
|
||||
solver.add_state(state);
|
||||
}
|
||||
|
||||
// Analyze attractor
|
||||
let attractor_info = analyzer.analyze().unwrap();
|
||||
println!(" Attractor type: {:?}", attractor_info.attractor_type);
|
||||
println!(" Trajectory points: {}", analyzer.trajectory_length());
|
||||
|
||||
// Verify temporal properties match attractor behavior
|
||||
let bounded_formula = TemporalFormula::globally(TemporalFormula::atom("bounded"));
|
||||
let bounded_result = solver.verify(&bounded_formula).unwrap();
|
||||
|
||||
let oscillating_formula = TemporalFormula::globally(TemporalFormula::atom("oscillating"));
|
||||
let oscillating_result = solver.verify(&oscillating_formula).unwrap();
|
||||
|
||||
println!(" ✓ Bounded property: {}", bounded_result.satisfied);
|
||||
println!(" ✓ Oscillating property: {}", oscillating_result.satisfied);
|
||||
|
||||
assert!(bounded_result.satisfied, "Limit cycle should remain bounded");
|
||||
assert!(oscillating_result.satisfied, "System should always oscillate");
|
||||
|
||||
// Verify eventually periodic
|
||||
let periodic_formula = TemporalFormula::finally(TemporalFormula::atom("periodic"));
|
||||
let periodic_result = solver.verify(&periodic_formula).unwrap();
|
||||
assert!(periodic_result.satisfied, "Should detect periodic behavior");
|
||||
|
||||
println!(" ✓ Attractor analysis matches temporal verification");
|
||||
println!("=== Test 3 PASSED ===\n");
|
||||
}
|
||||
|
||||
/// Test 4: Temporal Compare + Neural Solver Integration
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Use pattern matching to identify sequences
|
||||
/// - Verify sequence properties with temporal logic
|
||||
/// - Ensure pattern similarity correlates with verification confidence
|
||||
#[test]
|
||||
fn test_temporal_solver_integration() {
|
||||
println!("\n=== Test 4: Temporal Compare + Neural Solver Integration ===");
|
||||
|
||||
let comparator: TemporalComparator<String> = TemporalComparator::new(100, 1000);
|
||||
let mut solver = TemporalNeuralSolver::new(1000, 500, VerificationStrictness::Medium);
|
||||
|
||||
// Create sequences representing system states
|
||||
let mut seq1: Sequence<String> = Sequence::new();
|
||||
seq1.push("safe".to_string(), 0);
|
||||
seq1.push("safe".to_string(), 100);
|
||||
seq1.push("safe".to_string(), 200);
|
||||
seq1.push("unsafe".to_string(), 300);
|
||||
|
||||
let mut seq2: Sequence<String> = Sequence::new();
|
||||
seq2.push("safe".to_string(), 0);
|
||||
seq2.push("safe".to_string(), 100);
|
||||
seq2.push("safe".to_string(), 200);
|
||||
seq2.push("safe".to_string(), 300);
|
||||
|
||||
// Compare sequences
|
||||
let distance = comparator.compare(&seq1, &seq2, ComparisonAlgorithm::EditDistance).unwrap();
|
||||
println!(" Edit distance: {:.4}", distance.distance);
|
||||
assert_eq!(distance.distance, 1.0, "Should differ by one element");
|
||||
|
||||
// Verify temporal properties
|
||||
for i in 0..4 {
|
||||
let mut state = TemporalState::new(i, i * 100);
|
||||
let is_safe = seq2.elements[i as usize].value == "safe";
|
||||
state.set_proposition("safe", is_safe);
|
||||
solver.add_state(state);
|
||||
}
|
||||
|
||||
// G safe - should be true for seq2
|
||||
let safety_formula = TemporalFormula::globally(TemporalFormula::atom("safe"));
|
||||
let result = solver.verify(&safety_formula).unwrap();
|
||||
|
||||
println!(" ✓ Safety property verified: {}", result.satisfied);
|
||||
println!(" ✓ Confidence: {:.2}", result.confidence);
|
||||
assert!(result.satisfied, "seq2 should maintain safety");
|
||||
|
||||
println!("=== Test 4 PASSED ===\n");
|
||||
}
|
||||
|
||||
/// Test 5: Full System Integration with Strange Loop
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Meta-learning from complete workflow execution
|
||||
/// - Integrate all crates in hierarchical meta-analysis
|
||||
/// - Verify self-referential optimization
|
||||
#[test]
|
||||
fn test_full_system_strange_loop() {
|
||||
println!("\n=== Test 5: Full System Integration with Strange Loop ===");
|
||||
|
||||
let mut strange_loop = StrangeLoop::new(StrangeLoopConfig {
|
||||
max_levels: 5,
|
||||
max_knowledge_per_level: 100,
|
||||
enable_reflection: true,
|
||||
learning_rate: 0.1,
|
||||
});
|
||||
|
||||
let scheduler: RealtimeScheduler<String> = RealtimeScheduler::default();
|
||||
let mut analyzer = AttractorAnalyzer::new(3, 1000);
|
||||
let mut solver = TemporalNeuralSolver::default();
|
||||
|
||||
// Level 0: Base-level workflow
|
||||
println!(" Level 0: Base workflow execution...");
|
||||
let workflow_steps = vec![
|
||||
"schedule".to_string(),
|
||||
"execute".to_string(),
|
||||
"analyze".to_string(),
|
||||
"verify".to_string(),
|
||||
];
|
||||
|
||||
strange_loop.learn_at_level(MetaLevel::base(), &workflow_steps).unwrap();
|
||||
|
||||
// Schedule tasks for each workflow step
|
||||
for (i, step) in workflow_steps.iter().enumerate() {
|
||||
scheduler.schedule(
|
||||
step.clone(),
|
||||
Deadline::from_millis((i as u64 + 1) * 100),
|
||||
Priority::High,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
// Analyze dynamics
|
||||
for i in 0..150 {
|
||||
let point = PhasePoint::new(
|
||||
vec![i as f64, (i as f64).sin(), (i as f64).cos()],
|
||||
i as u64,
|
||||
);
|
||||
analyzer.add_point(point).unwrap();
|
||||
}
|
||||
|
||||
let attractor_info = analyzer.analyze().unwrap();
|
||||
|
||||
// Verify workflow properties
|
||||
for i in 0..workflow_steps.len() {
|
||||
let mut state = TemporalState::new(i as u64, i as u64 * 100);
|
||||
state.set_proposition("scheduled", i >= 0);
|
||||
state.set_proposition("executed", i >= 1);
|
||||
state.set_proposition("analyzed", i >= 2);
|
||||
state.set_proposition("verified", i >= 3);
|
||||
solver.add_state(state);
|
||||
}
|
||||
|
||||
// Level 1: Meta-learning from workflow patterns
|
||||
println!(" Level 1: Meta-learning from patterns...");
|
||||
let meta_patterns = vec![
|
||||
format!("attractor:{:?}", attractor_info.attractor_type),
|
||||
format!("stable:{}", attractor_info.is_stable),
|
||||
];
|
||||
strange_loop.learn_at_level(MetaLevel(1), &meta_patterns).unwrap();
|
||||
|
||||
// Level 2: Analyze behavioral dynamics
|
||||
println!(" Level 2: Behavioral dynamics analysis...");
|
||||
let trajectory_data: Vec<Vec<f64>> = (0..150)
|
||||
.map(|i| vec![i as f64, (i as f64).sin(), (i as f64).cos()])
|
||||
.collect();
|
||||
|
||||
let behavior_type = strange_loop.analyze_behavior(trajectory_data).unwrap();
|
||||
println!(" ✓ Detected behavior: {}", behavior_type);
|
||||
|
||||
// Verify meta-learning effectiveness
|
||||
let summary = strange_loop.get_summary();
|
||||
println!(" ✓ Meta-learning summary:");
|
||||
println!(" - Total levels: {}", summary.total_levels);
|
||||
println!(" - Total knowledge: {}", summary.total_knowledge);
|
||||
println!(" - Learning iterations: {}", summary.learning_iterations);
|
||||
|
||||
assert!(summary.total_levels >= 2);
|
||||
assert!(summary.total_knowledge > 0);
|
||||
assert!(summary.learning_iterations > 0);
|
||||
|
||||
// Verify workflow completion
|
||||
let eventually_verified = TemporalFormula::finally(TemporalFormula::atom("verified"));
|
||||
let result = solver.verify(&eventually_verified).unwrap();
|
||||
assert!(result.satisfied, "Workflow should eventually verify");
|
||||
|
||||
println!(" ✓ Complete system integration verified");
|
||||
println!("=== Test 5 PASSED ===\n");
|
||||
}
|
||||
|
||||
/// Test 6: Error Propagation Across Crates
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Test error handling in each crate
|
||||
/// - Verify errors propagate correctly across boundaries
|
||||
/// - Ensure graceful degradation
|
||||
#[test]
|
||||
fn test_error_propagation() {
|
||||
println!("\n=== Test 6: Error Propagation ===");
|
||||
|
||||
// Test 1: Attractor analyzer dimension mismatch
|
||||
let mut analyzer = AttractorAnalyzer::new(3, 1000);
|
||||
let invalid_point = PhasePoint::new(vec![1.0, 2.0], 100);
|
||||
let result = analyzer.add_point(invalid_point);
|
||||
assert!(result.is_err(), "Should error on dimension mismatch");
|
||||
println!(" ✓ Attractor dimension validation works");
|
||||
|
||||
// Test 2: Attractor analyzer insufficient data
|
||||
let analyzer2 = AttractorAnalyzer::new(2, 1000);
|
||||
let result = analyzer2.analyze();
|
||||
assert!(result.is_err(), "Should error on insufficient data");
|
||||
println!(" ✓ Attractor insufficient data detection works");
|
||||
|
||||
// Test 3: Temporal solver empty trace
|
||||
let solver = TemporalNeuralSolver::default();
|
||||
let formula = TemporalFormula::atom("test");
|
||||
let result = solver.verify(&formula);
|
||||
assert!(result.is_err(), "Should error on empty trace");
|
||||
println!(" ✓ Temporal solver empty trace detection works");
|
||||
|
||||
// Test 4: Scheduler queue full
|
||||
let scheduler: RealtimeScheduler<String> = RealtimeScheduler::new(SchedulerConfig {
|
||||
max_queue_size: 5,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
for i in 0..10 {
|
||||
let result = scheduler.schedule(
|
||||
format!("task_{}", i),
|
||||
Deadline::from_millis(100),
|
||||
Priority::Medium,
|
||||
);
|
||||
if i >= 5 {
|
||||
assert!(result.is_err(), "Should error when queue is full");
|
||||
}
|
||||
}
|
||||
println!(" ✓ Scheduler queue overflow detection works");
|
||||
|
||||
// Test 5: Strange loop max depth
|
||||
let mut strange_loop = StrangeLoop::default();
|
||||
let deep_level = MetaLevel(10);
|
||||
let data = vec!["test".to_string()];
|
||||
let result = strange_loop.learn_at_level(deep_level, &data);
|
||||
assert!(result.is_err(), "Should error on max depth exceeded");
|
||||
println!(" ✓ Strange loop depth limit enforcement works");
|
||||
|
||||
// Test 6: Temporal comparator sequence too long
|
||||
let comparator: TemporalComparator<i32> = TemporalComparator::new(100, 100);
|
||||
let mut long_seq: Sequence<i32> = Sequence::new();
|
||||
for i in 0..200 {
|
||||
long_seq.push(i, i as u64);
|
||||
}
|
||||
let mut short_seq: Sequence<i32> = Sequence::new();
|
||||
short_seq.push(1, 0);
|
||||
|
||||
let result = comparator.compare(&long_seq, &short_seq, ComparisonAlgorithm::DTW);
|
||||
assert!(result.is_err(), "Should error on sequence too long");
|
||||
println!(" ✓ Temporal comparator length validation works");
|
||||
|
||||
println!("=== Test 6 PASSED ===\n");
|
||||
}
|
||||
|
||||
/// Test 7: Performance and Scalability
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Test throughput under load
|
||||
/// - Verify latency requirements (<1ms for scheduler)
|
||||
/// - Ensure cache effectiveness
|
||||
#[test]
|
||||
fn test_performance_scalability() {
|
||||
println!("\n=== Test 7: Performance and Scalability ===");
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
// Test 1: Scheduler throughput
|
||||
let start = Instant::now();
|
||||
let scheduler: RealtimeScheduler<u64> = RealtimeScheduler::default();
|
||||
|
||||
for i in 0..1000 {
|
||||
scheduler.schedule(
|
||||
i,
|
||||
Deadline::from_millis(100),
|
||||
Priority::Medium,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
let duration = start.elapsed();
|
||||
println!(" ✓ Scheduled 1000 tasks in {:?}", duration);
|
||||
println!(" ✓ Average latency: {:?} per task", duration / 1000);
|
||||
assert!(duration.as_millis() < 100, "Should schedule fast");
|
||||
|
||||
// Test 2: Temporal comparison with caching
|
||||
let start = Instant::now();
|
||||
let comparator: TemporalComparator<i32> = TemporalComparator::new(1000, 10000);
|
||||
|
||||
let mut seq1: Sequence<i32> = Sequence::new();
|
||||
let mut seq2: Sequence<i32> = Sequence::new();
|
||||
for i in 0..100 {
|
||||
seq1.push(i, i as u64);
|
||||
seq2.push(i, i as u64);
|
||||
}
|
||||
|
||||
// First comparison - cache miss
|
||||
let _result1 = comparator.compare(&seq1, &seq2, ComparisonAlgorithm::DTW).unwrap();
|
||||
|
||||
// Second comparison - cache hit
|
||||
let _result2 = comparator.compare(&seq1, &seq2, ComparisonAlgorithm::DTW).unwrap();
|
||||
|
||||
let duration = start.elapsed();
|
||||
println!(" ✓ Compared 100-element sequences (2x) in {:?}", duration);
|
||||
|
||||
let stats = comparator.cache_stats();
|
||||
println!(" ✓ Cache hits: {}, misses: {}", stats.hits, stats.misses);
|
||||
println!(" ✓ Cache hit rate: {:.2}%", stats.hit_rate() * 100.0);
|
||||
assert!(stats.hits >= 1, "Should have at least one cache hit");
|
||||
|
||||
// Test 3: Attractor analysis performance
|
||||
let start = Instant::now();
|
||||
let mut analyzer = AttractorAnalyzer::new(3, 10000);
|
||||
|
||||
for i in 0..1000 {
|
||||
let point = PhasePoint::new(
|
||||
vec![(i as f64).sin(), (i as f64).cos(), i as f64 * 0.01],
|
||||
i,
|
||||
);
|
||||
analyzer.add_point(point).unwrap();
|
||||
}
|
||||
|
||||
let duration = start.elapsed();
|
||||
println!(" ✓ Added 1000 phase points in {:?}", duration);
|
||||
|
||||
let start = Instant::now();
|
||||
let _info = analyzer.analyze().unwrap();
|
||||
let analysis_duration = start.elapsed();
|
||||
println!(" ✓ Analyzed trajectory in {:?}", analysis_duration);
|
||||
|
||||
println!("=== Test 7 PASSED ===\n");
|
||||
}
|
||||
|
||||
/// Test 8: Pattern Detection Pipeline
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Detect patterns using temporal compare
|
||||
/// - Analyze pattern stability with attractors
|
||||
/// - Verify pattern properties with solver
|
||||
#[test]
|
||||
fn test_pattern_detection_pipeline() {
|
||||
println!("\n=== Test 8: Pattern Detection Pipeline ===");
|
||||
|
||||
let comparator: TemporalComparator<f64> = TemporalComparator::new(100, 1000);
|
||||
|
||||
// Time series with repeating pattern
|
||||
let series = vec![1.0, 2.0, 3.0, 2.0, 1.0, 1.0, 2.0, 3.0, 2.0, 1.0, 5.0, 6.0];
|
||||
let pattern = vec![1.0, 2.0, 3.0, 2.0, 1.0];
|
||||
|
||||
// Find similar patterns
|
||||
let matches = comparator.find_similar(&series, &pattern, 1.0);
|
||||
println!(" Found {} pattern matches", matches.len());
|
||||
|
||||
for (idx, dist) in &matches {
|
||||
println!(" Match at index {} with distance {:.4}", idx, dist);
|
||||
}
|
||||
|
||||
assert!(matches.len() >= 2, "Should find repeated pattern");
|
||||
assert_eq!(matches[0].0, 0, "First match at index 0");
|
||||
assert_eq!(matches[1].0, 5, "Second match at index 5");
|
||||
|
||||
// Test pattern detection
|
||||
let detected = comparator.detect_pattern(&series, &pattern, 1.0);
|
||||
assert!(detected, "Pattern should be detected");
|
||||
|
||||
let no_match = comparator.detect_pattern(&series, &vec![10.0, 20.0, 30.0], 1.0);
|
||||
assert!(!no_match, "Non-existent pattern should not be detected");
|
||||
|
||||
println!(" ✓ Pattern detection pipeline verified");
|
||||
println!("=== Test 8 PASSED ===\n");
|
||||
}
|
||||
|
||||
/// Test 9: State Management and Recovery
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Test state persistence and recovery
|
||||
/// - Verify clear/reset operations
|
||||
/// - Ensure no memory leaks
|
||||
#[test]
|
||||
fn test_state_management() {
|
||||
println!("\n=== Test 9: State Management and Recovery ===");
|
||||
|
||||
// Test 1: Attractor analyzer clear
|
||||
let mut analyzer = AttractorAnalyzer::new(2, 1000);
|
||||
|
||||
for i in 0..50 {
|
||||
analyzer.add_point(PhasePoint::new(vec![i as f64, i as f64], i)).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(analyzer.trajectory_length(), 50);
|
||||
analyzer.clear();
|
||||
assert_eq!(analyzer.trajectory_length(), 0);
|
||||
println!(" ✓ Attractor analyzer clear works");
|
||||
|
||||
// Test 2: Temporal solver trace clear
|
||||
let mut solver = TemporalNeuralSolver::default();
|
||||
|
||||
for i in 0..20 {
|
||||
let mut state = TemporalState::new(i, i * 100);
|
||||
state.set_proposition("test", true);
|
||||
solver.add_state(state);
|
||||
}
|
||||
|
||||
assert_eq!(solver.trace_length(), 20);
|
||||
solver.clear_trace();
|
||||
assert_eq!(solver.trace_length(), 0);
|
||||
println!(" ✓ Temporal solver clear works");
|
||||
|
||||
// Test 3: Strange loop reset
|
||||
let mut strange_loop = StrangeLoop::default();
|
||||
|
||||
strange_loop.learn_at_level(MetaLevel::base(), &vec!["a".to_string()]).unwrap();
|
||||
let before = strange_loop.get_summary();
|
||||
assert!(before.total_knowledge > 0);
|
||||
|
||||
strange_loop.reset();
|
||||
let after = strange_loop.get_summary();
|
||||
assert_eq!(after.total_knowledge, 0);
|
||||
println!(" ✓ Strange loop reset works");
|
||||
|
||||
// Test 4: Scheduler clear
|
||||
let scheduler: RealtimeScheduler<String> = RealtimeScheduler::default();
|
||||
|
||||
for i in 0..10 {
|
||||
scheduler.schedule(
|
||||
format!("task_{}", i),
|
||||
Deadline::from_millis(100),
|
||||
Priority::Medium,
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(scheduler.queue_size(), 10);
|
||||
scheduler.clear();
|
||||
assert_eq!(scheduler.queue_size(), 0);
|
||||
println!(" ✓ Scheduler clear works");
|
||||
|
||||
// Test 5: Temporal comparator cache clear
|
||||
let comparator: TemporalComparator<i32> = TemporalComparator::new(100, 1000);
|
||||
|
||||
let mut seq1: Sequence<i32> = Sequence::new();
|
||||
let mut seq2: Sequence<i32> = Sequence::new();
|
||||
seq1.push(1, 0);
|
||||
seq2.push(1, 0);
|
||||
|
||||
comparator.compare(&seq1, &seq2, ComparisonAlgorithm::DTW).unwrap();
|
||||
let stats_before = comparator.cache_stats();
|
||||
assert!(stats_before.size > 0 || stats_before.misses > 0);
|
||||
|
||||
comparator.clear_cache();
|
||||
let stats_after = comparator.cache_stats();
|
||||
assert_eq!(stats_after.size, 0);
|
||||
println!(" ✓ Temporal comparator cache clear works");
|
||||
|
||||
println!("=== Test 9 PASSED ===\n");
|
||||
}
|
||||
|
||||
/// Test 10: Deadline and Priority Handling
|
||||
///
|
||||
/// Scenario:
|
||||
/// - Schedule tasks with various deadlines
|
||||
/// - Verify priority-based execution order
|
||||
/// - Test deadline miss detection
|
||||
#[test]
|
||||
fn test_deadline_priority_handling() {
|
||||
println!("\n=== Test 10: Deadline and Priority Handling ===");
|
||||
|
||||
let scheduler: RealtimeScheduler<String> = RealtimeScheduler::default();
|
||||
scheduler.start();
|
||||
|
||||
// Schedule tasks with different priorities
|
||||
let low_id = scheduler.schedule(
|
||||
"low_priority".to_string(),
|
||||
Deadline::from_millis(100),
|
||||
Priority::Low,
|
||||
).unwrap();
|
||||
|
||||
let high_id = scheduler.schedule(
|
||||
"high_priority".to_string(),
|
||||
Deadline::from_millis(100),
|
||||
Priority::High,
|
||||
).unwrap();
|
||||
|
||||
let critical_id = scheduler.schedule(
|
||||
"critical_priority".to_string(),
|
||||
Deadline::from_millis(100),
|
||||
Priority::Critical,
|
||||
).unwrap();
|
||||
|
||||
// Verify priority ordering
|
||||
let task1 = scheduler.next_task().unwrap();
|
||||
assert_eq!(task1.id, critical_id, "Critical priority should execute first");
|
||||
assert_eq!(task1.priority, Priority::Critical);
|
||||
|
||||
let task2 = scheduler.next_task().unwrap();
|
||||
assert_eq!(task2.id, high_id, "High priority should execute second");
|
||||
|
||||
let task3 = scheduler.next_task().unwrap();
|
||||
assert_eq!(task3.id, low_id, "Low priority should execute last");
|
||||
|
||||
println!(" ✓ Priority-based execution order verified");
|
||||
|
||||
// Test deadline miss detection
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
let past_deadline = Deadline::from_micros(1);
|
||||
|
||||
scheduler.schedule(
|
||||
"late_task".to_string(),
|
||||
past_deadline,
|
||||
Priority::High,
|
||||
).unwrap();
|
||||
|
||||
let late_task = scheduler.next_task().unwrap();
|
||||
scheduler.execute_task(late_task, |_payload| {
|
||||
// Task execution
|
||||
});
|
||||
|
||||
let stats = scheduler.stats();
|
||||
println!(" ✓ Completed tasks: {}", stats.completed_tasks);
|
||||
println!(" ✓ Average latency: {} ns", stats.average_latency_ns);
|
||||
|
||||
scheduler.stop();
|
||||
assert!(!scheduler.is_running());
|
||||
println!(" ✓ Scheduler lifecycle management works");
|
||||
|
||||
println!("=== Test 10 PASSED ===\n");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod summary {
|
||||
#[test]
|
||||
fn print_test_summary() {
|
||||
println!("\n");
|
||||
println!("╔═══════════════════════════════════════════════════════════════╗");
|
||||
println!("║ MidStream Integration Test Suite ║");
|
||||
println!("╠═══════════════════════════════════════════════════════════════╣");
|
||||
println!("║ ║");
|
||||
println!("║ ✓ Test 1: Scheduler + Temporal Compare ║");
|
||||
println!("║ ✓ Test 2: Scheduler + Attractor Analysis ║");
|
||||
println!("║ ✓ Test 3: Attractor + Neural Solver ║");
|
||||
println!("║ ✓ Test 4: Temporal Compare + Neural Solver ║");
|
||||
println!("║ ✓ Test 5: Full System with Strange Loop ║");
|
||||
println!("║ ✓ Test 6: Error Propagation ║");
|
||||
println!("║ ✓ Test 7: Performance and Scalability ║");
|
||||
println!("║ ✓ Test 8: Pattern Detection Pipeline ║");
|
||||
println!("║ ✓ Test 9: State Management and Recovery ║");
|
||||
println!("║ ✓ Test 10: Deadline and Priority Handling ║");
|
||||
println!("║ ║");
|
||||
println!("║ Coverage: ║");
|
||||
println!("║ - Cross-crate integration: ✓ ║");
|
||||
println!("║ - Real-world scenarios: ✓ ║");
|
||||
println!("║ - Error handling: ✓ ║");
|
||||
println!("║ - Performance validation: ✓ ║");
|
||||
println!("║ - State management: ✓ ║");
|
||||
println!("║ ║");
|
||||
println!("╚═══════════════════════════════════════════════════════════════╝");
|
||||
println!("\n");
|
||||
}
|
||||
}
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
//! Comprehensive simulation tests for various real-world scenarios
|
||||
|
||||
use midstream::{
|
||||
LeanAgenticSystem, LeanAgenticConfig, AgentContext,
|
||||
KnowledgeGraph, Entity, EntityType, Relation,
|
||||
};
|
||||
use std::time::Instant;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_weather_intent_simulation() {
|
||||
let config = LeanAgenticConfig::default();
|
||||
let system = LeanAgenticSystem::new(config);
|
||||
let mut context = AgentContext::new("weather_session".to_string());
|
||||
|
||||
let messages = vec![
|
||||
"What's the weather like today?",
|
||||
"How about tomorrow?",
|
||||
"Will it rain this weekend?",
|
||||
"Should I bring an umbrella?",
|
||||
];
|
||||
|
||||
for (i, msg) in messages.iter().enumerate() {
|
||||
let result = system.process_stream_chunk(msg, context.clone()).await;
|
||||
assert!(result.is_ok(), "Message {} failed: {:?}", i, result);
|
||||
|
||||
let res = result.unwrap();
|
||||
println!("Message: {}", msg);
|
||||
println!(" Action: {}", res.action.description);
|
||||
println!(" Reward: {:.3}", res.reward);
|
||||
println!(" Verified: {}", res.verified);
|
||||
|
||||
context.add_message(msg.to_string());
|
||||
}
|
||||
|
||||
// Verify learning occurred
|
||||
let stats = system.get_stats().await;
|
||||
assert!(stats.total_actions >= messages.len() as u64);
|
||||
println!("\nFinal stats: {:?}", stats);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_knowledge_accumulation_simulation() {
|
||||
let config = LeanAgenticConfig::default();
|
||||
let system = LeanAgenticSystem::new(config);
|
||||
let mut context = AgentContext::new("learning_session".to_string());
|
||||
|
||||
let learning_sequence = vec![
|
||||
"My name is Alice and I work at Google",
|
||||
"I live in San Francisco",
|
||||
"I prefer detailed weather forecasts",
|
||||
"My favorite color is blue",
|
||||
"I usually wake up at 7 AM",
|
||||
];
|
||||
|
||||
for msg in &learning_sequence {
|
||||
let result = system.process_stream_chunk(msg, context.clone()).await.unwrap();
|
||||
context.add_message(msg.to_string());
|
||||
context.set_preference("detail_level".to_string(), 0.9);
|
||||
}
|
||||
|
||||
let stats = system.get_stats().await;
|
||||
|
||||
// Verify knowledge was accumulated
|
||||
assert!(stats.total_entities > 0, "No entities extracted");
|
||||
assert!(stats.learning_iterations > 0, "No learning occurred");
|
||||
|
||||
println!("Knowledge accumulation results:");
|
||||
println!(" Entities: {}", stats.total_entities);
|
||||
println!(" Learning iterations: {}", stats.learning_iterations);
|
||||
println!(" Average reward: {:.3}", stats.average_reward);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_high_frequency_streaming_simulation() {
|
||||
let config = LeanAgenticConfig {
|
||||
enable_formal_verification: false, // Disable for speed
|
||||
learning_rate: 0.05,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let system = LeanAgenticSystem::new(config);
|
||||
let context = AgentContext::new("streaming_session".to_string());
|
||||
|
||||
let start = Instant::now();
|
||||
let num_chunks = 1000;
|
||||
|
||||
for i in 0..num_chunks {
|
||||
let chunk = format!("Stream chunk {}", i);
|
||||
let result = system.process_stream_chunk(&chunk, context.clone()).await;
|
||||
assert!(result.is_ok(), "Chunk {} failed", i);
|
||||
}
|
||||
|
||||
let duration = start.elapsed();
|
||||
let chunks_per_sec = num_chunks as f64 / duration.as_secs_f64();
|
||||
|
||||
println!("\nHigh-frequency streaming results:");
|
||||
println!(" Total chunks: {}", num_chunks);
|
||||
println!(" Duration: {:?}", duration);
|
||||
println!(" Throughput: {:.2} chunks/sec", chunks_per_sec);
|
||||
println!(" Avg latency: {:.2} ms/chunk", duration.as_millis() as f64 / num_chunks as f64);
|
||||
|
||||
// Verify minimum throughput
|
||||
assert!(chunks_per_sec > 50.0, "Throughput too low: {:.2} chunks/sec", chunks_per_sec);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_concurrent_sessions_simulation() {
|
||||
let config = LeanAgenticConfig::default();
|
||||
let system = LeanAgenticSystem::new(config);
|
||||
|
||||
let num_sessions = 100;
|
||||
let mut handles = vec![];
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
for i in 0..num_sessions {
|
||||
let sys = &system;
|
||||
let handle = tokio::spawn(async move {
|
||||
let context = AgentContext::new(format!("session_{}", i));
|
||||
let messages = vec![
|
||||
"Hello",
|
||||
"What's the weather?",
|
||||
"Thank you",
|
||||
];
|
||||
|
||||
for msg in messages {
|
||||
sys.process_stream_chunk(msg, context.clone()).await.unwrap();
|
||||
}
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
|
||||
let duration = start.elapsed();
|
||||
|
||||
println!("\nConcurrent sessions results:");
|
||||
println!(" Sessions: {}", num_sessions);
|
||||
println!(" Duration: {:?}", duration);
|
||||
println!(" Avg per session: {:.2} ms", duration.as_millis() as f64 / num_sessions as f64);
|
||||
|
||||
let stats = system.get_stats().await;
|
||||
println!(" Total actions: {}", stats.total_actions);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_learning_convergence_simulation() {
|
||||
let config = LeanAgenticConfig {
|
||||
learning_rate: 0.1, // Higher learning rate for faster convergence
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let system = LeanAgenticSystem::new(config);
|
||||
let mut context = AgentContext::new("convergence_session".to_string());
|
||||
|
||||
// Repeat the same pattern to test learning convergence
|
||||
let pattern = "What is the weather in Tokyo?";
|
||||
let mut rewards = vec![];
|
||||
|
||||
for iteration in 0..100 {
|
||||
let result = system.process_stream_chunk(pattern, context.clone()).await.unwrap();
|
||||
rewards.push(result.reward);
|
||||
|
||||
if iteration % 10 == 0 {
|
||||
println!("Iteration {}: reward = {:.3}", iteration, result.reward);
|
||||
}
|
||||
|
||||
context.add_message(pattern.to_string());
|
||||
}
|
||||
|
||||
// Check if rewards are improving (basic convergence check)
|
||||
let early_avg: f64 = rewards[0..20].iter().sum::<f64>() / 20.0;
|
||||
let late_avg: f64 = rewards[80..100].iter().sum::<f64>() / 20.0;
|
||||
|
||||
println!("\nLearning convergence results:");
|
||||
println!(" Early average reward (0-20): {:.3}", early_avg);
|
||||
println!(" Late average reward (80-100): {:.3}", late_avg);
|
||||
println!(" Improvement: {:.3}", late_avg - early_avg);
|
||||
|
||||
// Rewards should stabilize or improve
|
||||
assert!(late_avg >= early_avg * 0.8, "Learning degraded significantly");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_knowledge_graph_scaling() {
|
||||
let mut kg = KnowledgeGraph::new();
|
||||
|
||||
let start = Instant::now();
|
||||
let num_entities = 10000;
|
||||
|
||||
// Add many entities
|
||||
for i in 0..num_entities {
|
||||
let entity = Entity {
|
||||
id: format!("entity_{}", i),
|
||||
name: format!("Entity {}", i),
|
||||
entity_type: if i % 3 == 0 {
|
||||
EntityType::Person
|
||||
} else if i % 3 == 1 {
|
||||
EntityType::Organization
|
||||
} else {
|
||||
EntityType::Concept
|
||||
},
|
||||
attributes: std::collections::HashMap::new(),
|
||||
confidence: 0.9,
|
||||
};
|
||||
|
||||
kg.update(vec![entity]).await.unwrap();
|
||||
}
|
||||
|
||||
let insert_duration = start.elapsed();
|
||||
|
||||
// Add relations
|
||||
let relation_start = Instant::now();
|
||||
for i in 0..1000 {
|
||||
kg.add_relation(Relation {
|
||||
id: format!("rel_{}", i),
|
||||
subject: format!("entity_{}", i * 10),
|
||||
predicate: "relates_to".to_string(),
|
||||
object: format!("entity_{}", i * 10 + 1),
|
||||
confidence: 0.85,
|
||||
source: "test".to_string(),
|
||||
});
|
||||
}
|
||||
let relation_duration = relation_start.elapsed();
|
||||
|
||||
// Query performance
|
||||
let query_start = Instant::now();
|
||||
let results = kg.query_entities(EntityType::Person);
|
||||
let query_duration = query_start.elapsed();
|
||||
|
||||
println!("\nKnowledge graph scaling results:");
|
||||
println!(" Entities inserted: {}", num_entities);
|
||||
println!(" Insert time: {:?}", insert_duration);
|
||||
println!(" Insert rate: {:.2} entities/sec", num_entities as f64 / insert_duration.as_secs_f64());
|
||||
println!(" Relations added: 1000");
|
||||
println!(" Relation time: {:?}", relation_duration);
|
||||
println!(" Query time: {:?}", query_duration);
|
||||
println!(" Results found: {}", results.len());
|
||||
|
||||
assert_eq!(kg.entity_count(), num_entities);
|
||||
assert_eq!(kg.relation_count(), 1000);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_adaptive_behavior_simulation() {
|
||||
let config = LeanAgenticConfig {
|
||||
learning_rate: 0.05,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let system = LeanAgenticSystem::new(config);
|
||||
let mut context = AgentContext::new("adaptive_session".to_string());
|
||||
|
||||
// Phase 1: Weather queries
|
||||
println!("\nPhase 1: Weather queries");
|
||||
for i in 0..10 {
|
||||
let msg = format!("What's the weather in city {}?", i);
|
||||
let result = system.process_stream_chunk(&msg, context.clone()).await.unwrap();
|
||||
context.add_message(msg);
|
||||
if i == 0 || i == 9 {
|
||||
println!(" Iteration {}: reward = {:.3}", i, result.reward);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Switch to learning/memory queries
|
||||
println!("\nPhase 2: Learning queries");
|
||||
for i in 0..10 {
|
||||
let msg = format!("Remember that I like {}", i);
|
||||
let result = system.process_stream_chunk(&msg, context.clone()).await.unwrap();
|
||||
context.add_message(msg);
|
||||
if i == 0 || i == 9 {
|
||||
println!(" Iteration {}: reward = {:.3}", i, result.reward);
|
||||
}
|
||||
}
|
||||
|
||||
let stats = system.get_stats().await;
|
||||
println!("\nAdaptive behavior stats:");
|
||||
println!(" Total actions: {}", stats.total_actions);
|
||||
println!(" Average reward: {:.3}", stats.average_reward);
|
||||
println!(" Entities learned: {}", stats.total_entities);
|
||||
|
||||
assert!(stats.total_actions >= 20);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_memory_efficiency() {
|
||||
use std::mem::size_of;
|
||||
|
||||
println!("\nMemory efficiency analysis:");
|
||||
println!(" AgentContext: {} bytes", size_of::<AgentContext>());
|
||||
println!(" Entity: {} bytes", size_of::<Entity>());
|
||||
println!(" Relation: {} bytes", size_of::<Relation>());
|
||||
|
||||
// Test memory growth with many sessions
|
||||
let config = LeanAgenticConfig::default();
|
||||
let system = LeanAgenticSystem::new(config);
|
||||
|
||||
for i in 0..100 {
|
||||
let context = AgentContext::new(format!("session_{}", i));
|
||||
system.process_stream_chunk("test", context).await.unwrap();
|
||||
}
|
||||
|
||||
let stats = system.get_stats().await;
|
||||
println!(" Sessions processed: 100");
|
||||
println!(" Total entities: {}", stats.total_entities);
|
||||
println!(" Estimated memory per session: ~{} KB",
|
||||
(stats.total_entities * size_of::<Entity>()) / 100 / 1024);
|
||||
}
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
/// Integration tests for temporal-compare pattern detection APIs
|
||||
///
|
||||
/// This test suite verifies that the find_similar() and detect_pattern() APIs
|
||||
/// work correctly with the published crate.
|
||||
|
||||
use midstreamer_temporal_compare::{TemporalComparator, Pattern, SimilarityMatch};
|
||||
|
||||
#[test]
|
||||
fn test_find_similar_with_f64() {
|
||||
let comparator: TemporalComparator<f64> = TemporalComparator::new(100, 1000);
|
||||
|
||||
// Create a time series with repeating patterns
|
||||
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0, 3.0, 4.0, 5.0, 6.0, 7.0];
|
||||
let pattern = vec![3.0, 4.0, 5.0];
|
||||
|
||||
// Find similar patterns with a reasonable threshold
|
||||
let matches = comparator.find_similar(&series, &pattern, 1.0);
|
||||
|
||||
// Verify we found the expected matches
|
||||
assert!(!matches.is_empty(), "Should find at least one match");
|
||||
assert_eq!(matches.len(), 2, "Should find exactly 2 matches");
|
||||
|
||||
// Verify the indices are correct
|
||||
assert_eq!(matches[0].0, 2, "First match should be at index 2");
|
||||
assert_eq!(matches[1].0, 5, "Second match should be at index 5");
|
||||
|
||||
// Verify distances are within threshold
|
||||
for (idx, distance) in &matches {
|
||||
assert!(*distance <= 1.0, "Distance {} at index {} exceeds threshold", distance, idx);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detect_pattern_exists() {
|
||||
let comparator: TemporalComparator<f64> = TemporalComparator::new(100, 1000);
|
||||
|
||||
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0, 3.0, 4.0, 5.0];
|
||||
let pattern = vec![3.0, 4.0, 5.0];
|
||||
|
||||
// Detect if pattern exists
|
||||
let found = comparator.detect_pattern(&series, &pattern, 0.5);
|
||||
|
||||
assert!(found, "Pattern should be detected in the series");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detect_pattern_not_exists() {
|
||||
let comparator: TemporalComparator<f64> = TemporalComparator::new(100, 1000);
|
||||
|
||||
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0];
|
||||
let pattern = vec![10.0, 20.0, 30.0];
|
||||
|
||||
// Detect if pattern exists with strict threshold
|
||||
let found = comparator.detect_pattern(&series, &pattern, 0.5);
|
||||
|
||||
assert!(!found, "Pattern should not be detected (too different)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_similar_generic_with_integers() {
|
||||
let comparator: TemporalComparator<i32> = TemporalComparator::new(100, 1000);
|
||||
|
||||
let haystack = vec![1, 2, 3, 4, 5, 3, 4, 5, 6];
|
||||
let needle = vec![3, 4, 5];
|
||||
|
||||
// Use the generic API with normalized threshold
|
||||
let matches = comparator.find_similar_generic(&haystack, &needle, 0.1).unwrap();
|
||||
|
||||
assert_eq!(matches.len(), 2, "Should find 2 exact matches");
|
||||
assert_eq!(matches[0].start_index, 2);
|
||||
assert_eq!(matches[1].start_index, 5);
|
||||
|
||||
// Verify similarity scores
|
||||
for m in &matches {
|
||||
assert!(m.similarity > 0.9, "Exact matches should have high similarity");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detect_recurring_patterns() {
|
||||
let comparator: TemporalComparator<char> = TemporalComparator::new(100, 1000);
|
||||
|
||||
// Create sequence with recurring patterns
|
||||
let sequence = vec!['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'];
|
||||
|
||||
// Detect patterns of length 3
|
||||
let patterns = comparator.detect_recurring_patterns(&sequence, 3, 3).unwrap();
|
||||
|
||||
assert!(!patterns.is_empty(), "Should detect recurring patterns");
|
||||
|
||||
// Find the 'abc' pattern
|
||||
let abc_pattern = patterns.iter()
|
||||
.find(|p| p.sequence == vec!['a', 'b', 'c']);
|
||||
|
||||
assert!(abc_pattern.is_some(), "Should find 'abc' pattern");
|
||||
|
||||
let pattern = abc_pattern.unwrap();
|
||||
assert_eq!(pattern.frequency(), 3, "Pattern should occur 3 times");
|
||||
assert!(pattern.confidence > 0.0, "Should have positive confidence");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detect_fuzzy_patterns() {
|
||||
let comparator: TemporalComparator<i32> = TemporalComparator::new(100, 1000);
|
||||
|
||||
// Sequence with similar but not identical patterns
|
||||
let sequence = vec![1, 2, 3, 1, 2, 4, 1, 2, 3];
|
||||
|
||||
// Detect fuzzy patterns (should group [1,2,3] and [1,2,4] together)
|
||||
let patterns = comparator.detect_fuzzy_patterns(&sequence, 3, 3, 0.7).unwrap();
|
||||
|
||||
assert!(!patterns.is_empty(), "Should detect fuzzy patterns");
|
||||
|
||||
// Should find at least one pattern that occurs multiple times
|
||||
let has_multiple = patterns.iter().any(|p| p.frequency() >= 2);
|
||||
assert!(has_multiple, "Should find patterns with multiple occurrences");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pattern_struct_api() {
|
||||
let sequence = vec![1, 2, 3];
|
||||
let occurrences = vec![0, 5, 10];
|
||||
let confidence = 0.85;
|
||||
|
||||
let pattern = Pattern::new(sequence.clone(), occurrences.clone(), confidence);
|
||||
|
||||
// Verify Pattern API
|
||||
assert_eq!(pattern.sequence, sequence);
|
||||
assert_eq!(pattern.occurrences, occurrences);
|
||||
assert_eq!(pattern.confidence, confidence);
|
||||
assert_eq!(pattern.frequency(), 3);
|
||||
assert_eq!(pattern.length(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_similarity_match_struct() {
|
||||
let match1 = SimilarityMatch::new(0, 0.5);
|
||||
|
||||
assert_eq!(match1.start_index, 0);
|
||||
assert_eq!(match1.distance, 0.5);
|
||||
assert!(match1.similarity > 0.0 && match1.similarity <= 1.0);
|
||||
|
||||
// Lower distance should give higher similarity
|
||||
let match2 = SimilarityMatch::new(0, 0.1);
|
||||
assert!(match2.similarity > match1.similarity,
|
||||
"Lower distance should yield higher similarity");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edge_case_empty_pattern() {
|
||||
let comparator: TemporalComparator<f64> = TemporalComparator::new(100, 1000);
|
||||
|
||||
let series = vec![1.0, 2.0, 3.0];
|
||||
let pattern: Vec<f64> = vec![];
|
||||
|
||||
let matches = comparator.find_similar(&series, &pattern, 1.0);
|
||||
assert!(matches.is_empty(), "Empty pattern should return no matches");
|
||||
|
||||
let found = comparator.detect_pattern(&series, &pattern, 1.0);
|
||||
assert!(!found, "Empty pattern should not be detected");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edge_case_pattern_longer_than_series() {
|
||||
let comparator: TemporalComparator<f64> = TemporalComparator::new(100, 1000);
|
||||
|
||||
let series = vec![1.0, 2.0];
|
||||
let pattern = vec![1.0, 2.0, 3.0, 4.0, 5.0];
|
||||
|
||||
let matches = comparator.find_similar(&series, &pattern, 1.0);
|
||||
assert!(matches.is_empty(), "Pattern longer than series should return no matches");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_approximate_matching_with_threshold() {
|
||||
let comparator: TemporalComparator<f64> = TemporalComparator::new(100, 1000);
|
||||
|
||||
// Series with approximate match
|
||||
let series = vec![1.0, 2.0, 3.1, 4.2, 5.0, 6.0];
|
||||
let pattern = vec![3.0, 4.0, 5.0];
|
||||
|
||||
// Strict threshold - should not match
|
||||
let strict_matches = comparator.find_similar(&series, &pattern, 0.1);
|
||||
assert!(strict_matches.is_empty(), "Strict threshold should reject approximate match");
|
||||
|
||||
// Loose threshold - should match
|
||||
let loose_matches = comparator.find_similar(&series, &pattern, 1.5);
|
||||
assert!(!loose_matches.is_empty(), "Loose threshold should accept approximate match");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_results_sorted_by_quality() {
|
||||
let comparator: TemporalComparator<f64> = TemporalComparator::new(100, 1000);
|
||||
|
||||
// Series with exact and approximate matches
|
||||
let series = vec![1.0, 2.0, 3.0, 4.0, 5.0, 3.5, 4.5, 5.5];
|
||||
let pattern = vec![3.0, 4.0, 5.0];
|
||||
|
||||
let matches = comparator.find_similar(&series, &pattern, 2.0);
|
||||
|
||||
assert!(!matches.is_empty(), "Should find matches");
|
||||
|
||||
// Verify results are sorted by distance (best first)
|
||||
for i in 0..matches.len().saturating_sub(1) {
|
||||
assert!(matches[i].1 <= matches[i + 1].1,
|
||||
"Results should be sorted by distance (ascending)");
|
||||
}
|
||||
|
||||
// First match should be the exact one
|
||||
assert!(matches[0].1 < 0.1, "Best match should have very low distance");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_caching_behavior() {
|
||||
let comparator: TemporalComparator<i32> = TemporalComparator::new(100, 1000);
|
||||
|
||||
let haystack = vec![1, 2, 3, 4, 5];
|
||||
let needle = vec![3, 4, 5];
|
||||
|
||||
// Clear cache to start fresh
|
||||
comparator.clear_cache();
|
||||
|
||||
// First call - should be cache miss
|
||||
let _ = comparator.find_similar_generic(&haystack, &needle, 0.1).unwrap();
|
||||
|
||||
// Second call - should be cache hit
|
||||
let _ = comparator.find_similar_generic(&haystack, &needle, 0.1).unwrap();
|
||||
|
||||
let stats = comparator.cache_stats();
|
||||
assert!(stats.hits > 0, "Should have cache hits");
|
||||
assert!(stats.hits + stats.misses > 0, "Should have cache activity");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_comprehensive_workflow() {
|
||||
let comparator: TemporalComparator<i32> = TemporalComparator::new(100, 1000);
|
||||
|
||||
// Create a rich sequence
|
||||
let sequence = vec![
|
||||
1, 2, 3, 4, // Pattern A
|
||||
1, 2, 3, 4, // Pattern A repeat
|
||||
5, 6, 7, // Pattern B
|
||||
5, 6, 7, // Pattern B repeat
|
||||
1, 2, 3, 4, // Pattern A again
|
||||
];
|
||||
|
||||
// Test 1: Exact pattern detection
|
||||
let exact_patterns = comparator.detect_recurring_patterns(&sequence, 3, 4).unwrap();
|
||||
assert!(!exact_patterns.is_empty(), "Should detect exact patterns");
|
||||
|
||||
// Test 2: Fuzzy pattern detection
|
||||
let fuzzy_patterns = comparator.detect_fuzzy_patterns(&sequence, 3, 4, 0.8).unwrap();
|
||||
assert!(!fuzzy_patterns.is_empty(), "Should detect fuzzy patterns");
|
||||
|
||||
// Test 3: Similarity search
|
||||
let needle = vec![1, 2, 3, 4];
|
||||
let matches = comparator.find_similar_generic(&sequence, &needle, 0.1).unwrap();
|
||||
assert_eq!(matches.len(), 3, "Should find 3 occurrences of pattern");
|
||||
|
||||
// Test 4: Simple detection
|
||||
let found = comparator.detect_pattern(
|
||||
&sequence.iter().map(|&x| x as f64).collect::<Vec<_>>(),
|
||||
&needle.iter().map(|&x| x as f64).collect::<Vec<_>>(),
|
||||
1.0
|
||||
);
|
||||
assert!(found, "Pattern should be detected");
|
||||
|
||||
// Test 5: Verify caching is working
|
||||
let stats = comparator.cache_stats();
|
||||
assert!(stats.size > 0, "Cache should have entries");
|
||||
}
|
||||
+467
@@ -0,0 +1,467 @@
|
||||
//! Integration tests for temporal comparison and scheduling
|
||||
//!
|
||||
//! Tests real-world scenarios combining temporal analysis and scheduling
|
||||
|
||||
use midstream::{
|
||||
TemporalComparator, Sequence, ComparisonAlgorithm,
|
||||
RealtimeScheduler, SchedulingPolicy, Priority,
|
||||
Action, AgentContext, AgenticLoop, LeanAgenticConfig,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_temporal_conversation_pattern_matching() {
|
||||
// Simulate detecting similar conversation patterns
|
||||
let mut comparator = TemporalComparator::<String>::new();
|
||||
|
||||
// Add historical conversation sequences
|
||||
comparator.add_sequence(Sequence {
|
||||
data: vec![
|
||||
"greeting".to_string(),
|
||||
"weather_query".to_string(),
|
||||
"location_query".to_string(),
|
||||
"weather_response".to_string(),
|
||||
],
|
||||
timestamp: 1000,
|
||||
id: "conv1".to_string(),
|
||||
});
|
||||
|
||||
comparator.add_sequence(Sequence {
|
||||
data: vec![
|
||||
"greeting".to_string(),
|
||||
"weather_query".to_string(),
|
||||
"location_query".to_string(),
|
||||
"weather_response".to_string(),
|
||||
"followup".to_string(),
|
||||
],
|
||||
timestamp: 2000,
|
||||
id: "conv2".to_string(),
|
||||
});
|
||||
|
||||
comparator.add_sequence(Sequence {
|
||||
data: vec![
|
||||
"greeting".to_string(),
|
||||
"calendar_query".to_string(),
|
||||
"calendar_response".to_string(),
|
||||
],
|
||||
timestamp: 3000,
|
||||
id: "conv3".to_string(),
|
||||
});
|
||||
|
||||
// Query with new conversation
|
||||
let query = vec![
|
||||
"greeting".to_string(),
|
||||
"weather_query".to_string(),
|
||||
"location_query".to_string(),
|
||||
];
|
||||
|
||||
let similar = comparator.find_similar(&query, 0.7, ComparisonAlgorithm::LCS);
|
||||
|
||||
// Should find conv1 and conv2 as similar (weather conversations)
|
||||
assert!(similar.len() >= 2);
|
||||
println!("Found {} similar conversations", similar.len());
|
||||
|
||||
for (idx, score) in similar.iter() {
|
||||
println!("Conversation {}: similarity = {}", idx, score);
|
||||
assert!(*score >= 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_temporal_action_sequence_analysis() {
|
||||
// Test analyzing agent action sequences over time
|
||||
let mut comparator = TemporalComparator::<String>::new();
|
||||
|
||||
// Normal behavior pattern
|
||||
let normal_sequence = vec![
|
||||
"plan".to_string(),
|
||||
"verify".to_string(),
|
||||
"execute".to_string(),
|
||||
"observe".to_string(),
|
||||
"learn".to_string(),
|
||||
];
|
||||
|
||||
// Anomalous behavior (skips verification)
|
||||
let anomalous_sequence = vec![
|
||||
"plan".to_string(),
|
||||
"execute".to_string(),
|
||||
"observe".to_string(),
|
||||
"learn".to_string(),
|
||||
];
|
||||
|
||||
// Compare sequences
|
||||
let similarity = comparator.compare(
|
||||
&normal_sequence,
|
||||
&anomalous_sequence,
|
||||
ComparisonAlgorithm::LCS,
|
||||
);
|
||||
|
||||
println!("Similarity between normal and anomalous: {}", similarity);
|
||||
|
||||
// LCS should show high similarity but not perfect
|
||||
assert!(similarity > 0.6);
|
||||
assert!(similarity < 1.0);
|
||||
|
||||
// Edit distance should show difference
|
||||
let distance = comparator.compare(
|
||||
&normal_sequence,
|
||||
&anomalous_sequence,
|
||||
ComparisonAlgorithm::EditDistance,
|
||||
);
|
||||
|
||||
println!("Edit distance: {}", distance);
|
||||
assert!(distance > 0.0); // Should detect the missing step
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_scheduler_with_deadlines() {
|
||||
// Test real-time scheduling with various deadline constraints
|
||||
let scheduler = RealtimeScheduler::new(SchedulingPolicy::EarliestDeadlineFirst);
|
||||
|
||||
// Schedule critical task with tight deadline
|
||||
let critical_action = Action {
|
||||
action_type: "critical_response".to_string(),
|
||||
description: "User safety check".to_string(),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: Some("safe".to_string()),
|
||||
expected_reward: 1.0,
|
||||
};
|
||||
|
||||
let critical_id = scheduler.schedule(
|
||||
critical_action,
|
||||
Priority::Critical,
|
||||
Duration::from_millis(50), // Very tight deadline
|
||||
Duration::from_millis(10),
|
||||
).await;
|
||||
|
||||
// Schedule normal task with relaxed deadline
|
||||
let normal_action = Action {
|
||||
action_type: "normal_query".to_string(),
|
||||
description: "Regular information request".to_string(),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: None,
|
||||
expected_reward: 0.7,
|
||||
};
|
||||
|
||||
scheduler.schedule(
|
||||
normal_action,
|
||||
Priority::Medium,
|
||||
Duration::from_secs(5), // Relaxed deadline
|
||||
Duration::from_millis(100),
|
||||
).await;
|
||||
|
||||
// EDF should prioritize the critical task due to earlier deadline
|
||||
let next = scheduler.next_task().await.unwrap();
|
||||
assert_eq!(next.id, critical_id);
|
||||
assert_eq!(next.action.action_type, "critical_response");
|
||||
|
||||
println!("Scheduler correctly prioritized critical task with tight deadline");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_scheduler_priority_override() {
|
||||
// Test that priority scheduling overrides based on priority level
|
||||
let scheduler = RealtimeScheduler::new(SchedulingPolicy::FixedPriority);
|
||||
|
||||
// Schedule low priority task first
|
||||
scheduler.schedule(
|
||||
Action {
|
||||
action_type: "background_task".to_string(),
|
||||
description: "Background processing".to_string(),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: None,
|
||||
expected_reward: 0.3,
|
||||
},
|
||||
Priority::Background,
|
||||
Duration::from_secs(10),
|
||||
Duration::from_millis(100),
|
||||
).await;
|
||||
|
||||
// Schedule high priority task second
|
||||
scheduler.schedule(
|
||||
Action {
|
||||
action_type: "urgent_task".to_string(),
|
||||
description: "Urgent response needed".to_string(),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: None,
|
||||
expected_reward: 0.9,
|
||||
},
|
||||
Priority::Critical,
|
||||
Duration::from_secs(10),
|
||||
Duration::from_millis(50),
|
||||
).await;
|
||||
|
||||
// Should get high priority task first despite being scheduled later
|
||||
let next = scheduler.next_task().await.unwrap();
|
||||
assert_eq!(next.action.action_type, "urgent_task");
|
||||
|
||||
println!("Priority scheduling correctly prioritized critical task");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_combined_temporal_and_scheduling() {
|
||||
// Integration test: Use temporal patterns to inform scheduling decisions
|
||||
let mut comparator = TemporalComparator::<String>::new();
|
||||
let scheduler = RealtimeScheduler::new(SchedulingPolicy::EarliestDeadlineFirst);
|
||||
|
||||
// Historical pattern: queries that led to good outcomes
|
||||
comparator.add_sequence(Sequence {
|
||||
data: vec![
|
||||
"user_query".to_string(),
|
||||
"context_check".to_string(),
|
||||
"knowledge_lookup".to_string(),
|
||||
"response".to_string(),
|
||||
],
|
||||
timestamp: 1000,
|
||||
id: "good_pattern".to_string(),
|
||||
});
|
||||
|
||||
// Current query sequence
|
||||
let current = vec!["user_query".to_string(), "context_check".to_string()];
|
||||
|
||||
// Find similar patterns
|
||||
let similar = comparator.find_similar(¤t, 0.5, ComparisonAlgorithm::LCS);
|
||||
|
||||
if !similar.is_empty() {
|
||||
println!("Found similar successful pattern, scheduling with high priority");
|
||||
|
||||
// Schedule next expected action with higher priority
|
||||
scheduler.schedule(
|
||||
Action {
|
||||
action_type: "knowledge_lookup".to_string(),
|
||||
description: "Predicted next action from pattern".to_string(),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: Some("success".to_string()),
|
||||
expected_reward: 0.85,
|
||||
},
|
||||
Priority::High, // Higher priority based on pattern match
|
||||
Duration::from_millis(100),
|
||||
Duration::from_millis(20),
|
||||
).await;
|
||||
}
|
||||
|
||||
let stats = scheduler.get_stats().await;
|
||||
assert_eq!(stats.total_scheduled, 1);
|
||||
|
||||
println!("Successfully combined temporal pattern matching with scheduling");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_scheduler_deadline_checking() {
|
||||
// Test the can_meet_deadline functionality
|
||||
let scheduler = RealtimeScheduler::new(SchedulingPolicy::EarliestDeadlineFirst);
|
||||
|
||||
// Empty queue - should be able to meet deadline
|
||||
let can_meet = scheduler.can_meet_deadline(
|
||||
Duration::from_millis(10),
|
||||
Duration::from_secs(1),
|
||||
).await;
|
||||
assert!(can_meet);
|
||||
|
||||
// Add many tasks
|
||||
for i in 0..50 {
|
||||
scheduler.schedule(
|
||||
Action {
|
||||
action_type: format!("task_{}", i),
|
||||
description: format!("Task {}", i),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: None,
|
||||
expected_reward: 0.7,
|
||||
},
|
||||
Priority::Medium,
|
||||
Duration::from_secs(10),
|
||||
Duration::from_millis(50), // Each task takes 50ms
|
||||
).await;
|
||||
}
|
||||
|
||||
// Now with 50 tasks * 50ms = 2500ms pending work
|
||||
let can_meet_tight = scheduler.can_meet_deadline(
|
||||
Duration::from_millis(10),
|
||||
Duration::from_millis(100), // Want to finish in 100ms
|
||||
).await;
|
||||
|
||||
assert!(!can_meet_tight); // Should not be able to meet tight deadline
|
||||
|
||||
let can_meet_loose = scheduler.can_meet_deadline(
|
||||
Duration::from_millis(10),
|
||||
Duration::from_secs(10), // Generous deadline
|
||||
).await;
|
||||
|
||||
assert!(can_meet_loose); // Should be able to meet loose deadline
|
||||
|
||||
println!("Deadline checking correctly estimates feasibility");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_temporal_caching() {
|
||||
// Test that temporal comparison caching works correctly
|
||||
let mut comparator = TemporalComparator::<i32>::new();
|
||||
|
||||
let seq1: Vec<i32> = (0..100).collect();
|
||||
let seq2: Vec<i32> = (0..100).map(|x| x + 1).collect();
|
||||
|
||||
// First comparison - not cached
|
||||
let result1 = comparator.compare(&seq1, &seq2, ComparisonAlgorithm::DTW);
|
||||
|
||||
// Second comparison - should be cached
|
||||
let result2 = comparator.compare(&seq1, &seq2, ComparisonAlgorithm::DTW);
|
||||
|
||||
assert_eq!(result1, result2);
|
||||
|
||||
let stats = comparator.cache_stats();
|
||||
println!("Cache stats: {:?}", stats);
|
||||
|
||||
// Should have cached the result
|
||||
assert_eq!(stats.dtw_count, 1); // Only computed once
|
||||
|
||||
// Try different algorithm - should compute again
|
||||
let _result3 = comparator.compare(&seq1, &seq2, ComparisonAlgorithm::LCS);
|
||||
|
||||
let stats2 = comparator.cache_stats();
|
||||
assert_eq!(stats2.lcs_count, 1);
|
||||
assert_eq!(stats2.total_comparisons, 2); // DTW + LCS
|
||||
|
||||
println!("Caching working correctly: {} total comparisons", stats2.total_comparisons);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pattern_detection_in_stream() {
|
||||
// Simulate detecting recurring patterns in a stream
|
||||
let comparator = TemporalComparator::<String>::new();
|
||||
|
||||
// Simulated stream of user intents
|
||||
let intent_stream = vec![
|
||||
"weather", "location", "weather", "news", "sports",
|
||||
"weather", "location", "weather", "calendar", "weather",
|
||||
"location", "weather",
|
||||
].into_iter().map(|s| s.to_string()).collect::<Vec<_>>();
|
||||
|
||||
// Pattern we're looking for
|
||||
let pattern = vec!["weather".to_string(), "location".to_string(), "weather".to_string()];
|
||||
|
||||
let positions = comparator.detect_pattern(&intent_stream, &pattern);
|
||||
|
||||
println!("Found pattern at positions: {:?}", positions);
|
||||
assert!(!positions.is_empty());
|
||||
|
||||
// Should find the pattern at position 0 and position 9
|
||||
assert!(positions.contains(&0));
|
||||
assert!(positions.contains(&9));
|
||||
|
||||
println!("Successfully detected {} pattern occurrences in stream", positions.len());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_scheduler_stats_tracking() {
|
||||
// Test that scheduler correctly tracks statistics
|
||||
let scheduler = RealtimeScheduler::new(SchedulingPolicy::EarliestDeadlineFirst);
|
||||
|
||||
// Schedule and execute several tasks
|
||||
for i in 0..10 {
|
||||
let task_id = scheduler.schedule(
|
||||
Action {
|
||||
action_type: format!("task_{}", i),
|
||||
description: format!("Task {}", i),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: None,
|
||||
expected_reward: 0.7,
|
||||
},
|
||||
Priority::Medium,
|
||||
Duration::from_secs(1),
|
||||
Duration::from_millis(10),
|
||||
).await;
|
||||
|
||||
// Mark as executed with varying durations
|
||||
scheduler.mark_executed(task_id, Duration::from_micros(100 * (i + 1))).await;
|
||||
}
|
||||
|
||||
let stats = scheduler.get_stats().await;
|
||||
|
||||
assert_eq!(stats.total_scheduled, 10);
|
||||
assert_eq!(stats.total_executed, 10);
|
||||
assert!(stats.average_latency_ns > 0);
|
||||
assert!(stats.max_latency_ns >= stats.min_latency_ns);
|
||||
|
||||
println!("Scheduler stats: {:?}", stats);
|
||||
println!("Average latency: {} μs", stats.average_latency_ns / 1000);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_real_world_conversation_flow() {
|
||||
// Simulate a realistic conversation flow with scheduling
|
||||
let mut comparator = TemporalComparator::<String>::new();
|
||||
let scheduler = RealtimeScheduler::new(SchedulingPolicy::EarliestDeadlineFirst);
|
||||
|
||||
// Add historical successful conversation patterns
|
||||
comparator.add_sequence(Sequence {
|
||||
data: vec![
|
||||
"greeting".to_string(),
|
||||
"clarification".to_string(),
|
||||
"action".to_string(),
|
||||
"confirmation".to_string(),
|
||||
],
|
||||
timestamp: 1000,
|
||||
id: "success_pattern".to_string(),
|
||||
});
|
||||
|
||||
// Current conversation
|
||||
let current_flow = vec!["greeting".to_string(), "clarification".to_string()];
|
||||
|
||||
// Check similarity to successful patterns
|
||||
let similar = comparator.find_similar(¤t_flow, 0.6, ComparisonAlgorithm::LCS);
|
||||
|
||||
if !similar.is_empty() {
|
||||
// We found a similar successful pattern, schedule next actions accordingly
|
||||
|
||||
// Schedule the predicted next action (from pattern)
|
||||
scheduler.schedule(
|
||||
Action {
|
||||
action_type: "action".to_string(),
|
||||
description: "Execute predicted action from pattern".to_string(),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: Some("confirmation".to_string()),
|
||||
expected_reward: 0.8,
|
||||
},
|
||||
Priority::High,
|
||||
Duration::from_millis(200),
|
||||
Duration::from_millis(50),
|
||||
).await;
|
||||
|
||||
// Schedule confirmation as follow-up
|
||||
scheduler.schedule(
|
||||
Action {
|
||||
action_type: "confirmation".to_string(),
|
||||
description: "Confirm action completion".to_string(),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: Some("success".to_string()),
|
||||
expected_reward: 0.9,
|
||||
},
|
||||
Priority::Medium,
|
||||
Duration::from_millis(500),
|
||||
Duration::from_millis(30),
|
||||
).await;
|
||||
|
||||
println!("Scheduled actions based on historical success pattern");
|
||||
}
|
||||
|
||||
// Execute scheduled tasks
|
||||
let mut executed_count = 0;
|
||||
while let Some(task) = scheduler.next_task().await {
|
||||
println!("Executing: {}", task.action.action_type);
|
||||
scheduler.mark_executed(task.id, Duration::from_millis(10)).await;
|
||||
executed_count += 1;
|
||||
}
|
||||
|
||||
assert_eq!(executed_count, 2);
|
||||
println!("Successfully completed conversation flow based on patterns");
|
||||
}
|
||||
+349
@@ -0,0 +1,349 @@
|
||||
//! WASM Integration Tests for MidStream System
|
||||
//!
|
||||
//! Tests WASM-specific functionality including:
|
||||
//! - WebAssembly compilation and execution
|
||||
//! - Browser compatibility
|
||||
//! - Memory constraints
|
||||
//! - Performance in WASM environments
|
||||
//! - QUIC/WebTransport integration
|
||||
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
use wasm_bindgen_test::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// WASM-specific imports
|
||||
use midstreamer_temporal_compare::{TemporalComparator, Sequence, ComparisonAlgorithm};
|
||||
use midstreamer_scheduler::{RealtimeScheduler, SchedulingPolicy, Priority};
|
||||
use midstreamer_attractor::{AttractorAnalyzer, PhasePoint};
|
||||
use midstreamer_neural_solver::{TemporalNeuralSolver, TemporalFormula, TemporalState, VerificationStrictness};
|
||||
use midstreamer_strange_loop::{StrangeLoop, MetaLevel};
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
/// Test 1: WASM Temporal Comparison
|
||||
#[wasm_bindgen_test]
|
||||
async fn test_wasm_temporal_comparison() {
|
||||
console_log!("=== WASM Temporal Comparison Test ===");
|
||||
|
||||
let mut comparator = TemporalComparator::<String>::new(50, 500);
|
||||
|
||||
// Add sequences
|
||||
comparator.add_sequence(Sequence {
|
||||
data: vec!["a".to_string(), "b".to_string(), "c".to_string()],
|
||||
timestamp: 1000,
|
||||
id: "seq1".to_string(),
|
||||
});
|
||||
|
||||
comparator.add_sequence(Sequence {
|
||||
data: vec!["a".to_string(), "b".to_string(), "d".to_string()],
|
||||
timestamp: 2000,
|
||||
id: "seq2".to_string(),
|
||||
});
|
||||
|
||||
// Find similar sequences
|
||||
let query = vec!["a".to_string(), "b".to_string()];
|
||||
let similar = comparator.find_similar(&query, 0.7, ComparisonAlgorithm::LCS);
|
||||
|
||||
console_log!("Found {} similar sequences in WASM", similar.len());
|
||||
assert!(similar.len() >= 2);
|
||||
|
||||
console_log!("✓ WASM temporal comparison successful");
|
||||
}
|
||||
|
||||
/// Test 2: WASM Scheduler
|
||||
#[wasm_bindgen_test]
|
||||
async fn test_wasm_scheduler() {
|
||||
console_log!("=== WASM Scheduler Test ===");
|
||||
|
||||
let scheduler = RealtimeScheduler::new(SchedulingPolicy::FixedPriority);
|
||||
|
||||
// Schedule multiple tasks
|
||||
for i in 0..10 {
|
||||
scheduler.schedule(
|
||||
create_action(&format!("wasm_task_{}", i), "WASM task"),
|
||||
Priority::Medium,
|
||||
std::time::Duration::from_secs(1),
|
||||
std::time::Duration::from_millis(10),
|
||||
).await;
|
||||
}
|
||||
|
||||
let stats = scheduler.get_stats().await;
|
||||
console_log!("Scheduled {} tasks in WASM", stats.total_scheduled);
|
||||
assert_eq!(stats.total_scheduled, 10);
|
||||
|
||||
console_log!("✓ WASM scheduler successful");
|
||||
}
|
||||
|
||||
/// Test 3: WASM Attractor Analysis
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_attractor_analysis() {
|
||||
console_log!("=== WASM Attractor Analysis Test ===");
|
||||
|
||||
let mut analyzer = AttractorAnalyzer::new(2, 1000);
|
||||
|
||||
// Add points
|
||||
for i in 0..150 {
|
||||
let point = PhasePoint::new(
|
||||
vec![
|
||||
(i as f64 * 0.1).sin(),
|
||||
(i as f64 * 0.1).cos(),
|
||||
],
|
||||
i as u64,
|
||||
);
|
||||
analyzer.add_point(point).unwrap();
|
||||
}
|
||||
|
||||
let result = analyzer.analyze();
|
||||
assert!(result.is_ok());
|
||||
|
||||
let info = result.unwrap();
|
||||
console_log!("Detected attractor: {:?}", info.attractor_type);
|
||||
console_log!("Confidence: {}", info.confidence);
|
||||
|
||||
console_log!("✓ WASM attractor analysis successful");
|
||||
}
|
||||
|
||||
/// Test 4: WASM Temporal Logic Verification
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_temporal_verification() {
|
||||
console_log!("=== WASM Temporal Verification Test ===");
|
||||
|
||||
let mut solver = TemporalNeuralSolver::new(1000, 500, VerificationStrictness::Medium);
|
||||
|
||||
// Create trace
|
||||
for i in 0..10 {
|
||||
let mut state = TemporalState::new(i, i * 100);
|
||||
state.set_proposition("safe", true);
|
||||
state.set_proposition("ready", i >= 5);
|
||||
solver.add_state(state);
|
||||
}
|
||||
|
||||
// Verify safety
|
||||
let formula = TemporalFormula::globally(TemporalFormula::atom("safe"));
|
||||
let result = solver.verify(&formula).unwrap();
|
||||
|
||||
console_log!("Safety verified: {}", result.satisfied);
|
||||
assert!(result.satisfied);
|
||||
|
||||
console_log!("✓ WASM temporal verification successful");
|
||||
}
|
||||
|
||||
/// Test 5: WASM Meta-Learning
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_meta_learning() {
|
||||
console_log!("=== WASM Meta-Learning Test ===");
|
||||
|
||||
let mut strange_loop = StrangeLoop::default();
|
||||
|
||||
let data = vec![
|
||||
"pattern1".to_string(),
|
||||
"pattern2".to_string(),
|
||||
"pattern1".to_string(),
|
||||
];
|
||||
|
||||
let result = strange_loop.learn_at_level(MetaLevel::base(), &data);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let summary = strange_loop.get_summary();
|
||||
console_log!("Learned {} patterns", summary.total_knowledge);
|
||||
|
||||
console_log!("✓ WASM meta-learning successful");
|
||||
}
|
||||
|
||||
/// Test 6: WASM Memory Constraints
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_memory_limits() {
|
||||
console_log!("=== WASM Memory Limits Test ===");
|
||||
|
||||
// Test with limited memory allocation
|
||||
let comparator = TemporalComparator::<i32>::new(100, 1000); // Smaller limits for WASM
|
||||
|
||||
// Add moderate amount of data
|
||||
for i in 0..50 {
|
||||
let seq: Vec<i32> = (0..100).map(|x| x + i).collect();
|
||||
comparator.add_sequence(Sequence {
|
||||
data: seq,
|
||||
timestamp: i as u64 * 1000,
|
||||
id: format!("seq_{}", i),
|
||||
});
|
||||
}
|
||||
|
||||
console_log!("✓ WASM memory constraints handled");
|
||||
}
|
||||
|
||||
/// Test 7: WASM Performance
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_performance() {
|
||||
console_log!("=== WASM Performance Test ===");
|
||||
|
||||
use web_sys::window;
|
||||
|
||||
let window = window().expect("should have window");
|
||||
let performance = window.performance().expect("should have performance");
|
||||
|
||||
let start = performance.now();
|
||||
|
||||
// Perform computations
|
||||
let comparator = TemporalComparator::<i32>::new(100, 1000);
|
||||
let seq1: Vec<i32> = (0..500).collect();
|
||||
let seq2: Vec<i32> = (0..500).map(|x| x + 1).collect();
|
||||
|
||||
let _similarity = comparator.compare(&seq1, &seq2, ComparisonAlgorithm::LCS);
|
||||
|
||||
let duration = performance.now() - start;
|
||||
|
||||
console_log!("Comparison took {} ms in WASM", duration);
|
||||
assert!(duration < 5000.0, "Should complete within 5 seconds");
|
||||
|
||||
console_log!("✓ WASM performance acceptable");
|
||||
}
|
||||
|
||||
/// Test 8: WASM Concurrent Operations
|
||||
#[wasm_bindgen_test]
|
||||
async fn test_wasm_concurrent_ops() {
|
||||
console_log!("=== WASM Concurrent Operations Test ===");
|
||||
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
let counter = Arc::new(AtomicU32::new(0));
|
||||
|
||||
// Spawn multiple concurrent tasks
|
||||
let mut handles = vec![];
|
||||
|
||||
for i in 0..5 {
|
||||
let counter_clone = counter.clone();
|
||||
let future = async move {
|
||||
let mut comparator = TemporalComparator::<i32>::new(10, 100);
|
||||
let seq: Vec<i32> = (0..10).map(|x| x + i).collect();
|
||||
|
||||
comparator.add_sequence(Sequence {
|
||||
data: seq,
|
||||
timestamp: i as u64,
|
||||
id: format!("concurrent_{}", i),
|
||||
});
|
||||
|
||||
counter_clone.fetch_add(1, Ordering::SeqCst);
|
||||
};
|
||||
|
||||
spawn_local(future);
|
||||
}
|
||||
|
||||
// Wait a bit for tasks to complete
|
||||
wasm_timer::Delay::new(std::time::Duration::from_millis(100)).await.ok();
|
||||
|
||||
let count = counter.load(Ordering::SeqCst);
|
||||
console_log!("Completed {} concurrent operations", count);
|
||||
|
||||
console_log!("✓ WASM concurrent operations successful");
|
||||
}
|
||||
|
||||
/// Test 9: WASM Error Handling
|
||||
#[wasm_bindgen_test]
|
||||
fn test_wasm_error_handling() {
|
||||
console_log!("=== WASM Error Handling Test ===");
|
||||
|
||||
// Test dimension mismatch
|
||||
let mut analyzer = AttractorAnalyzer::new(3, 100);
|
||||
let invalid_point = PhasePoint::new(vec![1.0, 2.0], 0);
|
||||
let result = analyzer.add_point(invalid_point);
|
||||
|
||||
assert!(result.is_err());
|
||||
console_log!("✓ Dimension mismatch handled correctly");
|
||||
|
||||
// Test empty trace
|
||||
let solver = TemporalNeuralSolver::default();
|
||||
let formula = TemporalFormula::atom("test");
|
||||
let result = solver.verify(&formula);
|
||||
|
||||
assert!(result.is_err());
|
||||
console_log!("✓ Empty trace handled correctly");
|
||||
|
||||
console_log!("✓ WASM error handling successful");
|
||||
}
|
||||
|
||||
/// Test 10: WASM Integration Workflow
|
||||
#[wasm_bindgen_test]
|
||||
async fn test_wasm_integration_workflow() {
|
||||
console_log!("=== WASM Integration Workflow Test ===");
|
||||
|
||||
// Step 1: Pattern detection
|
||||
let mut comparator = TemporalComparator::<String>::new(50, 500);
|
||||
comparator.add_sequence(Sequence {
|
||||
data: vec!["init".to_string(), "process".to_string(), "complete".to_string()],
|
||||
timestamp: 1000,
|
||||
id: "workflow".to_string(),
|
||||
});
|
||||
|
||||
// Step 2: Schedule based on pattern
|
||||
let scheduler = RealtimeScheduler::new(SchedulingPolicy::EarliestDeadlineFirst);
|
||||
scheduler.schedule(
|
||||
create_action("wasm_workflow", "Workflow task"),
|
||||
Priority::High,
|
||||
std::time::Duration::from_secs(1),
|
||||
std::time::Duration::from_millis(50),
|
||||
).await;
|
||||
|
||||
// Step 3: Verify behavior
|
||||
let mut solver = TemporalNeuralSolver::default();
|
||||
let mut state = TemporalState::new(1, 100);
|
||||
state.set_proposition("completed", true);
|
||||
solver.add_state(state);
|
||||
|
||||
let formula = TemporalFormula::atom("completed");
|
||||
let result = solver.verify(&formula).unwrap();
|
||||
|
||||
console_log!("Workflow completed: {}", result.satisfied);
|
||||
assert!(result.satisfied);
|
||||
|
||||
console_log!("✓ WASM integration workflow successful");
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
fn create_action(action_type: &str, description: &str) -> nanosecond_scheduler::Action {
|
||||
nanosecond_scheduler::Action {
|
||||
action_type: action_type.to_string(),
|
||||
description: description.to_string(),
|
||||
parameters: HashMap::new(),
|
||||
tool_calls: vec![],
|
||||
expected_outcome: None,
|
||||
expected_reward: 0.8,
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! console_log {
|
||||
($($t:tt)*) => {
|
||||
web_sys::console::log_1(&format!($($t)*).into());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod wasm_summary {
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn wasm_test_summary() {
|
||||
console_log!("\n");
|
||||
console_log!("╔═══════════════════════════════════════════════════════════════╗");
|
||||
console_log!("║ MidStream WASM Integration Test Suite ║");
|
||||
console_log!("╠═══════════════════════════════════════════════════════════════╣");
|
||||
console_log!("║ ║");
|
||||
console_log!("║ ✓ WASM Temporal Comparison ║");
|
||||
console_log!("║ ✓ WASM Scheduler ║");
|
||||
console_log!("║ ✓ WASM Attractor Analysis ║");
|
||||
console_log!("║ ✓ WASM Temporal Verification ║");
|
||||
console_log!("║ ✓ WASM Meta-Learning ║");
|
||||
console_log!("║ ✓ WASM Memory Limits ║");
|
||||
console_log!("║ ✓ WASM Performance ║");
|
||||
console_log!("║ ✓ WASM Concurrent Operations ║");
|
||||
console_log!("║ ✓ WASM Error Handling ║");
|
||||
console_log!("║ ✓ WASM Integration Workflow ║");
|
||||
console_log!("║ ║");
|
||||
console_log!("╚═══════════════════════════════════════════════════════════════╝");
|
||||
console_log!("\n");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user