use super::*;
#[test]
fn test_chunk_id_deterministic() {
let input = "beads:aegis:aegis-0a9";
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
let id1 = hex::encode(hasher.finalize());
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
let id2 = hex::encode(hasher.finalize());
assert_eq!(id1, id2);
assert_eq!(id1.len(), 64);
}
fn sample_issue() -> BeadRow {
BeadRow {
id: "aegis-0a9".to_string(),
title: "Fix the widget".to_string(),
description: "The widget is broken.".to_string(),
status: "open".to_string(),
priority: 1,
assignee: Some("strider".to_string()),
notes: "investigated already".to_string(),
metadata: String::new(),
}
}
#[test]
fn test_metadata_is_meaningful() {
assert!(!metadata_is_meaningful(""));
assert!(!metadata_is_meaningful(" {} "));
assert!(!metadata_is_meaningful("null"));
assert!(metadata_is_meaningful(r#"{"source":"T1"}"#));
}
#[test]
fn test_build_bead_content_includes_metadata() {
let mut issue = sample_issue();
issue.metadata = r#"{"source":"guidance-T1","ref":"doc#42"}"#.to_string();
let content = build_bead_content(&issue, &[], &[]);
assert!(content.contains("Metadata:"));
assert!(content.contains("guidance-T1"));
}
#[test]
fn test_build_bead_content_skips_empty_metadata() {
let issue = sample_issue(); let content = build_bead_content(&issue, &[], &[]);
assert!(!content.contains("Metadata:"));
}
#[test]
fn test_bead_excluded_by_label() {
let exclude = vec!["security".to_string(), "escalation".to_string()];
assert!(bead_excluded(&["security".to_string()], &exclude));
assert!(bead_excluded(&["Escalation".to_string()], &exclude)); assert!(bead_excluded(
&["pitch".to_string(), "security".to_string()],
&exclude
));
assert!(!bead_excluded(&["pitch".to_string()], &exclude));
assert!(!bead_excluded(&["security".to_string()], &[])); }
#[test]
fn test_content_hash_stable_and_sensitive() {
let a = content_hash("hello");
assert_eq!(a, content_hash("hello"));
assert_ne!(a, content_hash("hello!"));
assert_eq!(a.len(), 64);
}
#[test]
fn test_build_bead_content_includes_labels() {
let issue = sample_issue();
let labels = vec!["pitch".to_string(), "b:search".to_string()];
let content = build_bead_content(&issue, &[], &labels);
assert!(content.contains("Fix the widget"));
assert!(content.contains("The widget is broken."));
assert!(content.contains("Notes:\ninvestigated already"));
assert!(content.contains("Labels: pitch, b:search"));
assert!(content.contains("Status: open | Priority: P1 | Assignee: strider"));
}
#[test]
fn test_build_bead_content_no_labels_no_comments() {
let issue = sample_issue();
let content = build_bead_content(&issue, &[], &[]);
assert!(!content.contains("Labels:"));
assert!(!content.contains("Comments:"));
assert!(content.contains("Assignee: strider"));
}
#[test]
fn test_build_bead_content_includes_comments() {
let issue = sample_issue();
let c = CommentRow {
issue_id: "aegis-0a9".to_string(),
author: "ian".to_string(),
text: "looks good".to_string(),
};
let content = build_bead_content(&issue, &[&c], &[]);
assert!(content.contains("Comments:"));
assert!(content.contains("--- ian ---\nlooks good"));
}
#[test]
fn test_disabled_config_returns_empty() {
let config = BeadsConfig::default();
let rt = tokio::runtime::Runtime::new().unwrap();
let chunks = rt.block_on(fetch_beads(&config)).unwrap();
assert!(chunks.is_empty());
}
#[test]
fn test_empty_databases_returns_empty() {
let config = BeadsConfig {
enabled: true,
databases: vec![],
..Default::default()
};
let rt = tokio::runtime::Runtime::new().unwrap();
let chunks = rt.block_on(fetch_beads(&config)).unwrap();
assert!(chunks.is_empty());
}
#[test]
fn live_metadata_enrichment_fails_loudly_when_source_is_unreachable() {
let config = BeadsConfig {
enabled: true,
host: "127.0.0.1".to_string(),
port: 1,
databases: vec!["beads_aegis".to_string()],
..Default::default()
};
let ids = vec![("aegis".to_string(), "aegis-probe".to_string())];
let rt = tokio::runtime::Runtime::new().unwrap();
let err = rt
.block_on(fetch_bead_metadata(&config, &ids))
.expect_err("unreachable live metadata source must not become an empty map");
assert!(
err.to_string()
.contains("connect to beads_aegis for live bead enrichment"),
"unexpected error: {err:#}"
);
}
fn open_config() -> BeadsConfig {
BeadsConfig {
enabled: true,
databases: vec!["beads_aegis".to_string()],
max_age_days: 90,
..Default::default()
}
}
#[test]
fn single_bead_narrows_without_relaxing() {
let config = open_config();
let batch = issues_where_clause(&config, None);
let single = issues_where_clause(&config, Some("aegis-0a9"));
assert!(
single.starts_with(batch.as_str()),
"batch={batch}\nsingle={single}"
);
assert_eq!(
single.matches(" AND ").count(),
batch.matches(" AND ").count() + 1
);
assert!(single.ends_with("id = 'aegis-0a9'"));
}
#[test]
fn single_bead_clause_honours_include_closed() {
let mut config = open_config();
config.include_closed = true;
let single = issues_where_clause(&config, Some("x-1"));
assert!(single.contains("status != 'deleted'"));
assert!(!single.contains("status NOT IN ('closed', 'deleted') AND"));
}
#[test]
fn single_bead_clause_escapes_quotes() {
let config = open_config();
let single = issues_where_clause(&config, Some("o'brien-1"));
assert!(single.ends_with("id = 'o''brien-1'"), "{single}");
}
#[test]
fn chunk_id_is_the_hash_of_the_chunk_key() {
let key = bead_file_path("aegis", "aegis-0a9");
let mut hasher = Sha256::new();
hasher.update(key.as_bytes());
assert_eq!(hex::encode(hasher.finalize()).len(), 64);
assert_eq!(key, "beads:aegis:aegis-0a9");
}
#[test]
fn fetch_bead_is_a_no_op_when_beads_are_unconfigured() {
let rt = tokio::runtime::Runtime::new().unwrap();
let chunks = rt
.block_on(fetch_bead(&BeadsConfig::default(), "bo-1", None))
.unwrap();
assert!(chunks.is_empty());
let config = BeadsConfig {
enabled: true,
databases: vec![],
..Default::default()
};
let chunks = rt.block_on(fetch_bead(&config, "bo-1", None)).unwrap();
assert!(chunks.is_empty());
}
#[test]
fn fetch_bead_with_an_unmatched_rig_touches_nothing() {
let config = BeadsConfig {
enabled: true,
host: "192.0.2.1".to_string(),
databases: vec!["beads_aegis".to_string()],
..Default::default()
};
let rt = tokio::runtime::Runtime::new().unwrap();
let chunks = rt
.block_on(fetch_bead(&config, "bo-1", Some("gastown")))
.unwrap();
assert!(chunks.is_empty());
}
#[test]
fn visibility_rules_agree_with_the_sql_clause() {
use super::keys::bead_visible;
let recent = chrono::Utc::now().to_rfc3339();
let ancient = "2020-01-01T00:00:00Z";
let cases: &[(bool, u32, &str, &str, bool)] = &[
(false, 0, "deleted", ancient, false),
(true, 0, "deleted", ancient, false),
(true, 90, "deleted", &recent, false),
(false, 0, "open", ancient, true),
(false, 90, "open", ancient, true),
(true, 90, "in_progress", ancient, true),
(false, 0, "closed", &recent, false),
(true, 0, "closed", ancient, true),
(true, 90, "closed", &recent, true),
(true, 90, "closed", ancient, false),
];
for (include_closed, max_age_days, status, created_at, expected) in cases {
let config = BeadsConfig {
enabled: true,
databases: vec!["beads_aegis".into()],
include_closed: *include_closed,
max_age_days: *max_age_days,
..Default::default()
};
assert_eq!(
bead_visible(&config, status, Some(created_at)),
*expected,
"predicate: include_closed={include_closed} max_age_days={max_age_days} \
status={status} created_at={created_at}"
);
let clause = issues_where_clause(&config, None);
assert!(
clause.contains("deleted"),
"every clause must exclude deleted rows: {clause}"
);
assert_eq!(
clause.contains("status NOT IN ('closed', 'deleted')")
&& !clause.contains("status != 'deleted'"),
!*include_closed,
"closed-bead admission must track include_closed: {clause}"
);
assert_eq!(
clause.contains("created_at >= DATE_SUB"),
*max_age_days > 0,
"the age bound must appear exactly when max_age_days > 0: {clause}"
);
if *max_age_days > 0 {
assert!(
clause.contains("(status NOT IN ('closed', 'deleted') OR created_at >="),
"the age bound must apply to CLOSED beads only: {clause}"
);
}
}
}
#[test]
fn an_unparseable_created_at_is_treated_as_unbounded() {
use super::keys::bead_visible;
let config = BeadsConfig {
enabled: true,
include_closed: true,
max_age_days: 1,
..Default::default()
};
assert!(bead_visible(&config, "closed", None));
assert!(bead_visible(&config, "closed", Some("not-a-timestamp")));
}