220 lines
7.0 KiB
Rust
220 lines
7.0 KiB
Rust
//! Integration tests for 7sense bioacoustics platform
|
|
//!
|
|
//! This module organizes integration tests across all six bounded contexts:
|
|
//! - Audio Ingestion Context
|
|
//! - Embedding Context
|
|
//! - Vector Space Context
|
|
//! - Learning Context (via Analysis)
|
|
//! - Analysis Context
|
|
//! - Interpretation Context
|
|
//!
|
|
//! Tests are organized by context and follow the domain-driven design boundaries.
|
|
//!
|
|
//! Note: Individual test files (audio_test.rs, etc.) are compiled as separate
|
|
//! test binaries via [[test]] entries in Cargo.toml.
|
|
|
|
// Re-export commonly used test utilities
|
|
pub use crate::fixtures::*;
|
|
pub use crate::mocks::*;
|
|
|
|
/// Common test configuration
|
|
#[derive(Debug, Clone)]
|
|
pub struct TestConfig {
|
|
/// Sample rate for audio (32kHz for Perch 2.0)
|
|
pub sample_rate: u32,
|
|
/// Embedding dimensions (1536 for Perch 2.0)
|
|
pub embedding_dims: usize,
|
|
/// Default segment duration in ms
|
|
pub segment_duration_ms: u64,
|
|
/// HNSW M parameter
|
|
pub hnsw_m: usize,
|
|
/// HNSW ef_construction
|
|
pub hnsw_ef_construction: usize,
|
|
/// HNSW ef_search
|
|
pub hnsw_ef_search: usize,
|
|
/// Minimum cluster size for HDBSCAN
|
|
pub min_cluster_size: usize,
|
|
/// Target recall@10 for vector search
|
|
pub target_recall_at_10: f32,
|
|
}
|
|
|
|
impl Default for TestConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
sample_rate: 32000,
|
|
embedding_dims: 1536,
|
|
segment_duration_ms: 5000,
|
|
hnsw_m: 16,
|
|
hnsw_ef_construction: 200,
|
|
hnsw_ef_search: 100,
|
|
min_cluster_size: 5,
|
|
target_recall_at_10: 0.95,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TestConfig {
|
|
/// Create config for fast tests (lower quality but faster)
|
|
pub fn fast() -> Self {
|
|
Self {
|
|
hnsw_m: 8,
|
|
hnsw_ef_construction: 50,
|
|
hnsw_ef_search: 20,
|
|
min_cluster_size: 3,
|
|
target_recall_at_10: 0.90,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
/// Create config for high-quality tests (slower but more accurate)
|
|
pub fn high_quality() -> Self {
|
|
Self {
|
|
hnsw_m: 32,
|
|
hnsw_ef_construction: 400,
|
|
hnsw_ef_search: 200,
|
|
min_cluster_size: 10,
|
|
target_recall_at_10: 0.99,
|
|
..Default::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Shared test context that can be used across integration tests
|
|
pub struct IntegrationTestContext {
|
|
pub config: TestConfig,
|
|
pub recording_repo: MockRecordingRepository,
|
|
pub segment_repo: MockSegmentRepository,
|
|
pub embedding_repo: MockEmbeddingRepository,
|
|
pub vector_index: MockVectorIndex,
|
|
pub clustering_service: MockClusteringService,
|
|
pub evidence_builder: MockEvidencePackBuilder,
|
|
pub interpretation_generator: MockInterpretationGenerator,
|
|
}
|
|
|
|
impl Default for IntegrationTestContext {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl IntegrationTestContext {
|
|
pub fn new() -> Self {
|
|
let config = TestConfig::default();
|
|
Self {
|
|
config: config.clone(),
|
|
recording_repo: MockRecordingRepository::new(),
|
|
segment_repo: MockSegmentRepository::new(),
|
|
embedding_repo: MockEmbeddingRepository::new(),
|
|
vector_index: MockVectorIndex::with_config(HnswConfig {
|
|
m: config.hnsw_m,
|
|
ef_construction: config.hnsw_ef_construction,
|
|
ef_search: config.hnsw_ef_search,
|
|
max_layers: 6,
|
|
}),
|
|
clustering_service: MockClusteringService::with_params(config.min_cluster_size, 3),
|
|
evidence_builder: MockEvidencePackBuilder::new(),
|
|
interpretation_generator: MockInterpretationGenerator::new(),
|
|
}
|
|
}
|
|
|
|
pub fn with_config(config: TestConfig) -> Self {
|
|
Self {
|
|
config: config.clone(),
|
|
recording_repo: MockRecordingRepository::new(),
|
|
segment_repo: MockSegmentRepository::new(),
|
|
embedding_repo: MockEmbeddingRepository::new(),
|
|
vector_index: MockVectorIndex::with_config(HnswConfig {
|
|
m: config.hnsw_m,
|
|
ef_construction: config.hnsw_ef_construction,
|
|
ef_search: config.hnsw_ef_search,
|
|
max_layers: 6,
|
|
}),
|
|
clustering_service: MockClusteringService::with_params(config.min_cluster_size, 3),
|
|
evidence_builder: MockEvidencePackBuilder::new(),
|
|
interpretation_generator: MockInterpretationGenerator::new(),
|
|
}
|
|
}
|
|
|
|
/// Populate context with test data
|
|
pub fn with_test_data(self, num_recordings: usize, segments_per_recording: usize) -> Self {
|
|
for _ in 0..num_recordings {
|
|
let recording = create_test_recording();
|
|
let recording_id = recording.id;
|
|
self.recording_repo.save(recording).unwrap();
|
|
|
|
for i in 0..segments_per_recording {
|
|
let start_ms = i as u64 * 5500;
|
|
let segment = CallSegment {
|
|
id: SegmentId::new(),
|
|
recording_id,
|
|
start_ms,
|
|
end_ms: start_ms + 5000,
|
|
..Default::default()
|
|
};
|
|
let segment_id = segment.id;
|
|
self.segment_repo.save(segment).unwrap();
|
|
self.recording_repo
|
|
.add_segment_link(recording_id, segment_id);
|
|
|
|
let embedding = Embedding {
|
|
segment_id,
|
|
..Default::default()
|
|
};
|
|
let embedding_id = embedding.id;
|
|
let vector = embedding.vector.clone();
|
|
self.embedding_repo.save(embedding).unwrap();
|
|
self.vector_index.insert(embedding_id, vector).unwrap();
|
|
}
|
|
}
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Helper macro for async test setup
|
|
#[macro_export]
|
|
macro_rules! setup_test {
|
|
() => {
|
|
IntegrationTestContext::new()
|
|
};
|
|
(fast) => {
|
|
IntegrationTestContext::with_config(TestConfig::fast())
|
|
};
|
|
(high_quality) => {
|
|
IntegrationTestContext::with_config(TestConfig::high_quality())
|
|
};
|
|
(populated) => {
|
|
IntegrationTestContext::new().with_test_data(5, 10)
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_integration_context_creation() {
|
|
let ctx = IntegrationTestContext::new();
|
|
assert_eq!(ctx.config.sample_rate, 32000);
|
|
assert_eq!(ctx.config.embedding_dims, 1536);
|
|
}
|
|
|
|
#[test]
|
|
fn test_context_with_test_data() {
|
|
let ctx = IntegrationTestContext::new().with_test_data(2, 5);
|
|
assert_eq!(ctx.recording_repo.count(), 2);
|
|
assert_eq!(ctx.segment_repo.count(), 10);
|
|
assert_eq!(ctx.embedding_repo.count(), 10);
|
|
assert_eq!(ctx.vector_index.count(), 10);
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_variants() {
|
|
let fast = TestConfig::fast();
|
|
let hq = TestConfig::high_quality();
|
|
|
|
assert!(fast.hnsw_m < hq.hnsw_m);
|
|
assert!(fast.hnsw_ef_construction < hq.hnsw_ef_construction);
|
|
assert!(fast.target_recall_at_10 < hq.target_recall_at_10);
|
|
}
|
|
}
|