Skip to main content

lean_ctx/core/context_kernel/
client_e2e.rs

1//! End-to-end conformance tests for the client intelligence layer.
2
3#[cfg(test)]
4mod tests {
5    use super::super::client_profile::ProfileBuilder;
6    use super::super::context_broker::{ContextBroker, ContextMode, OutputFormat, ToolDescriptor};
7    use super::super::coverage_class::{self, CoverageClass};
8    use super::super::etpao_live::{EtpaoLive, OutcomeMetrics, RequestMetrics};
9
10    fn tool(index: usize) -> ToolDescriptor {
11        ToolDescriptor {
12            name: format!("tool-{index}"),
13            schema_tokens: 10,
14            priority: index as u8,
15        }
16    }
17
18    fn request(client_id: &str, tokens: usize, coverage_class: CoverageClass) -> RequestMetrics {
19        RequestMetrics {
20            input_tokens: tokens,
21            output_tokens: 0,
22            reasoning_tokens: 0,
23            schema_tokens: 0,
24            cache_write_tokens: 0,
25            retry_count: 0,
26            client_id: client_id.to_owned(),
27            coverage_class,
28        }
29    }
30
31    fn outcome(client_id: &str, accepted: bool) -> OutcomeMetrics {
32        OutcomeMetrics {
33            accepted,
34            quality_score: 1.0,
35            first_pass: accepted,
36            client_id: client_id.to_owned(),
37        }
38    }
39
40    #[test]
41    fn coverage_detection_full_inline() {
42        assert_eq!(
43            coverage_class::detect_coverage(true, false, false),
44            CoverageClass::FullInline
45        );
46    }
47
48    #[test]
49    fn coverage_detection_mcp_only() {
50        assert_eq!(
51            coverage_class::detect_coverage(false, true, false),
52            CoverageClass::ContextControlled
53        );
54    }
55
56    #[test]
57    fn profile_builder_defaults() {
58        let profile = ProfileBuilder::new("test").build();
59        assert_eq!(profile.client_id, "test");
60        assert!(profile.context_window > 0);
61    }
62
63    #[test]
64    fn broker_filters_tools_to_budget() {
65        let mut profile = ProfileBuilder::new("budgeted").build();
66        profile.tool_budget.max_tools = 5;
67        let broker = ContextBroker::new(profile);
68        let tools = (0..20).map(tool).collect::<Vec<_>>();
69
70        assert!(broker.select_tools(&tools).len() <= 5);
71    }
72
73    #[test]
74    fn broker_selects_handles_for_small_window() {
75        let profile = ProfileBuilder::new("small-window")
76            .context_window(16_000)
77            .build();
78        let broker = ContextBroker::new(profile);
79
80        assert!(broker.should_use_handles());
81    }
82
83    #[test]
84    fn etpao_records_and_computes() {
85        let mut etpao = EtpaoLive::new();
86        for index in 0..10 {
87            let client_id = format!("client-{index}");
88            etpao.record_request(request(&client_id, 1_000, CoverageClass::FullInline));
89            if index < 8 {
90                etpao.record_outcome(outcome(&client_id, true));
91            }
92        }
93
94        assert!((etpao.current_etpao() - 1_250.0).abs() < f64::EPSILON);
95    }
96
97    #[test]
98    fn etpao_by_coverage_class() {
99        let mut etpao = EtpaoLive::new();
100        etpao.record_request(request("inline", 1_000, CoverageClass::FullInline));
101        etpao.record_request(request(
102            "controlled",
103            1_000,
104            CoverageClass::ContextControlled,
105        ));
106        etpao.record_outcome(outcome("inline", true));
107        etpao.record_outcome(outcome("controlled", true));
108
109        assert_eq!(etpao.summary().by_coverage_class.len(), 2);
110    }
111
112    #[test]
113    fn full_pipeline_profile_to_etpao() {
114        let coverage = coverage_class::detect_coverage(true, false, false);
115        let mut profile = ProfileBuilder::new("pipeline")
116            .coverage(coverage)
117            .context_window(16_000)
118            .build();
119        profile.tool_budget.max_tools = 2;
120        let broker = ContextBroker::new(profile);
121        let selected = broker.select_tools(&(0..4).map(tool).collect::<Vec<_>>());
122
123        assert!(!selected.is_empty());
124        assert_eq!(broker.select_context_mode(), ContextMode::ManifestOnly);
125        assert_eq!(broker.select_output_format(), OutputFormat::TypedResult);
126
127        let mut etpao = EtpaoLive::new();
128        etpao.record_request(request("pipeline", 500, coverage));
129        etpao.record_outcome(outcome("pipeline", true));
130
131        assert!(etpao.summary().etpao > 0.0);
132    }
133}