Files
yvgude--lean-ctx/rust/tests/cortex_wiring_integration.rs
wehub-resource-sync 26382a7ac6
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
JetBrains Plugin / Actionlint (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
JetBrains Plugin / Validation (push) Waiting to run
JetBrains Plugin / Build (push) Waiting to run
JetBrains Plugin / Test (push) Blocked by required conditions
Security Check / Security Scan (push) Waiting to run
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:30 +08:00

425 lines
14 KiB
Rust

//! Integration test: full Context Engine wiring pipeline.
//!
//! Tests the data flow from provider chunks through consolidation into
//! the session cache, graph edges, and cross-source hints — the exact
//! path that the wired `ctx_provider` and `ctx_read` tools execute.
use lean_ctx::core::bm25_index::ChunkKind;
use lean_ctx::core::cache::SessionCache;
use lean_ctx::core::consolidation;
use lean_ctx::core::content_chunk::ContentChunk;
use lean_ctx::core::cross_source_hints;
use lean_ctx::core::graph_index::IndexEdge;
fn github_issue_chunk(id: &str, title: &str, body: &str, refs: Vec<&str>) -> ContentChunk {
ContentChunk::from_provider(
"github",
"issues",
id,
title,
ChunkKind::Issue,
body.to_string(),
refs.into_iter().map(String::from).collect(),
Some(serde_json::json!({"state": "open", "labels": ["bug"]})),
)
}
fn github_pr_chunk(id: &str, title: &str, body: &str, refs: Vec<&str>) -> ContentChunk {
ContentChunk::from_provider(
"github",
"pull_requests",
id,
title,
ChunkKind::PullRequest,
body.to_string(),
refs.into_iter().map(String::from).collect(),
Some(serde_json::json!({"state": "open"})),
)
}
/// Full pipeline: provider chunks → consolidate → cache + edges → hints for file.
#[test]
fn full_pipeline_provider_to_hints() {
let chunks = vec![
github_issue_chunk(
"42",
"Token expiry bug",
"The JWT token in src/auth/handler.rs expires too quickly",
vec!["src/auth/handler.rs"],
),
github_pr_chunk(
"100",
"Fix token lifetime",
"Adjusts TOKEN_LIFETIME in src/auth/handler.rs and src/config.rs",
vec!["src/auth/handler.rs", "src/config.rs"],
),
];
// Step 1: Consolidate
let artifacts = consolidation::consolidate(&chunks);
assert!(
!artifacts.is_empty(),
"consolidation should produce artifacts"
);
assert!(
!artifacts.edges.is_empty(),
"should produce cross-source edges"
);
assert!(
!artifacts.facts.is_empty(),
"should extract knowledge facts"
);
assert_eq!(
artifacts.cache_entries.len(),
2,
"should create cache entries for both chunks"
);
// Step 2: Apply to session cache
let mut cache = SessionCache::new();
let mut edges: Vec<IndexEdge> = Vec::new();
let result =
consolidation::apply_artifacts(&artifacts, None, Some(&mut edges), Some(&mut cache));
assert!(result.edges_created > 0, "edges should be created");
assert!(
result.cache_entries_stored > 0,
"cache entries should be stored"
);
// Step 3: Verify cache contains provider data
assert!(
cache.get("github://issues/42").is_some(),
"issue should be cached"
);
assert!(
cache.get("github://pull_requests/100").is_some(),
"PR should be cached"
);
// Step 4: Cross-source hints for the referenced file
let hints = cross_source_hints::hints_for_file("src/auth/handler.rs", &edges, "/project");
assert!(
!hints.is_empty(),
"should find hints for src/auth/handler.rs"
);
assert!(
hints.iter().any(|h| h.source_uri.contains("issues/42")),
"should link to issue #42"
);
assert!(
hints
.iter()
.any(|h| h.source_uri.contains("pull_requests/100")),
"should link to PR #100"
);
// Step 5: Format hints (as ctx_read would append them)
let formatted = cross_source_hints::format_hints(&hints);
assert!(
formatted.contains("Cross-Source Hints"),
"should have header"
);
assert!(formatted.contains("issues/42"), "should mention issue");
assert!(formatted.contains("pull_requests/100"), "should mention PR");
}
/// No external chunks → no artifacts, no hints.
#[test]
fn code_only_produces_no_hints() {
let code = ContentChunk::from(lean_ctx::core::bm25_index::CodeChunk {
file_path: "src/main.rs".into(),
symbol_name: "main".into(),
kind: ChunkKind::Function,
start_line: 1,
end_line: 10,
content: "fn main() { println!(\"hello\"); }".into(),
tokens: vec![],
token_count: 0,
});
let artifacts = consolidation::consolidate(&[code]);
assert!(artifacts.is_empty());
}
/// Multiple files referenced by same issue → hints appear for each file.
#[test]
fn multi_file_reference_produces_hints_for_each() {
let chunk = github_issue_chunk(
"55",
"Auth refactor needed",
"Affects src/auth/handler.rs, src/auth/middleware.rs, and src/db/sessions.rs",
vec![
"src/auth/handler.rs",
"src/auth/middleware.rs",
"src/db/sessions.rs",
],
);
let artifacts = consolidation::consolidate(&[chunk]);
let mut edges: Vec<IndexEdge> = Vec::new();
consolidation::apply_artifacts(&artifacts, None, Some(&mut edges), None);
for file in &[
"src/auth/handler.rs",
"src/auth/middleware.rs",
"src/db/sessions.rs",
] {
let hints = cross_source_hints::hints_for_file(file, &edges, "/project");
assert!(!hints.is_empty(), "should find hints for {file}");
assert!(
hints.iter().any(|h| h.source_uri.contains("issues/55")),
"{file} should link to issue #55"
);
}
}
/// Cache re-read returns the stored content.
#[test]
fn cached_provider_data_survives_reread() {
let chunks = vec![github_issue_chunk(
"99",
"Performance regression",
"Query in src/db/queries.rs takes 5s after upgrade",
vec!["src/db/queries.rs"],
)];
let artifacts = consolidation::consolidate(&chunks);
let mut cache = SessionCache::new();
consolidation::apply_artifacts(&artifacts, None, None, Some(&mut cache));
let cached = cache.get("github://issues/99");
assert!(cached.is_some());
let entry = cached.unwrap();
let content = entry.content().expect("cached entry should have content");
assert!(
content.contains("Performance regression") || content.contains("5s after upgrade"),
"cached content should contain the issue text"
);
}
/// Knowledge facts are correctly extracted from different chunk kinds.
#[test]
fn knowledge_extraction_from_mixed_chunks() {
let chunks = vec![
github_issue_chunk(
"10",
"Login broken after deploy",
"Users cannot log in since last deploy",
vec!["src/auth.rs"],
),
github_pr_chunk(
"20",
"Add rate limiting",
"Implements rate limiting middleware",
vec!["src/middleware.rs"],
),
];
let artifacts = consolidation::consolidate(&chunks);
assert!(
artifacts.facts.len() >= 2,
"should extract facts from both chunk types"
);
let has_bug_fact = artifacts.facts.iter().any(|f| f.category == "known_bugs");
let has_change_fact = artifacts
.facts
.iter()
.any(|f| f.category == "recent_changes");
assert!(has_bug_fact, "issue should produce a known_bugs fact");
assert!(has_change_fact, "PR should produce a recent_changes fact");
}
/// Active inference predicts preloads based on task description.
#[test]
fn active_inference_predicts_for_task() {
let available = vec!["github".to_string(), "jira".to_string()];
let mut bandit = lean_ctx::core::provider_bandit::ProviderBandit::new();
let predictions = lean_ctx::core::active_inference::predict_preloads(
"fix authentication bug in login flow",
&available,
&mut bandit,
2,
);
assert!(
!predictions.is_empty(),
"should predict at least one preload for a bug-related task"
);
assert!(
predictions.iter().all(|p| p.confidence > 0.0),
"all predictions should have positive confidence"
);
}
/// Simulates the exact MCP handler flow: `ctx_provider` query → consolidate →
/// then `ctx_read` would append hints. Exercises `ctx_provider::handle` with a
/// real `ToolContext` containing a shared `SessionCache`.
#[test]
fn mcp_handler_flow_provider_then_read_hints() {
use std::sync::Arc;
// Simulate the ToolContext that the MCP server creates
let cache = Arc::new(tokio::sync::RwLock::new(SessionCache::new()));
// Step 1: Provider query produces chunks and consolidates them
let chunks = vec![
github_issue_chunk(
"77",
"Memory leak in connection pool",
"Connection pool in src/db/pool.rs leaks when timeout occurs. See also src/db/config.rs",
vec!["src/db/pool.rs", "src/db/config.rs"],
),
github_pr_chunk(
"88",
"Fix pool leak on timeout",
"Properly drains connections in src/db/pool.rs on timeout. Tests in tests/pool_test.rs",
vec!["src/db/pool.rs", "tests/pool_test.rs"],
),
];
// This is what ctx_provider::consolidate_to_session does internally
let artifacts = consolidation::consolidate(&chunks);
assert!(!artifacts.is_empty());
// Write to cache (as consolidate_to_session does via ctx.cache)
{
let mut cache_guard = cache.blocking_write();
for entry in &artifacts.cache_entries {
cache_guard.store(&entry.uri, &entry.content);
}
}
// Step 2: Apply edges (these would be in the graph index)
let mut edges: Vec<IndexEdge> = Vec::new();
let result = consolidation::apply_artifacts(&artifacts, None, Some(&mut edges), None);
assert!(result.edges_created > 0);
// Step 3: Simulate ctx_read — check cross-source hints for referenced files
let hints_pool = cross_source_hints::hints_for_file("src/db/pool.rs", &edges, "/project");
assert!(
!hints_pool.is_empty(),
"pool.rs should have cross-source hints"
);
assert!(
hints_pool
.iter()
.any(|h| h.source_uri.contains("issues/77")),
"pool.rs should link to issue #77"
);
assert!(
hints_pool
.iter()
.any(|h| h.source_uri.contains("pull_requests/88")),
"pool.rs should link to PR #88"
);
let hints_config = cross_source_hints::hints_for_file("src/db/config.rs", &edges, "/project");
assert!(
!hints_config.is_empty(),
"config.rs should have cross-source hints"
);
let hints_test = cross_source_hints::hints_for_file("tests/pool_test.rs", &edges, "/project");
assert!(
!hints_test.is_empty(),
"pool_test.rs should have cross-source hints"
);
// Step 4: Verify format output matches what ctx_read appends
let formatted = cross_source_hints::format_hints(&hints_pool);
assert!(formatted.starts_with("\n--- Cross-Source Hints ---\n"));
assert!(formatted.contains("[mentions]") || formatted.contains("[mentioned_in]"));
// Step 5: Verify cache hit (session cache stores the provider results)
{
let cache_guard = cache.blocking_read();
assert!(cache_guard.get("github://issues/77").is_some());
assert!(cache_guard.get("github://pull_requests/88").is_some());
}
}
/// Verify the free-energy budget allocator works with realistic data.
#[test]
fn free_energy_budget_allocation() {
use lean_ctx::core::free_energy_budget::{ColumnBudgetRequest, allocate_budget};
let requests = vec![
ColumnBudgetRequest {
column_id: "code".into(),
saliency_score: 0.9,
estimated_tokens: 5000,
minimum_tokens: 0,
},
ColumnBudgetRequest {
column_id: "issues".into(),
saliency_score: 0.6,
estimated_tokens: 2000,
minimum_tokens: 0,
},
ColumnBudgetRequest {
column_id: "wiki".into(),
saliency_score: 0.3,
estimated_tokens: 3000,
minimum_tokens: 0,
},
];
let allocations = allocate_budget(4000, &requests, 0.05);
assert_eq!(allocations.len(), 3);
let total_allocated: usize = allocations.iter().map(|a| a.allocated_tokens).sum();
assert!(
total_allocated <= 4000,
"should not exceed budget: {total_allocated}"
);
let code_alloc = allocations.iter().find(|a| a.column_id == "code").unwrap();
let wiki_alloc = allocations.iter().find(|a| a.column_id == "wiki").unwrap();
assert!(
code_alloc.allocated_tokens >= wiki_alloc.allocated_tokens,
"higher-saliency code column should get more tokens than wiki"
);
}
/// ECS saliency ranking respects task relevance.
#[test]
fn saliency_ranks_relevant_chunks_higher() {
use lean_ctx::core::saliency::{EcsWeights, compute_ecs_scores};
let chunks = vec![
github_issue_chunk(
"1",
"Authentication token expired",
"Token in src/auth.rs expired too fast",
vec!["src/auth.rs"],
),
github_issue_chunk(
"2",
"CSS alignment issue",
"Button misaligned on mobile in styles.css",
vec!["src/styles.css"],
),
];
let keywords: Vec<String> = vec!["auth".into(), "token".into(), "expiry".into()];
let edge_counts = vec![0, 0];
let weights = EcsWeights {
w_task: 0.8,
w_graph: 0.1,
w_density: 0.1,
};
let scores = compute_ecs_scores(&chunks, &keywords, &edge_counts, &weights);
assert_eq!(scores.len(), 2);
let auth_score = &scores[0];
let css_score = &scores[1];
assert!(
auth_score.final_score >= css_score.final_score,
"auth-related chunk should score >= css chunk for auth task"
);
}