git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
13 KiB
Integration Test Implementation Guide
This guide helps implementers understand and use the integration tests for the EXO-AI 2025 cognitive substrate.
Philosophy: Test-Driven Development
The integration tests in this project are written BEFORE implementation. This provides several benefits:
- Clear API Specifications - Tests show exactly what interfaces are expected
- Executable Documentation - Tests demonstrate how to use the system
- Implementation Guidance - Tests guide implementation priorities
- Quality Assurance - Passing tests verify correctness
Quick Start for Implementers
Step 1: Choose a Component
Start with one of these components:
- exo-core (foundational traits) - Start here
- exo-backend-classical (ruvector integration) - Depends on exo-core
- exo-manifold (learned storage) - Depends on exo-core
- exo-hypergraph (topology) - Depends on exo-core
- exo-temporal (causal memory) - Depends on exo-core
- exo-federation (distributed) - Depends on all above
Step 2: Read the Tests
Find the relevant test file:
cd tests/
ls -la
# substrate_integration.rs - For exo-core/backend
# hypergraph_integration.rs - For exo-hypergraph
# temporal_integration.rs - For exo-temporal
# federation_integration.rs - For exo-federation
Read the test to understand expected behavior:
#[tokio::test]
#[ignore]
async fn test_substrate_store_and_retrieve() {
// This shows the expected API:
let config = SubstrateConfig::default();
let backend = ClassicalBackend::new(config).unwrap();
let substrate = SubstrateInstance::new(backend);
// ... rest of test shows expected behavior
}
Step 3: Implement to Pass Tests
Create the crate structure:
cd crates/
mkdir exo-core
cd exo-core
cargo init --lib
Implement the types and methods shown in the test:
// crates/exo-core/src/lib.rs
pub struct SubstrateConfig {
// fields based on test usage
}
pub struct SubstrateInstance {
// implementation
}
impl SubstrateInstance {
pub fn new(backend: impl SubstrateBackend) -> Self {
// implementation
}
pub async fn store(&self, pattern: Pattern) -> Result<PatternId, Error> {
// implementation
}
pub async fn search(&self, query: Query, k: usize) -> Result<Vec<SearchResult>, Error> {
// implementation
}
}
Step 4: Remove #[ignore] and Test
// Remove this line:
// #[ignore]
#[tokio::test]
async fn test_substrate_store_and_retrieve() {
// test code...
}
Run the test:
cargo test --test substrate_integration test_substrate_store_and_retrieve
Step 5: Iterate Until Passing
Fix compilation errors, then runtime errors, until:
test substrate_tests::test_substrate_store_and_retrieve ... ok
Detailed Component Guides
Implementing exo-core
Priority Order:
- Core Types - Pattern, Query, Metadata, SubstrateTime
- Backend Trait - SubstrateBackend trait definition
- Substrate Instance - Main API facade
- Error Types - Comprehensive error handling
Key Tests:
cargo test --test substrate_integration test_substrate_store_and_retrieve
cargo test --test substrate_integration test_filtered_search
cargo test --test substrate_integration test_bulk_operations
Expected API Surface:
// Types
pub struct Pattern {
pub embedding: Vec<f32>,
pub metadata: Metadata,
pub timestamp: SubstrateTime,
pub antecedents: Vec<PatternId>,
}
pub struct Query {
embedding: Vec<f32>,
filter: Option<Filter>,
}
pub struct SearchResult {
pub id: PatternId,
pub pattern: Pattern,
pub score: f32,
}
// Traits
pub trait SubstrateBackend: Send + Sync {
type Error: std::error::Error;
fn similarity_search(
&self,
query: &[f32],
k: usize,
filter: Option<&Filter>,
) -> Result<Vec<SearchResult>, Self::Error>;
// ... other methods
}
// Main API
pub struct SubstrateInstance {
backend: Arc<dyn SubstrateBackend>,
}
impl SubstrateInstance {
pub fn new(backend: impl SubstrateBackend + 'static) -> Self;
pub async fn store(&self, pattern: Pattern) -> Result<PatternId, Error>;
pub async fn search(&self, query: Query, k: usize) -> Result<Vec<SearchResult>, Error>;
}
Implementing exo-manifold
Depends On: exo-core, burn framework
Priority Order:
- Manifold Network - Neural network architecture (SIREN layers)
- Gradient Descent Retrieval - Query via optimization
- Continuous Deformation - Learning without discrete insert
- Forgetting Mechanism - Strategic memory decay
Key Tests:
cargo test --test substrate_integration test_manifold_deformation
cargo test --test substrate_integration test_strategic_forgetting
Expected Architecture:
use burn::prelude::*;
pub struct ManifoldEngine<B: Backend> {
network: LearnedManifold<B>,
optimizer: AdamOptimizer<B>,
config: ManifoldConfig,
}
impl<B: Backend> ManifoldEngine<B> {
pub fn retrieve(&self, query: Tensor<B, 1>, k: usize) -> Vec<(Pattern, f32)> {
// Gradient descent on manifold
}
pub fn deform(&mut self, pattern: Pattern, salience: f32) {
// Continuous learning
}
pub fn forget(&mut self, region: &ManifoldRegion, decay_rate: f32) {
// Strategic forgetting
}
}
Implementing exo-hypergraph
Depends On: exo-core, petgraph, ruvector-graph
Priority Order:
- Hyperedge Storage - Multi-entity relationships
- Topological Queries - Basic graph queries
- Persistent Homology - TDA integration (teia crate)
- Sheaf Structures - Advanced consistency (optional)
Key Tests:
cargo test --test hypergraph_integration test_hyperedge_creation_and_query
cargo test --test hypergraph_integration test_persistent_homology
cargo test --test hypergraph_integration test_betti_numbers
Expected Architecture:
use ruvector_graph::GraphDatabase;
use petgraph::Graph;
pub struct HypergraphSubstrate {
base: GraphDatabase,
hyperedges: HyperedgeIndex,
topology: SimplicialComplex,
sheaf: Option<SheafStructure>,
}
impl HypergraphSubstrate {
pub async fn create_hyperedge(
&mut self,
entities: &[EntityId],
relation: &Relation,
) -> Result<HyperedgeId, Error>;
pub async fn persistent_homology(
&self,
dimension: usize,
epsilon_range: (f32, f32),
) -> Result<PersistenceDiagram, Error>;
pub async fn betti_numbers(&self, max_dim: usize) -> Result<Vec<usize>, Error>;
}
Implementing exo-temporal
Depends On: exo-core
Priority Order:
- Causal Graph - Antecedent tracking
- Causal Queries - Cone-based retrieval
- Memory Consolidation - Short-term to long-term
- Predictive Pre-fetch - Anticipation
Key Tests:
cargo test --test temporal_integration test_causal_storage_and_query
cargo test --test temporal_integration test_memory_consolidation
cargo test --test temporal_integration test_predictive_anticipation
Expected Architecture:
pub struct TemporalMemory {
short_term: ShortTermBuffer,
long_term: LongTermStore,
causal_graph: CausalGraph,
tkg: TemporalKnowledgeGraph,
}
impl TemporalMemory {
pub async fn store(
&mut self,
pattern: Pattern,
antecedents: &[PatternId],
) -> Result<PatternId, Error>;
pub async fn causal_query(
&self,
query: &Query,
reference_time: SubstrateTime,
cone_type: CausalConeType,
) -> Result<Vec<CausalResult>, Error>;
pub async fn consolidate(&mut self) -> Result<(), Error>;
pub async fn anticipate(&mut self, hints: &[AnticipationHint]) -> Result<(), Error>;
}
Implementing exo-federation
Depends On: exo-core, exo-temporal, ruvector-raft, kyberlib
Priority Order:
- CRDT Merge - Conflict-free reconciliation
- Post-Quantum Handshake - Kyber key exchange
- Byzantine Consensus - PBFT-style agreement
- Onion Routing - Privacy-preserving queries
Key Tests:
cargo test --test federation_integration test_crdt_merge_reconciliation
cargo test --test federation_integration test_byzantine_consensus
cargo test --test federation_integration test_post_quantum_handshake
Expected Architecture:
use ruvector_raft::RaftNode;
use kyberlib::{encapsulate, decapsulate};
pub struct FederatedMesh {
local: Arc<SubstrateInstance>,
consensus: RaftNode,
gateway: FederationGateway,
pq_keys: PostQuantumKeypair,
}
impl FederatedMesh {
pub async fn join_federation(
&mut self,
peer: &PeerAddress,
) -> Result<FederationToken, Error>;
pub async fn federated_query(
&self,
query: &Query,
scope: FederationScope,
) -> Result<Vec<FederatedResult>, Error>;
pub async fn byzantine_commit(
&self,
update: &StateUpdate,
) -> Result<CommitProof, Error>;
pub async fn merge_crdt_state(&mut self, state: CrdtState) -> Result<(), Error>;
}
Common Implementation Patterns
Async-First Design
All integration tests use tokio::test. Implement async throughout:
#[tokio::test]
async fn test_example() {
let result = substrate.async_operation().await.unwrap();
}
Error Handling
Use Result<T, Error> everywhere. Tests call .unwrap() or .expect():
pub async fn store(&self, pattern: Pattern) -> Result<PatternId, Error> {
// Implementation
}
// In tests:
let id = substrate.store(pattern).await.unwrap();
Test Utilities
Leverage the test helpers:
use common::fixtures::*;
use common::assertions::*;
use common::helpers::*;
#[tokio::test]
async fn test_example() {
init_test_logger();
let embeddings = generate_test_embeddings(100, 128);
let results = substrate.search(query, 10).await.unwrap();
assert_scores_descending(&results.iter().map(|r| r.score).collect::<Vec<_>>());
}
Debugging Integration Test Failures
Enable Logging
RUST_LOG=debug cargo test --test substrate_integration -- --nocapture
Run Single Test
cargo test --test substrate_integration test_substrate_store_and_retrieve -- --exact --nocapture
Add Debug Prints
#[tokio::test]
async fn test_example() {
let result = substrate.search(query, 10).await.unwrap();
dbg!(&result); // Debug print
assert_eq!(result.len(), 10);
}
Use Breakpoints
With VS Code + rust-analyzer:
- Set breakpoint in test or implementation
- Run "Debug Test" from code lens
- Step through execution
Performance Profiling
Measure Test Duration
use common::helpers::measure_async;
#[tokio::test]
async fn test_performance() {
let (result, duration) = measure_async(async {
substrate.search(query, 10).await.unwrap()
}).await;
assert!(duration.as_millis() < 10, "Query too slow: {:?}", duration);
}
Benchmark Mode
cargo test --test substrate_integration --release -- --nocapture
Coverage Analysis
Generate coverage reports:
cargo install cargo-tarpaulin
cargo tarpaulin --workspace --out Html --output-dir coverage
open coverage/index.html
Target: >80% coverage for implemented crates.
CI/CD Integration
Tests run automatically on:
- Pull requests (all tests)
- Main branch (all tests + coverage)
- Nightly (all tests + benchmarks)
See: .github/workflows/integration-tests.yml
FAQ
Q: All tests are ignored. How do I start?
A: Pick a test, implement the required types/methods, remove #[ignore], run the test.
Q: Test expects types I haven't implemented yet?
A: Implement them! The test shows exactly what's needed.
Q: Can I modify the tests?
A: Generally no - tests define the contract. If a test is wrong, discuss with the team first.
Q: How do I add new integration tests?
A: Follow existing patterns, add to relevant file, document in tests/README.md.
Q: Tests depend on each other?
A: They shouldn't. Each test should be independent. Use test fixtures for shared setup.
Q: How do I mock dependencies?
A: Use the fixtures in common/fixtures.rs or create test-specific mocks.
Getting Help
- Architecture Questions: See
../architecture/ARCHITECTURE.md - API Questions: Read the test code - it shows expected usage
- Implementation Questions: Check pseudocode in
../architecture/PSEUDOCODE.md - General Questions: Open a GitHub issue
Success Checklist
Before marking a component "done":
- All relevant integration tests pass (not ignored)
- Code coverage > 80%
- No compiler warnings
- Documentation written (rustdoc)
- Examples added to crate
- Performance targets met (see tests/README.md)
- Code reviewed by team
Next Steps
- Read the architecture:
../architecture/ARCHITECTURE.md - Pick a component (recommend starting with exo-core)
- Read its integration tests
- Implement to pass tests
- Submit PR with passing tests
Good luck! The tests are your guide. Trust the TDD process.