lean_ctx/core/context_kernel/
envelope_e2e.rs1#[cfg(test)]
4mod tests {
5 use std::sync::MutexGuard;
6
7 use super::super::accounting_fix;
8 use super::super::live_dashboard;
9 use super::super::mcp_bridge::{self, McpCallData};
10 use super::super::proxy_bridge::{self, ProxyRequestData};
11 use super::super::receipt_chain;
12 use super::super::token_envelope::{self, ProviderKind, TokenEnvelope};
13 use super::super::types::ReceiptOutcome;
14 use super::super::usage_normalizer;
15
16 fn isolated_test() -> MutexGuard<'static, ()> {
17 let guard = crate::core::context_kernel::kernel_config::KERNEL_TEST_LOCK
18 .lock()
19 .unwrap_or_else(std::sync::PoisonError::into_inner);
20 proxy_bridge::reset_state();
21 mcp_bridge::reset_mcp_state();
22 usage_normalizer::reset_usage();
23 receipt_chain::reset_chain();
24 crate::core::context_kernel::kernel_config::reset_features();
25 guard
26 }
27
28 fn proxy_request(model: &str, input_tokens: usize, tokens_saved: usize) -> ProxyRequestData {
29 ProxyRequestData {
30 headers: vec![("x-user-id".to_owned(), "e2e-user".to_owned())],
31 input_tokens,
32 output_tokens: 100,
33 reasoning_tokens: 25,
34 tokens_saved,
35 model: Some(model.to_owned()),
36 provider: Some("OpenAI".to_owned()),
37 request_count: 1,
38 ..ProxyRequestData::default()
39 }
40 }
41
42 fn mcp_call(tool_name: &str) -> McpCallData {
43 McpCallData {
44 tool_name: tool_name.to_owned(),
45 input_tokens: 400,
46 output_tokens: 80,
47 is_retry: false,
48 call_number: 1,
49 }
50 }
51
52 fn accounting() -> accounting_fix::PostDeliveryAccounting {
53 accounting_fix::compute_honest_accounting(1_000, 700, 50, 20)
54 }
55
56 fn record_receipt(envelope: &TokenEnvelope, outcome: ReceiptOutcome, kernel_hit: bool) {
57 receipt_chain::record_chain_entry(
58 "e2e",
59 envelope.clone(),
60 accounting(),
61 outcome,
62 kernel_hit,
63 usize::from(kernel_hit) * 50,
64 );
65 }
66
67 #[test]
68 fn proxy_to_envelope_to_normalizer() {
69 let _guard = isolated_test();
70 let request = proxy_request("gpt-5", 1_000, 300);
71
72 let envelope = token_envelope::from_proxy_data(&request);
73 assert_eq!(envelope.provider, ProviderKind::OpenAi);
74
75 usage_normalizer::record_envelope(&envelope);
76 assert_eq!(usage_normalizer::session_usage().total_requests, 1);
77 assert!(
78 usage_normalizer::model_breakdown()
79 .iter()
80 .any(|(model, _)| model == "gpt-5")
81 );
82 }
83
84 #[test]
85 fn mcp_to_envelope_pipeline() {
86 let _guard = isolated_test();
87 let call = mcp_call("ctx_read");
88
89 let envelope = token_envelope::from_mcp_call(&call);
90 assert_eq!(envelope.provider, ProviderKind::Unknown);
91 assert!(envelope.model.is_empty(), "MCP calls have no model");
92
93 usage_normalizer::record_envelope(&envelope);
94 assert_eq!(usage_normalizer::session_usage().total_requests, 1);
95 }
96
97 #[test]
98 fn full_receipt_chain() {
99 let _guard = isolated_test();
100 for index in 0..5 {
101 let envelope = token_envelope::from_proxy_data(&proxy_request("gpt-5", 1_000, 300));
102 let accepted = index < 3;
103 let outcome = if accepted {
104 ReceiptOutcome::Accepted
105 } else {
106 ReceiptOutcome::Rejected
107 };
108 record_receipt(&envelope, outcome, accepted);
109 }
110
111 let summary = receipt_chain::chain_summary();
112 assert_eq!(summary.total_entries, 5);
113 assert_eq!(summary.accepted, 3);
114 assert_eq!(summary.rejected, 2);
115 let kernel_hit_rate = summary.kernel_supplemented as f64 / summary.total_entries as f64;
116 assert!((kernel_hit_rate - 0.6).abs() < f64::EPSILON);
117 }
118
119 #[test]
120 fn dashboard_aggregates_all() {
121 let _guard = isolated_test();
122 for _ in 0..3 {
123 let request = proxy_request("gpt-5", 1_000, 300);
124 let _ = proxy_bridge::process_proxy_request(&request);
125 let envelope = token_envelope::from_proxy_data(&request);
126 usage_normalizer::record_envelope(&envelope);
127 record_receipt(&envelope, ReceiptOutcome::Accepted, true);
128 }
129 for tool in ["ctx_read", "ctx_search"] {
130 let call = mcp_call(tool);
131 mcp_bridge::record_mcp_call(&call);
132 let envelope = token_envelope::from_mcp_call(&call);
133 usage_normalizer::record_envelope(&envelope);
134 record_receipt(&envelope, ReceiptOutcome::Accepted, true);
135 }
136
137 let snapshot = live_dashboard::snapshot();
138 assert!(snapshot.proxy_etpao > 0.0);
139 assert_eq!(snapshot.usage.total_requests, 5);
140 assert_eq!(snapshot.chain.total_entries, 5);
141 }
142
143 #[test]
144 fn compression_overview_accurate() {
145 let _guard = isolated_test();
146 for _ in 0..5 {
147 usage_normalizer::record_envelope(&token_envelope::from_proxy_data(&proxy_request(
148 "gpt-5-mini",
149 1_000,
150 100,
151 )));
152 usage_normalizer::record_envelope(&token_envelope::from_proxy_data(&proxy_request(
153 "gpt-5", 1_000, 500,
154 )));
155 }
156
157 let overview = usage_normalizer::compression_overview();
158 let expected = 3_000.0 / 13_000.0;
159 assert!((overview.avg_compression_ratio - expected).abs() < 1.0e-9);
160 assert_eq!(overview.best_model.as_deref(), Some("gpt-5"));
161 }
162
163 #[test]
164 fn receipt_chain_honest_accounting() {
165 let _guard = isolated_test();
166 let envelope = token_envelope::from_proxy_data(&proxy_request("gpt-5", 1_000, 300));
167 let accounting = accounting_fix::compute_honest_accounting(1_000, 600, 250, 100);
168 receipt_chain::record_chain_entry(
169 "proxy",
170 envelope,
171 accounting,
172 ReceiptOutcome::Accepted,
173 true,
174 250,
175 );
176
177 assert!(receipt_chain::chain_summary().total_phantom_savings_pct > 0.0);
178 }
179
180 #[test]
181 fn end_to_end_evidence_pipeline() {
182 let _guard = isolated_test();
183 let request = proxy_request("gpt-5", 1_000, 300);
184 let _ = proxy_bridge::process_proxy_request(&request);
185 let envelope = token_envelope::from_proxy_data(&request);
186 usage_normalizer::record_envelope(&envelope);
187 record_receipt(&envelope, ReceiptOutcome::Accepted, true);
188
189 let snapshot = live_dashboard::snapshot();
190 assert_eq!(snapshot.usage.total_requests, 1);
191 assert_eq!(snapshot.usage.total_tokens, envelope.total_tokens());
192 assert_eq!(snapshot.chain.total_entries, 1);
193 assert_eq!(snapshot.chain.accepted, 1);
194 assert!(snapshot.proxy_etpao > 0.0);
195 }
196
197 #[test]
198 fn envelope_merge_is_additive() {
199 let _guard = isolated_test();
200 let envelopes = [
201 token_envelope::from_proxy_data(&proxy_request("gpt-5", 100, 10)),
202 token_envelope::from_proxy_data(&proxy_request("gpt-5", 200, 20)),
203 token_envelope::from_proxy_data(&proxy_request("gpt-5", 300, 30)),
204 ];
205 let expected = envelopes
206 .iter()
207 .map(TokenEnvelope::total_tokens)
208 .sum::<usize>();
209
210 let merged = TokenEnvelope::merge(&envelopes);
211 assert_eq!(merged.total_tokens(), expected);
212 }
213}