#[tokio::test]
async fn rerank_executed_falls_back_to_reranked_when_final_scores_field_is_absent() {
let store = setup_memory_store();
let candidate = Uuid::new_v4();
let reranked_winner = Uuid::new_v4();
let mut event = make_event("default");
event.kind = EventKind::RerankExecuted;
event.payload = json!({
"candidates": [candidate.to_string()],
"reranked": [[reranked_winner.to_string(), [["relevance", 0.9]]]],
});
let event_id = event.id;
store.append_event(event).await.unwrap();
let selected = selected_uuids_for(&store, event_id).await;
assert_eq!(
selected,
vec![reranked_winner.to_string()],
"selected observation must decode the UUID leading the `reranked` tuple \
when `final_scores` is absent"
);
}
#[tokio::test]
async fn rerank_executed_prefers_final_scores_order_over_reranked_when_both_present() {
let store = setup_memory_store();
let a = Uuid::new_v4();
let b = Uuid::new_v4();
let payload = khive_types::RerankExecutedPayload {
served_by_profile_id: Some("profile-a".to_string()),
reranker: khive_types::RerankerKind::Native,
model_id: Some(khive_types::Id128::from_u128(1)),
query_id: None,
tiers: Vec::new(),
ignored_weights: Vec::new(),
unidentified_candidates: 0,
candidates: vec![
khive_types::Id128::from_bytes(*a.as_bytes()),
khive_types::Id128::from_bytes(*b.as_bytes()),
],
reranked: vec![
(
khive_types::Id128::from_bytes(*b.as_bytes()),
vec![("relevance".to_string(), 0.4)],
),
(
khive_types::Id128::from_bytes(*a.as_bytes()),
vec![("relevance".to_string(), 0.9)],
),
],
final_scores: vec![
(khive_types::Id128::from_bytes(*a.as_bytes()), 0.9),
(khive_types::Id128::from_bytes(*b.as_bytes()), 0.4),
],
latency_us: 1200,
hook_applied: false,
hook_target_match: false,
};
let mut event = make_event("default");
event.kind = EventKind::RerankExecuted;
event.payload = serde_json::to_value(&payload).unwrap();
let event_id = event.id;
store.append_event(event).await.unwrap();
let selected = selected_uuids_for(&store, event_id).await;
assert_eq!(
selected,
vec![a.to_string(), b.to_string()],
"selected order must follow `final_scores`, not `reranked`"
);
}
#[tokio::test]
async fn rerank_executed_ignores_stray_selected_field_and_uses_final_scores() {
let store = setup_memory_store();
let a = Uuid::new_v4();
let b = Uuid::new_v4();
let stray_selected_winner = Uuid::new_v4();
let payload = khive_types::RerankExecutedPayload {
served_by_profile_id: Some("profile-a".to_string()),
reranker: khive_types::RerankerKind::Native,
model_id: Some(khive_types::Id128::from_u128(1)),
query_id: None,
tiers: Vec::new(),
ignored_weights: Vec::new(),
unidentified_candidates: 0,
candidates: vec![
khive_types::Id128::from_bytes(*a.as_bytes()),
khive_types::Id128::from_bytes(*b.as_bytes()),
],
reranked: vec![],
final_scores: vec![
(khive_types::Id128::from_bytes(*a.as_bytes()), 0.9),
(khive_types::Id128::from_bytes(*b.as_bytes()), 0.4),
],
latency_us: 1200,
hook_applied: false,
hook_target_match: false,
};
let mut event = make_event("default");
event.kind = EventKind::RerankExecuted;
let mut payload_value = serde_json::to_value(&payload).unwrap();
payload_value.as_object_mut().unwrap().insert(
"selected".to_string(),
json!([stray_selected_winner.to_string()]),
);
event.payload = payload_value;
let event_id = event.id;
store.append_event(event).await.unwrap();
let selected = selected_uuids_for(&store, event_id).await;
assert_eq!(
selected,
vec![a.to_string(), b.to_string()],
"stray `selected` field must be ignored for RerankExecuted; \
projection must follow `final_scores` only"
);
}
#[tokio::test]
async fn rerank_executed_uses_final_scores_when_reranked_is_empty() {
let store = setup_memory_store();
let winner = Uuid::new_v4();
let payload = khive_types::RerankExecutedPayload {
served_by_profile_id: None,
reranker: khive_types::RerankerKind::Native,
model_id: Some(khive_types::Id128::from_u128(1)),
query_id: None,
tiers: Vec::new(),
ignored_weights: Vec::new(),
unidentified_candidates: 0,
candidates: vec![khive_types::Id128::from_bytes(*winner.as_bytes())],
reranked: vec![],
final_scores: vec![(khive_types::Id128::from_bytes(*winner.as_bytes()), 0.75)],
latency_us: 800,
hook_applied: false,
hook_target_match: false,
};
let mut event = make_event("default");
event.kind = EventKind::RerankExecuted;
event.payload = serde_json::to_value(&payload).unwrap();
let event_id = event.id;
store.append_event(event).await.unwrap();
let selected = selected_uuids_for(&store, event_id).await;
assert_eq!(
selected,
vec![winner.to_string()],
"final_scores must be used even when reranked is present-but-empty"
);
}
#[tokio::test]
async fn rerank_executed_final_scores_single_element_tuple_rejected() {
let store = setup_memory_store();
let mut event = make_event("default");
event.kind = EventKind::RerankExecuted;
event.payload = json!({
"candidates": [],
"final_scores": [[Uuid::new_v4().to_string()]],
});
let event_id = event.id;
let result = store.append_event(event).await;
assert!(result.is_err(), "single-element tuple must be rejected");
let fetched = store.get_event(event_id).await.unwrap();
assert!(fetched.is_none(), "event row must not exist after rollback");
}
#[tokio::test]
async fn rerank_executed_final_scores_extra_element_tuple_rejected() {
let store = setup_memory_store();
let mut event = make_event("default");
event.kind = EventKind::RerankExecuted;
event.payload = json!({
"candidates": [],
"final_scores": [[Uuid::new_v4().to_string(), 0.5, "extra"]],
});
let event_id = event.id;
let result = store.append_event(event).await;
assert!(result.is_err(), "extra-element tuple must be rejected");
let fetched = store.get_event(event_id).await.unwrap();
assert!(fetched.is_none(), "event row must not exist after rollback");
}
#[tokio::test]
async fn rerank_executed_final_scores_non_numeric_second_element_rejected() {
let store = setup_memory_store();
let mut event = make_event("default");
event.kind = EventKind::RerankExecuted;
event.payload = json!({
"candidates": [],
"final_scores": [[Uuid::new_v4().to_string(), "not-a-number"]],
});
let event_id = event.id;
let result = store.append_event(event).await;
assert!(result.is_err(), "non-numeric score must be rejected");
let fetched = store.get_event(event_id).await.unwrap();
assert!(fetched.is_none(), "event row must not exist after rollback");
}
#[tokio::test]
async fn rerank_executed_reranked_malformed_sub_scores_rejected() {
let store = setup_memory_store();
let mut event = make_event("default");
event.kind = EventKind::RerankExecuted;
event.payload = json!({
"candidates": [],
"reranked": [[Uuid::new_v4().to_string(), "not-an-array"]],
});
let event_id = event.id;
let result = store.append_event(event).await;
assert!(
result.is_err(),
"malformed reranked sub-scores must be rejected"
);
let fetched = store.get_event(event_id).await.unwrap();
assert!(fetched.is_none(), "event row must not exist after rollback");
}