#![cfg(test)]
use crate::memgraph;
use crate::records::store::LoadedRecord;
use crate::records::{mrf, Confidence, Kind, Op, Record};
use chrono::TimeZone;
use proptest::prelude::*;
use uuid::Uuid;
fn value_strategy() -> impl Strategy<Value = String> {
proptest::string::string_regex("[a-zA-Z0-9 ,.;:!?()\\[\\]{}'\"/@#$%^&*+=_~-]{1,120}")
.unwrap()
.prop_map(|s| {
let t = s.trim().to_string();
if t.is_empty() {
"x".to_string()
} else {
t
}
})
}
fn key_strategy() -> impl Strategy<Value = String> {
proptest::string::string_regex("[a-z0-9]{1,10}(\\.[a-z0-9-]{1,10}){0,3}").unwrap()
}
fn record_strategy() -> impl Strategy<Value = Record> {
(
key_strategy(),
value_strategy(),
proptest::option::of(value_strategy()),
proptest::collection::vec(value_strategy(), 0..4),
proptest::collection::vec("[a-z][a-z0-9-]{0,15}", 0..3),
)
.prop_map(|(key, summary, rationale, details, tags)| Record {
id: Uuid::now_v7(),
key,
kind: Kind::DomainFact,
op: Op::Assert,
summary,
rationale,
confidence: Confidence::Verified,
created_at: chrono::Utc
.with_ymd_and_hms(2026, 7, 27, 10, 30, 0)
.unwrap(),
writer: "w1".into(),
human: None,
agent: None,
session: None,
pr: None,
issue: None,
alias_key: None,
canonical_key: None,
details,
alternatives: vec![],
consequences: vec![],
paths: vec![],
symbols: vec![],
tags,
evidence: vec![],
supersedes: vec![],
related: vec![],
extensions: vec![],
})
}
proptest! {
#[test]
fn mrf_round_trip(record in record_strategy()) {
let text = mrf::serialize(&record);
let parsed = mrf::parse(text.as_bytes()).unwrap();
prop_assert_eq!(&parsed, &record);
prop_assert_eq!(mrf::serialize(&parsed), text);
}
#[test]
fn json_round_trip(record in record_strategy()) {
let json = serde_json::to_string(&record).unwrap();
let back: Record = serde_json::from_str(&json).unwrap();
prop_assert_eq!(back, record);
}
#[test]
fn chain_head_derivation(len in 1usize..12) {
let mut records: Vec<LoadedRecord> = Vec::new();
let mut prev: Option<Uuid> = None;
for _ in 0..len {
let mut r = Record {
id: Uuid::now_v7(),
key: "a.chain".into(),
kind: Kind::DomainFact,
op: Op::Assert,
summary: "S.".into(),
rationale: None,
confidence: Confidence::Verified,
created_at: chrono::Utc.with_ymd_and_hms(2026, 1, 1, 0, 0, 0).unwrap(),
writer: "w".into(),
human: None, agent: None, session: None, pr: None, issue: None,
alias_key: None, canonical_key: None,
details: vec![], alternatives: vec![], consequences: vec![],
paths: vec![], symbols: vec![], tags: vec!["t".into()],
evidence: vec![], supersedes: vec![], related: vec![], extensions: vec![],
};
if let Some(p) = prev {
r.supersedes = vec![p];
}
prev = Some(r.id);
records.push(LoadedRecord { rel_path: r.relative_path(), record: r, issues: vec![] });
}
let graph = memgraph::build(&records);
let state = &graph.keys["a.chain"];
prop_assert_eq!(state.head_ids.len(), 1);
prop_assert_eq!(state.head_ids[0], prev.unwrap());
prop_assert!(!state.conflicted);
}
#[test]
fn dag_heads_are_exactly_unsuperseded(edges in proptest::collection::vec(proptest::bool::ANY, 0..40)) {
let n = 8usize;
let mut records: Vec<LoadedRecord> = Vec::new();
let mut ids: Vec<Uuid> = Vec::new();
let mut edge_iter = edges.into_iter();
for i in 0..n {
let mut supersedes = Vec::new();
for prior in ids.iter().take(i) {
if edge_iter.next().unwrap_or(false) {
supersedes.push(*prior);
}
}
let r = Record {
id: Uuid::now_v7(),
key: "a.dag".into(),
kind: Kind::DomainFact,
op: Op::Assert,
summary: "S.".into(),
rationale: None,
confidence: Confidence::Verified,
created_at: chrono::Utc.with_ymd_and_hms(2026, 1, 1, 0, 0, 0).unwrap(),
writer: "w".into(),
human: None, agent: None, session: None, pr: None, issue: None,
alias_key: None, canonical_key: None,
details: vec![], alternatives: vec![], consequences: vec![],
paths: vec![], symbols: vec![], tags: vec!["t".into()],
evidence: vec![], supersedes, related: vec![], extensions: vec![],
};
ids.push(r.id);
records.push(LoadedRecord { rel_path: r.relative_path(), record: r, issues: vec![] });
}
let graph = memgraph::build(&records);
let state = &graph.keys["a.dag"];
prop_assert!(!state.head_ids.is_empty());
let superseded: std::collections::HashSet<Uuid> = records
.iter()
.flat_map(|r| r.record.supersedes.iter().copied())
.collect();
let expected: Vec<Uuid> = {
let mut v: Vec<Uuid> = ids.iter().copied().filter(|i| !superseded.contains(i)).collect();
v.sort();
v
};
prop_assert_eq!(state.head_ids.clone(), expected);
prop_assert_eq!(state.conflicted, state.head_ids.len() > 1);
}
#[test]
fn packer_respects_budget(budget in 200u32..4000, n in 0usize..60) {
use crate::retrieval::{ContextResult, MemoryItem};
use crate::team::{Revisions, SyncState};
let result = ContextResult {
revisions: Revisions {
baseline_ref: "refs/remotes/origin/main".into(),
team_memory_revision: "abcdef0123456789".into(),
local_head: None,
branch_memory_revision: "b".into(),
working_memory_revision: "empty".into(),
last_fetch_at: None,
sync_state: SyncState::Current,
shared_memory_exact: true,
memlay_version: "0".into(),
record_format_version: 1,
},
branch: Some("main".into()),
dirty: false,
modules: vec![],
memories: (0..n).map(|i| MemoryItem {
ref_id: format!("memory:{i}"),
key: format!("k.{i}"),
kind: "decision".into(),
summary: "A ".repeat(60),
rationale: Some("Because ".repeat(30)),
confidence: "verified".into(),
layer: "team".into(),
score: 100,
reason: "r".into(),
stale: false,
conflicted: false,
introduced_by: None,
agent: None,
created_at: "2026-01-01T00:00:00Z".into(),
}).collect(),
code_targets: vec![],
files: vec![],
tests: vec![],
edges: vec![],
changes: vec![],
conflicts: vec![],
warnings: vec![],
estimated_tokens: 0,
token_budget: budget,
};
let text = crate::retrieval::mcf::render(&result, budget, false);
let tokens = crate::retrieval::query::estimate_tokens(&text);
prop_assert!(tokens <= budget + budget / 10 + 10, "budget {} -> {}", budget, tokens);
}
#[test]
fn redaction_removes_configured_secrets(
prefix in value_strategy(),
secret in "[A-Za-z0-9]{12,40}",
suffix in value_strategy(),
) {
let text = format!("{prefix} {secret} {suffix}");
let (out, _) = crate::audit::redact(&text, std::slice::from_ref(&secret));
prop_assert!(!out.contains(&secret));
}
}
#[test]
fn mcf_token_reduction_vs_pretty_json() {
use crate::retrieval::{ChangeItem, CodeItem, ContextResult, EdgeItem, MemoryItem};
use crate::team::{Revisions, SyncState};
let result = ContextResult {
revisions: Revisions {
baseline_ref: "refs/remotes/origin/main".into(),
team_memory_revision: "4f12ab0987654321".into(),
local_head: Some("deadbeef".into()),
branch_memory_revision: "b1".into(),
working_memory_revision: "empty".into(),
last_fetch_at: None,
sync_state: SyncState::Current,
shared_memory_exact: true,
memlay_version: "0.1.0".into(),
record_format_version: 1,
},
branch: Some("feature/retries".into()),
dirty: true,
modules: vec![("apps/api/payments".into(), 3)],
memories: (0..4)
.map(|i| MemoryItem {
ref_id: format!("memory:0000-{i}"),
key: format!("payments.webhook.policy-{i}"),
kind: "decision".into(),
summary: "Provider event IDs are idempotency keys for webhook processing.".into(),
rationale: Some(
"Duplicate provider delivery must not process an event twice.".into(),
),
confidence: "verified".into(),
layer: "team".into(),
score: 100,
reason: "exact key".into(),
stale: false,
conflicted: false,
introduced_by: Some("alice@example.com".into()),
agent: Some("codex".into()),
created_at: "2026-07-21T10:12:00Z".into(),
})
.collect(),
code_targets: (0..4)
.map(|i| CodeItem {
ref_id: format!("symbol:{i}"),
symbol: format!("PaymentWebhookController.handle{i}"),
kind: "method".into(),
path: "apps/api/payments/webhook.ts".into(),
line: 41,
signature: "handle(req: Request): void".into(),
score: 95,
reason: "exact symbol".into(),
is_test: false,
})
.collect(),
files: vec![],
tests: vec![],
edges: vec![EdgeItem {
from: "apps/api/payments/webhook.ts".into(),
to: "queue".into(),
}],
changes: vec![ChangeItem {
ref_id: "memory:c1".into(),
summary: "Moved processing to the asynchronous consumer.".into(),
date: "2026-07-21".into(),
human: Some("alice@example.com".into()),
agent: Some("codex".into()),
layer: "team".into(),
}],
conflicts: vec![],
warnings: vec![],
estimated_tokens: 0,
token_budget: 4000,
};
let mcf = crate::retrieval::mcf::render(&result, 4000, false);
let json = serde_json::to_string_pretty(&result).unwrap();
let mcf_tokens = crate::retrieval::query::estimate_tokens(&mcf) as f64;
let json_tokens = crate::retrieval::query::estimate_tokens(&json) as f64;
assert!(
mcf_tokens <= json_tokens * 0.75,
"MCF {mcf_tokens} tokens vs JSON {json_tokens}: reduction below 25%"
);
assert!(mcf.contains("team=4f12ab098"));
assert!(mcf.contains("WHY "));
assert!(mcf.contains("CHANGE 2026-07-21 alice@example.com + codex"));
}