Skip to main content

voxora_engine/
testing.rs

1//! Test helpers — fake adapters that implement the [`EngineAdapter`]
2//! contract for downstream tests.
3
4use voxora_traits::{AsrEngine, AsrError, ModelCapabilities, TranscribeOptions, TranscriptionResult};
5
6use crate::adapter::EngineAdapter;
7use crate::backend::{BackendDescriptor, BackendKind};
8use crate::family::EngineFamily;
9use crate::info::EngineInfo;
10
11/// Minimal adapter used in tests. Delegates `AsrEngine` calls to a
12/// deterministic in-memory implementation.
13#[derive(Debug, Clone)]
14pub struct MockAdapter {
15    family: EngineFamily,
16    capabilities: ModelCapabilities,
17    backend: BackendDescriptor,
18}
19
20impl MockAdapter {
21    /// Build a CPU-backed, English+Spanish, multilingual mock adapter
22    /// for `family`. Use the builder helpers to override the defaults.
23    pub fn new(family: EngineFamily) -> Self {
24        let capabilities =
25            ModelCapabilities::new(true, true, false, vec!["en".into(), "es".into()]);
26        Self {
27            family,
28            capabilities,
29            backend: BackendDescriptor::new(BackendKind::Cpu),
30        }
31    }
32
33    /// Swap the backend kind (e.g. to exercise `cuda` code paths in
34    /// the adapter contract).
35    pub fn with_backend(mut self, backend: BackendKind) -> Self {
36        self.backend = BackendDescriptor::new(backend);
37        self
38    }
39
40    /// Override the [`ModelCapabilities`] advertised by the mock.
41    pub fn with_capabilities(mut self, caps: ModelCapabilities) -> Self {
42        self.capabilities = caps;
43        self
44    }
45}
46
47impl AsrEngine for MockAdapter {
48    fn capabilities(&self) -> ModelCapabilities {
49        self.capabilities.clone()
50    }
51
52    fn transcribe(
53        &self,
54        samples: &[f32],
55        opts: &TranscribeOptions,
56    ) -> Result<TranscriptionResult, AsrError> {
57        Ok(TranscriptionResult::new(format!(
58            "mock of {} samples ({:?})",
59            samples.len(),
60            opts.language
61        )))
62    }
63}
64
65impl EngineAdapter for MockAdapter {
66    fn family(&self) -> EngineFamily {
67        self.family
68    }
69
70    fn info(&self) -> EngineInfo {
71        EngineInfo::new(self.family, self.capabilities.clone())
72            .with_model_label(format!("mock-{}", self.family.as_config()))
73    }
74
75    fn backend(&self) -> BackendDescriptor {
76        self.backend
77    }
78
79    fn as_asr_engine(&self) -> &dyn AsrEngine {
80        self
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn mock_reports_its_family() {
90        let m = MockAdapter::new(EngineFamily::Whisper);
91        assert_eq!(m.family(), EngineFamily::Whisper);
92    }
93
94    #[test]
95    fn mock_transcribe_returns_deterministic_string() {
96        let m = MockAdapter::new(EngineFamily::Qwen3Asr);
97        let r = m
98            .transcribe(&[0.0_f32; 8], &TranscribeOptions::default())
99            .expect("ok");
100        assert_eq!(r.text, "mock of 8 samples (None)");
101    }
102
103    #[test]
104    fn mock_with_backend_reflects_in_adapter() {
105        let m = MockAdapter::new(EngineFamily::Whisper).with_backend(BackendKind::Cuda);
106        assert_eq!(m.backend().kind, BackendKind::Cuda);
107    }
108}