lean_ctx/core/context_kernel/
wiring_e2e.rs1#[cfg(test)]
4mod tests {
5 use super::super::client_profile::ProfileBuilder;
6 use super::super::client_wiring;
7 use super::super::coverage_class::{self, CoverageClass};
8 use super::super::etpao_live::EtpaoLive;
9 use super::super::identity::{CallerIdentity, CallerRole, IdentityLedger};
10 use super::super::identity_resolver;
11 use super::super::tool_surface::{ToolCategory, ToolSchema, ToolSurfaceOptimizer};
12
13 fn headers(values: &[(&str, &str)]) -> Vec<(String, String)> {
14 values
15 .iter()
16 .map(|(name, value)| ((*name).to_owned(), (*value).to_owned()))
17 .collect()
18 }
19
20 fn tool(index: usize, token_count: usize) -> ToolSchema {
21 ToolSchema {
22 name: format!("tool-{index}"),
23 description: format!("Production tool number {index}"),
24 parameters_json: r#"{"type":"object","properties":{}}"#.to_owned(),
25 token_count,
26 priority: u8::try_from(index).unwrap_or(u8::MAX),
27 category: ToolCategory::Core,
28 }
29 }
30
31 #[test]
32 fn identity_to_ledger_pipeline() {
33 let identity = CallerIdentity {
34 user_id: Some("alice".to_owned()),
35 team_id: Some("backend".to_owned()),
36 role: CallerRole::Developer,
37 ..CallerIdentity::default()
38 };
39 let mut ledger = IdentityLedger::new();
40
41 for _ in 0..5 {
42 ledger.record(&identity, 100, 25, true);
43 }
44
45 let attribution = ledger
46 .attribution_for("alice")
47 .expect("alice must have ledger attribution");
48 assert_eq!(attribution.request_count, 5);
49 }
50
51 #[test]
52 fn headers_to_identity_to_context() {
53 let headers = headers(&[
54 ("x-user-id", "bob"),
55 ("x-team-id", "frontend"),
56 ("x-cost-center", "eng-01"),
57 ]);
58 let identity = identity_resolver::resolve_with_defaults(&headers);
59 let context = client_wiring::build_request_context(&headers, true, false, false);
60
61 assert_eq!(identity, context.identity);
62 assert_eq!(context.identity.user_id.as_deref(), Some("bob"));
63 assert_eq!(context.identity.cost_center.as_deref(), Some("eng-01"));
64 }
65
66 #[test]
67 fn full_inline_optimizes_tools() {
68 let mut profile = ProfileBuilder::new("inline")
69 .coverage(CoverageClass::FullInline)
70 .build();
71 profile.tool_budget.max_tools = 5;
72 profile.tool_budget.max_schema_tokens = 10_000;
73 let schemas = (0..15)
74 .map(|index| tool(index, 100 + index * 10))
75 .collect::<Vec<_>>();
76
77 let reduction = ToolSurfaceOptimizer::from_profile(&profile).optimize(&schemas);
78
79 assert!(reduction.reduced_count <= 5);
80 assert!(reduction.tokens_saved > 0);
81 }
82
83 #[test]
84 fn unmanaged_no_optimization() {
85 let context = client_wiring::build_request_context(&[], false, false, false);
86
87 assert!(!client_wiring::should_optimize(&context));
88 assert_eq!(
89 client_wiring::optimization_level(&context),
90 client_wiring::OptimizationLevel::None
91 );
92 }
93
94 #[test]
95 fn etpao_tracks_per_identity() {
96 let first = client_wiring::build_request_context(
97 &headers(&[("x-client-id", "client-a")]),
98 true,
99 false,
100 false,
101 );
102 let second = client_wiring::build_request_context(
103 &headers(&[("x-client-id", "client-b")]),
104 false,
105 true,
106 false,
107 );
108 let mut etpao = EtpaoLive::new();
109
110 client_wiring::record_request_etpao(&mut etpao, &first, 800, 200);
111 client_wiring::record_request_etpao(&mut etpao, &second, 600, 150);
112
113 assert_eq!(etpao.request_count(), 2);
114 assert!(etpao.etpao_for_client("client-a").is_some());
115 assert!(etpao.etpao_for_client("client-b").is_some());
116 assert_eq!(etpao.summary().total_tokens, 1_750);
117 }
118
119 #[test]
120 fn tool_surface_savings_realistic() {
121 let schemas = (0..20)
122 .map(|index| tool(index, 100 + index * 20))
123 .collect::<Vec<_>>();
124
125 let reduction = ToolSurfaceOptimizer::new(5, 1_000).optimize(&schemas);
126
127 assert!(reduction.reduced_count <= 5);
128 assert!(reduction.reduced_tokens <= 1_000);
129 assert!(reduction.savings_pct > 50.0);
130 }
131
132 #[test]
133 fn end_to_end_request_lifecycle() {
134 let headers = headers(&[
135 ("x-client-id", "lifecycle-client"),
136 ("x-user-id", "carol"),
137 ("x-team-id", "platform"),
138 ("x-cost-center", "eng-02"),
139 ]);
140 let identity = identity_resolver::resolve_with_defaults(&headers);
141 let coverage = coverage_class::detect_coverage(true, false, false);
142 let mut context = client_wiring::build_request_context(&headers, true, false, false);
143 context.profile.tool_budget.max_tools = 3;
144 context.profile.tool_budget.max_schema_tokens = 600;
145 let schemas = (0..8)
146 .map(|index| tool(index, 100 + index * 10))
147 .collect::<Vec<_>>();
148 let reduction = ToolSurfaceOptimizer::from_profile(&context.profile).optimize(&schemas);
149 let mut etpao = EtpaoLive::new();
150 let mut ledger = IdentityLedger::new();
151
152 client_wiring::record_request_etpao(&mut etpao, &context, 1_000, 250);
153 client_wiring::record_outcome_etpao(&mut etpao, &context, true, 0.95);
154 ledger.record(&identity, 1_250, reduction.tokens_saved, true);
155
156 assert_eq!(identity, context.identity);
157 assert_eq!(coverage, CoverageClass::FullInline);
158 assert_eq!(context.profile.coverage, coverage);
159 assert!(context.broker_budget.context_tokens > 0);
160 assert!(!reduction.selected_tools.is_empty());
161 assert!(ledger.total_tokens() > 0);
162 assert!(etpao.current_etpao() > 0.0);
163 }
164}