Files
yvgude--lean-ctx/rust/tests/context_cortex_phase2.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

417 lines
13 KiB
Rust

//! Phase 2: Hippocampal Consolidation Loop — Integration Tests
//!
//! Verifies the full consolidation pipeline:
//! 1. Cross-source edge creation + merge
//! 2. Knowledge auto-extraction from provider data
//! 3. Session cache integration for provider results
//! 4. End-to-end: Provider → Consolidate → All stores populated
use lean_ctx::core::bm25_index::{BM25Index, ChunkKind};
use lean_ctx::core::cache::SessionCache;
use lean_ctx::core::consolidation::{apply_artifacts, consolidate};
use lean_ctx::core::content_chunk::ContentChunk;
use lean_ctx::core::cross_source_edges::{
EDGE_DOCUMENTS, EDGE_MENTIONS, EDGE_QUERIES, EDGE_RESOLVES, extract_cross_source_edges,
merge_edges,
};
use lean_ctx::core::graph_index::IndexEdge;
use lean_ctx::core::knowledge_provider_extract::extract_facts;
// ---------------------------------------------------------------------------
// Helper: create provider chunks
// ---------------------------------------------------------------------------
fn github_issue(id: &str, title: &str, labels: &[&str], refs: Vec<&str>) -> ContentChunk {
ContentChunk::from_provider(
"github",
"issues",
id,
title,
ChunkKind::Issue,
format!("Body of {title}"),
refs.into_iter().map(String::from).collect(),
Some(serde_json::json!({
"state": "open",
"author": "testuser",
"labels": labels,
})),
)
}
fn github_pr(id: &str, title: &str, refs: Vec<&str>) -> ContentChunk {
ContentChunk::from_provider(
"github",
"pull_requests",
id,
title,
ChunkKind::PullRequest,
format!("PR body: {title}"),
refs.into_iter().map(String::from).collect(),
Some(serde_json::json!({"state": "open"})),
)
}
fn wiki_page(id: &str, title: &str, refs: Vec<&str>) -> ContentChunk {
ContentChunk::from_provider(
"confluence",
"wikis",
id,
title,
ChunkKind::WikiPage,
format!("Documentation: {title}"),
refs.into_iter().map(String::from).collect(),
None,
)
}
fn db_schema(table: &str, refs: Vec<&str>) -> ContentChunk {
ContentChunk::from_provider(
"postgres",
"schemas",
table,
&format!("public.{table}"),
ChunkKind::DbSchema,
format!("CREATE TABLE {table} (id serial PRIMARY KEY)"),
refs.into_iter().map(String::from).collect(),
None,
)
}
// ---------------------------------------------------------------------------
// 1. Cross-source edges
// ---------------------------------------------------------------------------
#[test]
fn issue_creates_bidirectional_mentions_edges() {
let chunks = vec![github_issue(
"42",
"Auth crash",
&["bug"],
vec!["src/auth.rs"],
)];
let edges = extract_cross_source_edges(&chunks);
let forward: Vec<_> = edges
.iter()
.filter(|e| e.from.contains("issues/42") && e.to == "src/auth.rs")
.collect();
let reverse: Vec<_> = edges
.iter()
.filter(|e| e.from == "src/auth.rs" && e.to.contains("issues/42"))
.collect();
assert_eq!(forward.len(), 1);
assert_eq!(reverse.len(), 1);
assert_eq!(forward[0].kind, EDGE_MENTIONS);
assert_eq!(reverse[0].kind, "mentioned_in");
}
#[test]
fn pr_creates_resolves_edges_with_high_weight() {
let chunks = vec![github_pr("100", "Fix auth", vec!["src/auth.rs"])];
let edges = extract_cross_source_edges(&chunks);
let resolves: Vec<_> = edges.iter().filter(|e| e.kind == EDGE_RESOLVES).collect();
assert_eq!(resolves.len(), 1);
assert_eq!(resolves[0].weight, 1.5);
}
#[test]
fn wiki_creates_documents_edges() {
let chunks = vec![wiki_page(
"auth-guide",
"Auth Guide",
vec!["src/auth/mod.rs"],
)];
let edges = extract_cross_source_edges(&chunks);
assert!(edges.iter().any(|e| e.kind == EDGE_DOCUMENTS));
}
#[test]
fn db_schema_creates_queries_edges() {
let chunks = vec![db_schema("users", vec!["src/db/users.rs"])];
let edges = extract_cross_source_edges(&chunks);
assert!(edges.iter().any(|e| e.kind == EDGE_QUERIES));
let query_edge = edges.iter().find(|e| e.kind == EDGE_QUERIES).unwrap();
assert_eq!(query_edge.weight, 1.2);
}
#[test]
fn hub_detection_multiple_sources_point_to_same_file() {
let chunks = vec![
github_issue("1", "Bug in auth", &["bug"], vec!["src/auth.rs"]),
github_issue("2", "Feature for auth", &["feature"], vec!["src/auth.rs"]),
github_pr("10", "Fix auth", vec!["src/auth.rs"]),
wiki_page("auth-doc", "Auth Docs", vec!["src/auth.rs"]),
];
let edges = extract_cross_source_edges(&chunks);
let incoming_to_auth = edges.iter().filter(|e| e.to == "src/auth.rs").count();
assert_eq!(incoming_to_auth, 4);
}
#[test]
fn merge_edges_dedup_and_weight_upgrade() {
let mut existing = vec![IndexEdge {
from: "github://issues/1".into(),
to: "src/auth.rs".into(),
kind: EDGE_MENTIONS.into(),
weight: 0.5,
}];
let new = vec![
IndexEdge {
from: "github://issues/1".into(),
to: "src/auth.rs".into(),
kind: EDGE_MENTIONS.into(),
weight: 2.0,
},
IndexEdge {
from: "github://issues/2".into(),
to: "src/db.rs".into(),
kind: EDGE_MENTIONS.into(),
weight: 1.0,
},
];
let added = merge_edges(&mut existing, new);
assert_eq!(added, 1);
assert_eq!(existing.len(), 2);
assert_eq!(
existing
.iter()
.find(|e| e.to == "src/auth.rs")
.unwrap()
.weight,
2.0
);
}
// ---------------------------------------------------------------------------
// 2. Knowledge extraction
// ---------------------------------------------------------------------------
#[test]
fn bug_issue_extracts_known_bugs_fact() {
let chunks = vec![github_issue(
"42",
"Token expiry crash",
&["bug"],
vec!["src/auth.rs"],
)];
let facts = extract_facts(&chunks);
let bug = facts.iter().find(|f| f.category == "known_bugs");
assert!(bug.is_some());
let bug = bug.unwrap();
assert!(bug.key.contains("42"));
assert!(bug.value.contains("Token expiry crash"));
assert_eq!(bug.confidence, 0.9);
}
#[test]
fn pr_extracts_recent_changes_and_changed_files() {
let chunks = vec![github_pr("100", "Fix token lifetime", vec!["src/auth.rs"])];
let facts = extract_facts(&chunks);
assert!(facts.iter().any(|f| f.category == "recent_changes"));
let changed = facts
.iter()
.find(|f| f.category == "changed_files" && f.key == "src/auth.rs");
assert!(changed.is_some());
assert!(changed.unwrap().confidence >= 0.9);
}
#[test]
fn wiki_extracts_documentation_facts() {
let chunks = vec![wiki_page("api-guide", "API Guide", vec!["src/api/mod.rs"])];
let facts = extract_facts(&chunks);
assert!(facts.iter().any(|f| f.category == "documentation"));
assert!(facts.iter().any(|f| f.category == "documented_files"));
}
#[test]
fn db_extracts_data_model_facts() {
let chunks = vec![db_schema("sessions", vec![])];
let facts = extract_facts(&chunks);
assert_eq!(facts.len(), 1);
assert_eq!(facts[0].category, "data_model");
assert_eq!(facts[0].confidence, 0.95);
}
// ---------------------------------------------------------------------------
// 3. Session cache integration
// ---------------------------------------------------------------------------
#[test]
fn consolidation_stores_in_session_cache() {
let chunks = vec![
github_issue("42", "Auth bug", &["bug"], vec!["src/auth.rs"]),
github_pr("100", "Fix auth", vec!["src/auth.rs"]),
];
let artifacts = consolidate(&chunks);
let mut cache = SessionCache::new();
apply_artifacts(&artifacts, None, None, Some(&mut cache));
assert!(cache.get("github://issues/42").is_some());
assert!(cache.get("github://pull_requests/100").is_some());
}
#[test]
fn cached_provider_result_has_correct_content() {
let chunks = vec![github_issue("42", "Auth bug", &["bug"], vec![])];
let artifacts = consolidate(&chunks);
let mut cache = SessionCache::new();
apply_artifacts(&artifacts, None, None, Some(&mut cache));
let entry = cache.get("github://issues/42").unwrap();
let content = entry.content().unwrap();
assert!(content.contains("Auth bug"));
}
// ---------------------------------------------------------------------------
// 4. End-to-end consolidation
// ---------------------------------------------------------------------------
#[test]
fn end_to_end_consolidation_populates_all_stores() {
let chunks = vec![
github_issue(
"42",
"Auth token crash",
&["bug", "p1"],
vec!["src/auth.rs"],
),
github_pr(
"100",
"Fix auth expiry",
vec!["src/auth.rs", "src/token.rs"],
),
wiki_page("auth-doc", "Auth Architecture", vec!["src/auth/mod.rs"]),
db_schema("sessions", vec!["src/db/session.rs"]),
];
let artifacts = consolidate(&chunks);
assert!(!artifacts.is_empty());
assert_eq!(artifacts.bm25_chunks.len(), 4);
assert!(!artifacts.edges.is_empty());
assert!(!artifacts.facts.is_empty());
assert_eq!(artifacts.cache_entries.len(), 4);
let mut index = BM25Index {
chunks: Vec::new(),
inverted: std::collections::HashMap::new(),
avg_doc_len: 0.0,
doc_count: 0,
doc_freqs: std::collections::HashMap::new(),
files: std::collections::HashMap::new(),
content_truncated: false,
};
let mut edges: Vec<IndexEdge> = Vec::new();
let mut cache = SessionCache::new();
let result = apply_artifacts(
&artifacts,
Some(&mut index),
Some(&mut edges),
Some(&mut cache),
);
// BM25
assert_eq!(result.chunks_indexed, 4);
assert_eq!(index.doc_count, 4);
assert_eq!(index.external_chunk_count(), 4);
// Graph edges
assert!(result.edges_created > 0);
assert!(edges.iter().any(|e| e.to == "src/auth.rs"));
assert!(edges.iter().any(|e| e.kind == EDGE_RESOLVES));
assert!(edges.iter().any(|e| e.kind == EDGE_DOCUMENTS));
assert!(edges.iter().any(|e| e.kind == EDGE_QUERIES));
// Knowledge facts
assert!(result.facts_extracted > 0);
let facts = &artifacts.facts;
assert!(facts.iter().any(|f| f.category == "known_bugs"));
assert!(facts.iter().any(|f| f.category == "recent_changes"));
assert!(facts.iter().any(|f| f.category == "documentation"));
assert!(facts.iter().any(|f| f.category == "data_model"));
// Session cache
assert_eq!(result.cache_entries_stored, 4);
assert!(cache.get("github://issues/42").is_some());
assert!(cache.get("github://pull_requests/100").is_some());
assert!(cache.get("confluence://wikis/auth-doc").is_some());
assert!(cache.get("postgres://schemas/sessions").is_some());
}
#[test]
fn consolidation_summary_is_accurate() {
let chunks = vec![
github_issue("1", "Bug", &["bug"], vec!["src/a.rs", "src/b.rs"]),
github_pr("10", "Fix", vec!["src/a.rs"]),
];
let artifacts = consolidate(&chunks);
let summary = artifacts.summary();
assert_eq!(summary.chunks_indexed, 2);
assert!(summary.edges_created > 0);
assert!(summary.facts_extracted > 0);
assert_eq!(summary.cache_entries_stored, 2);
}
#[test]
fn consolidation_with_only_code_chunks_is_noop() {
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: 5,
content: "fn main() {}".into(),
tokens: vec![],
token_count: 0,
});
let artifacts = consolidate(&[code]);
assert!(artifacts.edges.is_empty());
assert!(artifacts.facts.is_empty());
assert!(artifacts.cache_entries.is_empty());
}
#[test]
fn bm25_search_finds_consolidated_provider_data() {
let chunks = vec![github_issue(
"42",
"Authentication token expired",
&["bug"],
vec!["src/auth.rs"],
)];
let artifacts = consolidate(&chunks);
let mut index = BM25Index {
chunks: Vec::new(),
inverted: std::collections::HashMap::new(),
avg_doc_len: 0.0,
doc_count: 0,
doc_freqs: std::collections::HashMap::new(),
files: std::collections::HashMap::new(),
content_truncated: false,
};
apply_artifacts(&artifacts, Some(&mut index), None, None);
let results = index.search("authentication token", 5);
assert!(!results.is_empty());
assert!(results[0].file_path.contains("github://issues/42"));
}