test(storage): pin FTS5 contract for search_transcripts
search_transcripts already backs onto the transcripts_fts virtual table (migration v4, trigger-maintained) via MATCH + ORDER BY rank. Adding a test to lock the behaviour: token matching is case-insensitive, rank- ordered, and non-matching tokens return nothing. This is Phase G of the post-OpenWhispr audit — semantic embeddings stay deferred until the FTS experience actually hits a wall. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -888,6 +888,61 @@ mod tests {
|
||||
assert!(deleted.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search_transcripts_uses_fts5_and_ranks_by_relevance() {
|
||||
// Locks in the FTS5 behaviour contract: MATCH-based substring-like
|
||||
// search (case-insensitive tokens) with rank-ordered results, so
|
||||
// anyone swapping `search_transcripts` out for embeddings later
|
||||
// knows what invariants they must preserve.
|
||||
let pool = test_pool().await;
|
||||
|
||||
let rows = [
|
||||
("t1", "The Parakeet speech model runs on sherpa-onnx."),
|
||||
(
|
||||
"t2",
|
||||
"Parakeet parakeet parakeet dominates English benchmarks.",
|
||||
),
|
||||
("t3", "Whisper large-v3 remains the multilingual champion."),
|
||||
];
|
||||
|
||||
for (id, text) in rows {
|
||||
insert_transcript(
|
||||
&pool,
|
||||
&InsertTranscriptParams {
|
||||
id,
|
||||
text,
|
||||
source: "microphone",
|
||||
profile_id: crate::DEFAULT_PROFILE_ID,
|
||||
title: None,
|
||||
audio_path: None,
|
||||
duration: 1.0,
|
||||
engine: Some("whisper"),
|
||||
model_id: Some("whisper-tiny-en"),
|
||||
inference_ms: None,
|
||||
sample_rate: None,
|
||||
audio_channels: None,
|
||||
format_mode: None,
|
||||
remove_fillers: false,
|
||||
british_english: false,
|
||||
anti_hallucination: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let hits = search_transcripts(&pool, "parakeet", 10).await.unwrap();
|
||||
let ids: Vec<&str> = hits.iter().map(|row| row.id.as_str()).collect();
|
||||
assert_eq!(
|
||||
ids,
|
||||
vec!["t2", "t1"],
|
||||
"t3 has no 'parakeet' token; t2 outranks t1 on repetition"
|
||||
);
|
||||
|
||||
let no_match = search_transcripts(&pool, "moonshine", 10).await.unwrap();
|
||||
assert!(no_match.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn task_crud_roundtrip() {
|
||||
let pool = test_pool().await;
|
||||
|
||||
Reference in New Issue
Block a user