Skip to main content

lean_ctx/core/context_kernel/
wiring_e2e.rs

1//! End-to-end conformance tests for request identity and client wiring.
2
3#[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}