mod common;
use common::{send_one, SilentAsr};
use euhadra::prelude::*;
#[tokio::test]
async fn llm_failure_falls_back_to_processed_text() {
let emitter = MockEmitter::new();
let outputs = emitter.outputs();
let pipeline = Pipeline::builder()
.asr(MockAsr::new("raw dictation text"))
.filter(SimpleFillerFilter::english())
.processor(BasicPunctuationRestorer)
.refiner(MockRefiner::failing("upstream timeout"))
.context(MockContextProvider::new())
.emitter(emitter)
.build()
.unwrap();
let session = pipeline.session();
send_one(&session.audio).await;
let result = session
.finish()
.await
.expect("pipeline must succeed despite LLM failure");
assert!(result.emit_result.as_ref().unwrap().success, "fallback emission must succeed");
let buf = outputs.lock().await;
assert_eq!(buf.len(), 1);
let RefinementOutput::TextInsertion { text, .. } = &buf[0] else {
panic!("expected TextInsertion fallback");
};
assert_eq!(
text, "Raw dictation text.",
"the fallback must keep the Tier 1+2 work, not revert to raw ASR"
);
assert_ne!(
text, &result.raw_text,
"falling back to the raw transcript would discard successful stages"
);
assert!(
result
.diagnostics
.failures
.iter()
.any(|f| f.stage == Stage::Refiner),
"the refiner failure should be reported, got {:?}",
result.diagnostics.failures
);
}
#[tokio::test]
async fn silent_asr_produces_no_speech_error() {
let pipeline = Pipeline::builder()
.asr(SilentAsr)
.refiner(MockRefiner::passthrough())
.context(MockContextProvider::new())
.emitter(MockEmitter::new())
.build()
.unwrap();
let session = pipeline.session();
send_one(&session.audio).await;
let err = session
.finish()
.await
.expect_err("empty ASR output must surface an error");
assert!(
matches!(err, PipelineError::NoSpeech),
"expected NoSpeech, got: {err}"
);
}