use super::*;
use crate::embed::cache::key::{CacheKey, Normalization, Pooling};
use crate::embed::protocol::{
BatchId, CacheProbeRequest, CancelRequest, EmbedRequest, Frame, MsgType, Response, WorkerState,
};
use std::sync::atomic::Ordering;
fn make_key(text: &str) -> CacheKey {
CacheKey {
model_digest: CacheKey::model_digest(b"test-model"),
tokenizer_digest: CacheKey::tokenizer_digest(b"test-tokenizer"),
prompt_role_and_version: 0,
pooling: Pooling::Mean,
normalization: Normalization::L2,
output_dimensions: 4,
content_hash: CacheKey::content_hash(text),
}
}
fn make_key_with_dim(text: &str, dim: u32) -> CacheKey {
CacheKey {
output_dimensions: dim,
..make_key(text)
}
}
fn no_compile_config() -> RuntimeConfig {
RuntimeConfig {
model_name: "__leindex_test_no_model__".to_string(),
rerank_model_name: "__leindex_test_no_rerank_model__".to_string(),
..RuntimeConfig::default()
}
}
#[test]
fn test_health_response_has_digest_fields() {
let rt = WorkerRuntime::new(no_compile_config());
let health = rt.health_response(WorkerState::Ready, None);
assert!(health.model_digest.is_none() || health.model_digest.is_some());
assert!(health.tokenizer_digest.is_none() || health.tokenizer_digest.is_some());
assert!(
health.config_digest.is_some(),
"config digest should be computed"
);
}
#[cfg(target_os = "linux")]
#[test]
fn test_health_response_has_host_rss() {
let rt = WorkerRuntime::new(no_compile_config());
let health = rt.health_response(WorkerState::Ready, None);
assert!(
health.host_rss_mib.is_some(),
"host_rss_mib should be reported on Linux"
);
assert!(
health.host_rss_mib.unwrap() > 0,
"host_rss_mib should be positive"
);
}
#[test]
fn test_health_response_roundtrip_with_digests() {
let rt = WorkerRuntime::new(no_compile_config());
let health = rt.health_response(WorkerState::Ready, None);
let frame = protocol::health_response_frame(BatchId::new(1), health).unwrap();
let wire = frame.encode_wire().unwrap();
let decoded = Frame::from_wire_bytes(&wire[4..]).unwrap();
let resp: Response = decoded.decode_payload().unwrap();
match resp {
Response::Health(h) => {
assert_eq!(h.state, WorkerState::Ready);
assert!(h.config_digest.is_some());
}
_ => panic!("expected health response"),
}
}
#[test]
fn test_embed_with_cache_keys_accepted() {
let rt = WorkerRuntime::new(no_compile_config());
let texts = vec![
"text alpha".to_string(),
"text beta".to_string(),
"text gamma".to_string(),
];
let dim = 4usize;
let keys: Vec<CacheKey> = texts
.iter()
.map(|t| make_key_with_dim(t, dim as u32))
.collect();
let request = EmbedRequest {
texts,
expected_dim: dim,
cache_keys: keys,
};
let frame = protocol::embed_request_frame(BatchId::new(1), request).unwrap();
let response = rt.dispatch(&frame);
assert!(
response.header.msg_type == MsgType::Error
|| response.header.msg_type == MsgType::EmbedResponse,
"expected Error or EmbedResponse, got {:?}",
response.header.msg_type
);
}
#[test]
fn test_embed_with_cache_all_hits_ordering() {
let tmp = tempfile::tempdir().unwrap();
let mut cache = crate::embed::cache::GlobalEmbeddingCache::open(tmp.path()).unwrap();
let texts = vec![
"hit one".to_string(),
"hit two".to_string(),
"hit three".to_string(),
];
let dim = 4u32;
let keys: Vec<CacheKey> = texts.iter().map(|t| make_key_with_dim(t, dim)).collect();
let vec0 = vec![0.10, 0.11, 0.12, 0.13];
let vec1 = vec![0.20, 0.21, 0.22, 0.23];
let vec2 = vec![0.30, 0.31, 0.32, 0.33];
cache.put(&keys[0], &vec0, None).unwrap();
cache.put(&keys[1], &vec1, None).unwrap();
cache.put(&keys[2], &vec2, None).unwrap();
let mut rt = WorkerRuntime::new(no_compile_config());
rt.cache = Some(std::sync::Arc::new(std::sync::Mutex::new(cache)));
let request = EmbedRequest {
texts: texts.clone(),
expected_dim: dim as usize,
cache_keys: keys.clone(),
};
let frame = protocol::embed_request_frame(BatchId::new(1), request).unwrap();
let response = rt.dispatch(&frame);
assert_eq!(response.header.msg_type, MsgType::EmbedResponse);
let decoded: Response = response.decode_payload().unwrap();
match decoded {
Response::Embed(embed_resp) => {
assert_eq!(embed_resp.count, 3);
assert_eq!(embed_resp.dimension, dim as usize);
assert_eq!(embed_resp.get_embedding(0).unwrap(), &vec0[..]);
assert_eq!(embed_resp.get_embedding(1).unwrap(), &vec1[..]);
assert_eq!(embed_resp.get_embedding(2).unwrap(), &vec2[..]);
}
_ => panic!("expected embed response"),
}
}
#[test]
fn test_embed_all_cache_hits_no_inference() {
let tmp = tempfile::tempdir().unwrap();
let mut cache = crate::embed::cache::GlobalEmbeddingCache::open(tmp.path()).unwrap();
let texts = vec!["hit a".to_string(), "hit b".to_string()];
let dim = 4u32;
let keys: Vec<CacheKey> = texts.iter().map(|t| make_key_with_dim(t, dim)).collect();
let vec_a = vec![1.0, 2.0, 3.0, 4.0];
let vec_b = vec![5.0, 6.0, 7.0, 8.0];
cache.put(&keys[0], &vec_a, None).unwrap();
cache.put(&keys[1], &vec_b, None).unwrap();
let mut rt = WorkerRuntime::new(no_compile_config());
rt.cache = Some(std::sync::Arc::new(std::sync::Mutex::new(cache)));
let request = EmbedRequest {
texts,
expected_dim: dim as usize,
cache_keys: keys,
};
let frame = protocol::embed_request_frame(BatchId::new(1), request).unwrap();
let response = rt.dispatch(&frame);
let decoded: Response = response.decode_payload().unwrap();
match decoded {
Response::Embed(embed_resp) => {
assert_eq!(embed_resp.count, 2);
assert_eq!(embed_resp.get_embedding(0).unwrap(), &vec_a[..]);
assert_eq!(embed_resp.get_embedding(1).unwrap(), &vec_b[..]);
}
_ => panic!("expected embed response"),
}
}
#[test]
fn test_embed_cache_keys_length_mismatch() {
let rt = WorkerRuntime::new(no_compile_config());
let texts = vec!["a".to_string(), "b".to_string()];
let keys = vec![make_key("a")];
let request = EmbedRequest {
texts,
expected_dim: 4,
cache_keys: keys,
};
let frame = protocol::embed_request_frame(BatchId::new(1), request).unwrap();
let response = rt.dispatch(&frame);
assert!(
response.header.msg_type == MsgType::Error
|| response.header.msg_type == MsgType::EmbedResponse
);
if response.header.msg_type == MsgType::Error {
let _: Response = response.decode_payload().unwrap();
}
}
#[test]
fn test_cancel_targets_registered_batch() {
let rt = WorkerRuntime::new(no_compile_config());
let (_guard, token) = rt.register_embed_cancel(BatchId::new(42)).unwrap();
assert!(!token.load(Ordering::Acquire));
let cancel_req = CancelRequest {
reason: "test cancellation".to_string(),
};
let frame = protocol::cancel_request_frame(BatchId::new(42), cancel_req).unwrap();
let response = rt.dispatch(&frame);
assert!(token.load(Ordering::Acquire));
assert_eq!(response.header.msg_type, MsgType::Cancel);
let decoded: Response = response.decode_payload().unwrap();
match decoded {
Response::Cancel(resp) => assert!(resp.acknowledged),
_ => panic!("expected cancel response"),
}
}
#[test]
fn test_cancel_unknown_batch_is_acknowledged_noop() {
let rt = WorkerRuntime::new(no_compile_config());
let frame = protocol::cancel_request_frame(
BatchId::new(404),
CancelRequest {
reason: "unknown".to_string(),
},
)
.unwrap();
let response = rt.dispatch(&frame);
assert_eq!(response.header.msg_type, MsgType::Cancel);
assert!(
rt.active_embed_cancels
.lock()
.unwrap_or_else(|p| p.into_inner())
.is_empty()
);
}
#[test]
fn test_duplicate_active_batch_id_is_rejected_and_guard_cleans_up() {
let rt = WorkerRuntime::new(no_compile_config());
let (guard, _) = rt.register_embed_cancel(BatchId::new(7)).unwrap();
let duplicate = match rt.register_embed_cancel(BatchId::new(7)) {
Ok(_) => panic!("duplicate active batch id must be rejected"),
Err(error) => error,
};
assert_eq!(duplicate.kind, ErrorKind::InvalidRequest);
drop(guard);
assert!(rt.register_embed_cancel(BatchId::new(7)).is_ok());
}
#[test]
fn test_cache_probe_rpc() {
let tmp = tempfile::tempdir().unwrap();
let mut cache = crate::embed::cache::GlobalEmbeddingCache::open(tmp.path()).unwrap();
let dim = 4u32;
let keys: Vec<CacheKey> = (0..3)
.map(|i| make_key_with_dim(&format!("text {i}"), dim))
.collect();
cache.put(&keys[0], &[0.1, 0.2, 0.3, 0.4], None).unwrap();
cache.put(&keys[2], &[0.5, 0.6, 0.7, 0.8], None).unwrap();
let mut rt = WorkerRuntime::new(no_compile_config());
rt.cache = Some(std::sync::Arc::new(std::sync::Mutex::new(cache)));
let probe_req = CacheProbeRequest { keys: keys.clone() };
let frame = protocol::cache_probe_request_frame(BatchId::new(1), probe_req).unwrap();
let response = rt.dispatch(&frame);
assert_eq!(response.header.msg_type, MsgType::CacheProbeResponse);
let decoded: Response = response.decode_payload().unwrap();
match decoded {
Response::CacheProbe(probe_resp) => {
let mut hits = probe_resp.hit_indices.clone();
hits.sort();
let mut misses = probe_resp.miss_indices.clone();
misses.sort();
assert_eq!(hits, vec![0, 2]);
assert_eq!(misses, vec![1]);
}
_ => panic!("expected cache probe response"),
}
}
#[test]
fn test_cache_probe_no_cache() {
let rt = WorkerRuntime::new(no_compile_config());
let key = make_key("text");
let probe_req = CacheProbeRequest { keys: vec![key; 5] };
let frame = protocol::cache_probe_request_frame(BatchId::new(1), probe_req).unwrap();
let response = rt.dispatch(&frame);
assert_eq!(response.header.msg_type, MsgType::CacheProbeResponse);
let decoded: Response = response.decode_payload().unwrap();
match decoded {
Response::CacheProbe(probe_resp) => {
assert!(probe_resp.hit_indices.is_empty());
assert_eq!(probe_resp.miss_indices.len(), 5);
}
_ => panic!("expected cache probe response"),
}
}
#[test]
fn test_cache_gc_with_project_references() {
let tmp = tempfile::tempdir().unwrap();
let mut cache = crate::embed::cache::GlobalEmbeddingCache::open(tmp.path()).unwrap();
let dim = 4u32;
let key_live = make_key_with_dim("live", dim);
let key_dead = make_key_with_dim("dead", dim);
cache.put(&key_live, &[1.0, 2.0, 3.0, 4.0], None).unwrap();
cache.put(&key_dead, &[5.0, 6.0, 7.0, 8.0], None).unwrap();
let fp_live = key_live.fingerprint();
cache.add_reference(&fp_live, "project-a", 1);
let report = cache.gc().unwrap();
assert_eq!(report.rows_removed, 1, "dead row should be removed");
assert_eq!(report.rows_retained, 1, "live row should be retained");
assert!(report.reclaimed_bytes > 0, "should report reclaimed bytes");
assert_eq!(cache.row_count().unwrap(), 1);
assert!(cache.get(&key_live).unwrap().is_some());
assert!(cache.get(&key_dead).unwrap().is_none());
}
#[test]
fn test_cache_hit_vectors_bit_identical() {
let tmp = tempfile::tempdir().unwrap();
let mut cache = crate::embed::cache::GlobalEmbeddingCache::open(tmp.path()).unwrap();
let key = make_key_with_dim("bit identical test", 4);
let original = vec![0.123456, -0.654321, 1.0, 0.0];
cache.put(&key, &original, None).unwrap();
let cached = cache.get(&key).unwrap().unwrap();
for (a, b) in original.iter().zip(cached.iter()) {
assert_eq!(a.to_bits(), b.to_bits(), "f32 bits must match exactly");
}
}
#[test]
fn test_val_cache_013_crash_isolation_cache_consistency() {
let tmp = tempfile::tempdir().unwrap();
let cache_root = tmp.path().join("cache");
{
let mut cache = crate::embed::cache::GlobalEmbeddingCache::open(&cache_root).unwrap();
let key1 = make_key_with_dim("crash test entry 1", 4);
cache.put(&key1, &[1.0, 2.0, 3.0, 4.0], None).unwrap();
}
let mut cache = crate::embed::cache::GlobalEmbeddingCache::open(&cache_root).unwrap();
let key1 = make_key_with_dim("crash test entry 1", 4);
let recovered = cache.get(&key1).unwrap();
assert!(recovered.is_some(), "cached entry must survive crash");
assert_eq!(recovered.unwrap(), vec![1.0, 2.0, 3.0, 4.0]);
let row_count_before = cache.row_count().unwrap();
cache.put(&key1, &[1.0, 2.0, 3.0, 4.0], None).unwrap();
assert_eq!(
cache.row_count().unwrap(),
row_count_before,
"idempotent retry must not duplicate rows"
);
}
#[test]
fn test_val_cache_013_no_zombie_partial_rows() {
let tmp = tempfile::tempdir().unwrap();
let cache = crate::embed::cache::GlobalEmbeddingCache::open(tmp.path()).unwrap();
let key = make_key_with_dim("partial test", 4);
let fp = key.fingerprint();
let path = cache.row_path(&fp);
let staging = path.with_extension("partial");
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&staging, b"incomplete data").unwrap();
assert_eq!(
cache.row_count().unwrap(),
0,
"staging files must not count"
);
let result = cache.get(&key).unwrap();
assert!(result.is_none(), "staging file must not be readable");
}
#[test]
fn test_val_cache_011_idle_without_polling() {
let rt = WorkerRuntime::new(no_compile_config());
assert!(
!rt.is_idle_expired(),
"fresh runtime must not be idle-expired"
);
assert!(
rt.active_embed_cancels
.lock()
.unwrap_or_else(|p| p.into_inner())
.is_empty(),
"no active embed cancellation tokens when idle"
);
let health = rt.health_response(WorkerState::Ready, None);
assert_eq!(health.state, WorkerState::Ready);
}