Fix usage terminal state regression

This commit is contained in:
fawney19
2026-04-28 10:31:16 +08:00
parent 3f1abb6906
commit 0c9b5ddd77
2 changed files with 50 additions and 3 deletions

View File

@@ -3041,6 +3041,45 @@ mod tests {
assert_eq!(stored.total_tokens, 10);
}
#[tokio::test]
async fn stale_pending_update_does_not_reopen_void_failure() {
let repository = InMemoryUsageReadRepository::default();
repository
.upsert(UpsertUsageRecord {
status: "failed".to_string(),
billing_status: "void".to_string(),
status_code: Some(503),
error_message: Some("provider timeout".to_string()),
error_category: Some("provider_error".to_string()),
response_time_ms: Some(90),
finalized_at_unix_secs: Some(101),
created_at_unix_ms: Some(100),
updated_at_unix_secs: 101,
..sample_upsert_usage_record("req-void-failure-1")
})
.await
.expect("failed usage should upsert");
repository
.upsert(UpsertUsageRecord {
created_at_unix_ms: Some(100),
updated_at_unix_secs: 102,
..sample_upsert_usage_record("req-void-failure-1")
})
.await
.expect("stale pending usage should upsert");
let stored = repository
.find_by_request_id("req-void-failure-1")
.await
.expect("usage lookup should succeed")
.expect("usage should exist");
assert_eq!(stored.status, "failed");
assert_eq!(stored.billing_status, "void");
assert_eq!(stored.status_code, Some(503));
assert_eq!(stored.finalized_at_unix_secs, Some(101));
}
#[tokio::test]
async fn stale_pending_update_does_not_regress_streaming_usage() {
let repository = InMemoryUsageReadRepository::default();

View File

@@ -171,7 +171,9 @@ pub(crate) fn incoming_usage_can_recover_terminal_failure(
incoming_billing_status: &str,
) -> bool {
incoming_billing_status == "pending"
&& matches!(incoming_status, "pending" | "streaming" | "completed")
// Late pending placeholders are not authoritative enough to reopen a void terminal row;
// they can otherwise regress a real failure back to pending when background writes race.
&& matches!(incoming_status, "streaming" | "completed")
}
pub(crate) fn usage_can_recover_terminal_failure(
@@ -374,7 +376,7 @@ mod tests {
}
#[test]
fn incoming_usage_recovery_only_applies_to_pending_lifecycle_states() {
fn incoming_usage_recovery_requires_streaming_or_completed_state() {
assert!(incoming_usage_can_recover_terminal_failure(
"completed",
"pending"
@@ -383,6 +385,9 @@ mod tests {
"streaming",
"pending"
));
assert!(!incoming_usage_can_recover_terminal_failure(
"pending", "pending"
));
assert!(!incoming_usage_can_recover_terminal_failure(
"failed", "void"
));
@@ -393,7 +398,7 @@ mod tests {
}
#[test]
fn usage_recovery_requires_void_failure_to_be_followed_by_pending_lifecycle_state() {
fn usage_recovery_requires_void_failure_to_be_followed_by_streaming_or_completed_state() {
assert!(usage_can_recover_terminal_failure(
"failed",
"void",
@@ -406,6 +411,9 @@ mod tests {
"streaming",
"pending"
));
assert!(!usage_can_recover_terminal_failure(
"failed", "void", "pending", "pending"
));
assert!(!usage_can_recover_terminal_failure(
"completed",
"pending",