lean_ctx/core/context_kernel/
provider_metrics_e2e.rs1#[cfg(test)]
2mod tests {
3 use serde_json::{Value, json};
4
5 use super::super::envelope_bridge;
6 use super::super::provider_parity::{detect_provider, envelope_from_usage};
7 use super::super::token_envelope::ProviderKind;
8 use super::super::{evidence_wiring, kernel_config, usage_normalizer};
9
10 fn isolated() -> std::sync::MutexGuard<'static, ()> {
11 let guard = kernel_config::KERNEL_TEST_LOCK
12 .lock()
13 .unwrap_or_else(std::sync::PoisonError::into_inner);
14 kernel_config::reset_features();
15 evidence_wiring::reset();
16 usage_normalizer::reset_usage();
17 envelope_bridge::reset();
18 guard
19 }
20
21 #[allow(clippy::needless_pass_by_value)]
22 fn record(url: &str, expected: ProviderKind, model: &str, usage: Value) {
23 let provider = detect_provider(url);
24 assert_eq!(provider, expected);
25 let envelope = envelope_from_usage(provider, model, &usage);
26 envelope_bridge::record_proxy_envelope(&envelope);
27 }
28
29 macro_rules! provider_e2e {
30 ($name:ident, $url:expr, $kind:expr, $model:expr, $usage:expr, $totals:expr) => {
31 #[test]
32 fn $name() {
33 let _guard = isolated();
34 record($url, $kind, $model, $usage);
35 let stats = envelope_bridge::provider_stats();
36 assert_eq!(stats.len(), 1);
37 assert_eq!(stats[0].provider, $kind);
38 assert_eq!(stats[0].request_count, 1);
39 assert_eq!((stats[0].total_input, stats[0].total_output), $totals);
40 }
41 };
42 }
43
44 provider_e2e!(
45 openai_e2e,
46 "https://api.openai.com",
47 ProviderKind::OpenAi,
48 "gpt-5",
49 json!({"prompt_tokens": 100, "completion_tokens": 25}),
50 (100, 25)
51 );
52 provider_e2e!(
53 anthropic_e2e,
54 "https://api.anthropic.com",
55 ProviderKind::Anthropic,
56 "claude-sonnet",
57 json!({"input_tokens": 80, "output_tokens": 20}),
58 (80, 20)
59 );
60 provider_e2e!(
61 bedrock_e2e,
62 "https://bedrock-runtime.us-east-1.amazonaws.com",
63 ProviderKind::Bedrock,
64 "amazon.nova-pro",
65 json!({"inputTokens": 70, "outputTokens": 15}),
66 (70, 15)
67 );
68 provider_e2e!(
69 azure_e2e,
70 "https://tenant.openai.azure.com",
71 ProviderKind::Azure,
72 "gpt-4.1",
73 json!({"prompt_tokens": 60, "completion_tokens": 10}),
74 (60, 10)
75 );
76
77 #[test]
78 fn multi_provider_e2e() {
79 let _guard = isolated();
80 for _ in 0..3 {
81 record(
82 "https://api.openai.com",
83 ProviderKind::OpenAi,
84 "gpt-5",
85 json!({"prompt_tokens": 10, "completion_tokens": 2}),
86 );
87 }
88 for _ in 0..2 {
89 record(
90 "https://api.anthropic.com",
91 ProviderKind::Anthropic,
92 "claude-sonnet",
93 json!({"input_tokens": 20, "output_tokens": 4}),
94 );
95 }
96 let stats = envelope_bridge::provider_stats();
97 assert_eq!(stats.len(), 2);
98 assert_eq!(
99 (stats[0].provider, stats[0].request_count),
100 (ProviderKind::OpenAi, 3)
101 );
102 assert_eq!(
103 (stats[1].provider, stats[1].request_count),
104 (ProviderKind::Anthropic, 2)
105 );
106 }
107
108 #[test]
109 fn envelope_bridge_records_evidence() {
110 let _guard = isolated();
111 record(
112 "https://api.openai.com",
113 ProviderKind::OpenAi,
114 "gpt-5",
115 json!({
116 "prompt_tokens": 40,
117 "completion_tokens": 12,
118 "completion_tokens_details": {"reasoning_tokens": 7}
119 }),
120 );
121 let summary = evidence_wiring::dispatch_summary();
122 assert!(summary.proxy_dispatches >= 1);
123 assert!(summary.total_input_tokens >= 40);
124 assert!(summary.total_output_tokens >= 12);
125 }
127
128 #[test]
129 fn envelope_bridge_records_usage() {
130 let _guard = isolated();
131 record(
132 "https://api.anthropic.com",
133 ProviderKind::Anthropic,
134 "claude-sonnet",
135 json!({
136 "input_tokens": 30,
137 "output_tokens": 9,
138 "cache_read_input_tokens": 6
139 }),
140 );
141 let usage = usage_normalizer::session_usage();
142 assert!(usage.total_requests >= 1);
143 assert!(usage.total_tokens >= 39);
144 let model = &usage.per_model["claude-sonnet"];
145 assert!(model.input_tokens >= 30);
146 assert!(model.output_tokens >= 9);
147 assert!(model.cache_read_tokens >= 6);
148 }
149
150 #[test]
151 fn reset_isolates() {
152 let _guard = isolated();
153 record(
154 "https://api.openai.com",
155 ProviderKind::OpenAi,
156 "gpt-5",
157 json!({"prompt_tokens": 10, "completion_tokens": 2}),
158 );
159 envelope_bridge::reset();
160 assert!(envelope_bridge::provider_stats().is_empty());
161 }
162}