1mod build;
4pub mod query;
5mod types;
6
7pub use types::{DOC_RELATIONS, Edge, Graph, MENTIONS, Resolution};
8
9#[cfg(test)]
10mod tests {
11 use super::*;
12 use crate::config::{Config, OwnerKind, RawConfig};
13 use crate::corpus::{Corpus, LoadOptions};
14
15 fn write(root: &std::path::Path, rel: &str, text: &str) {
16 let p = root.join(rel);
17 std::fs::create_dir_all(p.parent().unwrap()).unwrap();
18 std::fs::write(p, text).unwrap();
19 }
20
21 #[test]
22 fn resolves_and_queries() {
23 let tmp = tempfile::tempdir().unwrap();
24 let r = tmp.path();
25 std::fs::create_dir_all(r.join("apps/shop/src/cart")).unwrap();
26 write(
27 r,
28 "apps/shop/.spec/sot/cart.sot.md",
29 "---\nstatus: active\nrel:\n governs: [\"apps/shop/src/cart/**\"]\n---\n# Cart\n## Totals {#CO-1}\n",
30 );
31 write(
32 r,
33 "apps/shop/.spec/adr/20260101000000-old.adr.md",
34 "---\nstatus: superseded\naliases: [ADR-7]\n---\n# Old\n",
35 );
36 write(
37 r,
38 "apps/shop/.spec/adr/20260201000000-new.adr.md",
39 "---\nstatus: active\nrel:\n supersedes: [ADR-7]\n implements: [\"sot:shop/cart#CO-1\"]\n---\n# New\n",
40 );
41 write(
42 r,
43 "apps/shop/.spec/plan/20260301000000-ship.plan.md",
44 "---\nstatus: active\n---\n# Ship\n[[adr:shop/20260201000000]] [[sot:shop/cart#NOPE]]\n",
45 );
46 let mut raw = RawConfig::default();
47 raw.owners.kinds = vec![OwnerKind { pattern: "apps/*".into(), id: "{1}".into(), manifest: false }];
48 let c = Corpus::load(Config::from_raw(r.to_path_buf(), raw, false), &LoadOptions::default());
49 let g = Graph::build(&c);
50 let idx = |id: &str| g.by_id[id];
51 assert!(matches!(g.resolve("ADR-7"), Resolution::Doc { via_alias: true, .. }));
52 assert!(matches!(g.resolve("sot:shop/cart#NOPE"), Resolution::MissingAnchor { .. }));
53 assert_eq!(
54 query::chain(&g, idx("adr:shop/20260101000000")),
55 [idx("adr:shop/20260101000000"), idx("adr:shop/20260201000000")]
56 );
57 let imp: Vec<usize> = query::impact(&g, idx("sot:shop/cart")).into_iter().map(|x| x.0).collect();
58 assert_eq!(imp, [idx("adr:shop/20260201000000"), idx("plan:shop/20260301000000")]);
59 let gov = query::governing(&c, "apps/shop/src/cart/total.rs");
60 assert_eq!(gov[0].idx, idx("sot:shop/cart"));
61 assert_eq!(gov.len(), 4);
62 }
63}