Files
yvgude--lean-ctx/rust/tests/output_contracts_knowledge.rs
wehub-resource-sync 26382a7ac6
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
JetBrains Plugin / Actionlint (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
JetBrains Plugin / Validation (push) Has been cancelled
JetBrains Plugin / Build (push) Has been cancelled
JetBrains Plugin / Test (push) Has been cancelled
Security Check / Security Scan (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:30 +08:00

430 lines
13 KiB
Rust

use std::path::Path;
use lean_ctx::core::knowledge::ProjectKnowledge;
use lean_ctx::core::memory_policy::MemoryPolicy;
#[test]
fn ctx_knowledge_recall_is_budgeted_and_deterministic() {
let _g = lean_ctx::core::data_dir::test_env_lock();
let tmp = tempfile::tempdir().expect("tempdir");
let data_dir = tmp.path().join("data");
std::fs::create_dir_all(&data_dir).expect("create data dir");
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", data_dir.to_string_lossy().to_string()) };
let project_root = tmp.path().join("proj");
std::fs::create_dir_all(&project_root).expect("create project root");
let project_root_str = project_root.to_string_lossy().to_string();
let policy = MemoryPolicy::default();
let mut knowledge = ProjectKnowledge::load_or_create(&project_root_str);
for i in 0..50 {
knowledge.remember(
"architecture",
&format!("k{i:02}"),
&format!("v{i:02}"),
"s1",
0.8,
&policy,
);
}
knowledge.save().expect("save knowledge");
let out1 = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"recall",
Some("architecture"),
None,
None,
None,
"s1",
None,
None,
None,
None,
None,
);
let out2 = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"recall",
Some("architecture"),
None,
None,
None,
"s1",
None,
None,
None,
None,
None,
);
assert_eq!(out1, out2, "recall output must be deterministic");
assert!(
out1.contains("showing 10/50"),
"recall header must indicate truncation"
);
let fact_lines = out1
.lines()
.filter(|l| l.starts_with(" [architecture/"))
.count();
assert!(fact_lines <= 10, "must not exceed recall budget");
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::remove_var("LEAN_CTX_DATA_DIR") };
}
#[test]
fn ctx_knowledge_export_is_file_backed_not_json_stdout() {
let _g = lean_ctx::core::data_dir::test_env_lock();
let tmp = tempfile::tempdir().expect("tempdir");
let data_dir = tmp.path().join("data");
std::fs::create_dir_all(&data_dir).expect("create data dir");
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", data_dir.to_string_lossy().to_string()) };
let project_root = tmp.path().join("proj");
std::fs::create_dir_all(&project_root).expect("create project root");
let project_root_str = project_root.to_string_lossy().to_string();
let policy = MemoryPolicy::default();
let mut knowledge = ProjectKnowledge::load_or_create(&project_root_str);
knowledge.remember("arch", "db", "MySQL", "s1", 0.8, &policy);
knowledge.save().expect("save knowledge");
let out = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"export",
None,
None,
None,
None,
"s1",
None,
None,
None,
None,
None,
);
assert!(
out.starts_with("Export saved: "),
"export must return a compact confirmation"
);
assert!(
!out.trim_start().starts_with('{'),
"export must not print full JSON to stdout"
);
let path_str = out
.strip_prefix("Export saved: ")
.and_then(|s| s.split_whitespace().next())
.expect("extract export path");
assert!(Path::new(path_str).exists(), "export file must exist");
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::remove_var("LEAN_CTX_DATA_DIR") };
}
#[test]
fn ctx_knowledge_feedback_persists_and_affects_quality_score() {
let _g = lean_ctx::core::data_dir::test_env_lock();
let tmp = tempfile::tempdir().expect("tempdir");
let data_dir = tmp.path().join("data");
std::fs::create_dir_all(&data_dir).expect("create data dir");
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", data_dir.to_string_lossy().to_string()) };
let project_root = tmp.path().join("proj");
std::fs::create_dir_all(&project_root).expect("create project root");
let project_root_str = project_root.to_string_lossy().to_string();
let policy = MemoryPolicy::default();
let mut knowledge = ProjectKnowledge::load_or_create(&project_root_str);
knowledge.remember("arch", "db", "MySQL", "s1", 0.8, &policy);
knowledge.save().expect("save knowledge");
let before = ProjectKnowledge::load_or_create(&project_root_str);
let before_fact = before
.facts
.iter()
.find(|f| f.is_current() && f.category == "arch" && f.key == "db")
.expect("fact exists");
let before_score = before_fact.quality_score();
let out = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"feedback",
Some("arch"),
Some("db"),
Some("up"),
None,
"s2",
None,
None,
None,
None,
None,
);
assert!(
out.contains("Feedback recorded"),
"feedback output must confirm recording: {out}"
);
let after = ProjectKnowledge::load_or_create(&project_root_str);
let after_fact = after
.facts
.iter()
.find(|f| f.is_current() && f.category == "arch" && f.key == "db")
.expect("fact exists");
assert_eq!(after_fact.feedback_up, 1);
assert_eq!(after_fact.feedback_down, 0);
assert!(after_fact.last_feedback.is_some());
assert!(
after_fact.quality_score() > before_score,
"quality score should increase after positive feedback"
);
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::remove_var("LEAN_CTX_DATA_DIR") };
}
#[test]
fn ctx_knowledge_relations_persist_and_render() {
let _g = lean_ctx::core::data_dir::test_env_lock();
let tmp = tempfile::tempdir().expect("tempdir");
let data_dir = tmp.path().join("data");
std::fs::create_dir_all(&data_dir).expect("create data dir");
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", data_dir.to_string_lossy().to_string()) };
let project_root = tmp.path().join("proj");
std::fs::create_dir_all(&project_root).expect("create project root");
let project_root_str = project_root.to_string_lossy().to_string();
let policy = MemoryPolicy::default();
let mut knowledge = ProjectKnowledge::load_or_create(&project_root_str);
knowledge.remember("arch", "db", "MySQL", "s1", 0.9, &policy);
knowledge.remember("arch", "cache", "Redis", "s1", 0.9, &policy);
knowledge.save().expect("save knowledge");
let out = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"relate",
Some("arch"),
Some("db"),
Some("depends_on"),
Some("arch/cache"),
"s2",
None,
None,
None,
None,
None,
);
assert!(out.contains("Relation"), "relate must confirm: {out}");
let loaded = ProjectKnowledge::load_or_create(&project_root_str);
let graph =
lean_ctx::core::knowledge_relations::KnowledgeRelationGraph::load(&loaded.project_hash)
.expect("relations graph should exist");
assert!(
graph.edges.iter().any(|e| {
e.from.category == "arch"
&& e.from.key == "db"
&& e.to.category == "arch"
&& e.to.key == "cache"
&& e.kind == lean_ctx::core::knowledge_relations::KnowledgeEdgeKind::DependsOn
}),
"expected depends_on edge in relations graph"
);
let list = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"relations",
Some("arch"),
Some("db"),
None,
Some("out"),
"s2",
None,
None,
None,
None,
None,
);
assert!(
list.contains("depends_on") && list.contains("arch/cache"),
"relations must include the edge: {list}"
);
let diagram = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"relations_diagram",
Some("arch"),
Some("db"),
None,
Some("out"),
"s2",
None,
None,
None,
None,
None,
);
assert!(
diagram.contains("graph TD") && diagram.contains("depends_on"),
"relations_diagram must return mermaid: {diagram}"
);
let out2 = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"unrelate",
Some("arch"),
Some("db"),
Some("depends_on"),
Some("arch/cache"),
"s2",
None,
None,
None,
None,
None,
);
assert!(
out2.contains("removed"),
"unrelate must confirm removal: {out2}"
);
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::remove_var("LEAN_CTX_DATA_DIR") };
}
#[test]
fn ctx_knowledge_lifecycle_report_covers_all_layers() {
let _g = lean_ctx::core::data_dir::test_env_lock();
let tmp = tempfile::tempdir().expect("tempdir");
let data_dir = tmp.path().join("data");
std::fs::create_dir_all(&data_dir).expect("create data dir");
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", data_dir.to_string_lossy().to_string()) };
let project_root = tmp.path().join("proj");
std::fs::create_dir_all(&project_root).expect("create project root");
let project_root_str = project_root.to_string_lossy().to_string();
let policy = MemoryPolicy::default();
let mut knowledge = ProjectKnowledge::load_or_create(&project_root_str);
knowledge.remember("architecture", "auth", "JWT RS256", "s1", 0.9, &policy);
knowledge.save().expect("save knowledge");
let out = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"lifecycle_report",
None,
None,
None,
None,
"s1",
None,
None,
None,
None,
None,
);
for layer in [
"knowledge",
"archives",
"episodic",
"procedural",
"embeddings",
] {
assert!(
out.contains(layer),
"lifecycle report must mention layer '{layer}': {out}"
);
}
assert!(
out.contains("1 active"),
"knowledge layer must show real counts: {out}"
);
assert!(
out.contains("Layer boundaries"),
"report must document layer boundaries: {out}"
);
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::remove_var("LEAN_CTX_DATA_DIR") };
}
#[test]
fn ctx_knowledge_recall_as_of_time_travels() {
let _g = lean_ctx::core::data_dir::test_env_lock();
let tmp = tempfile::tempdir().expect("tempdir");
let data_dir = tmp.path().join("data");
std::fs::create_dir_all(&data_dir).expect("create data dir");
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::set_var("LEAN_CTX_DATA_DIR", data_dir.to_string_lossy().to_string()) };
let project_root = tmp.path().join("proj");
std::fs::create_dir_all(&project_root).expect("create project root");
let project_root_str = project_root.to_string_lossy().to_string();
let policy = MemoryPolicy::default();
let mut knowledge = ProjectKnowledge::load_or_create(&project_root_str);
knowledge.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
knowledge.facts[0].confirmation_count = 3;
let between = chrono::Utc::now();
std::thread::sleep(std::time::Duration::from_millis(10));
knowledge.remember("arch", "db", "MySQL", "s2", 0.9, &policy);
knowledge.save().expect("save knowledge");
let past = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"recall",
None,
None,
None,
Some("db"),
"s1",
None,
None,
None,
None,
Some(&between.to_rfc3339()),
);
assert!(
past.contains("PostgreSQL") && !past.contains("MySQL"),
"as_of recall must return the value valid at that time: {past}"
);
let invalid = lean_ctx::tools::ctx_knowledge::handle(
&project_root_str,
"recall",
None,
None,
None,
Some("db"),
"s1",
None,
None,
None,
None,
Some("not-a-date"),
);
assert!(
invalid.contains("invalid as_of"),
"invalid as_of must produce a clear error: {invalid}"
);
// TODO: Audit that the environment access only happens in single-threaded code.
unsafe { std::env::remove_var("LEAN_CTX_DATA_DIR") };
}