mod support;
use support::git_repo::GitRepo;
use support::inspect::{Inspector, capture_supersession_round};
fn build_fork() -> (serde_json::Value, String, String, String) {
let repo = GitRepo::new();
repo.write("src/lib.rs", "pub fn value() -> u32 { 1 }\n");
repo.commit_all("base");
repo.write("src/lib.rs", "pub fn value() -> u32 { 2 }\n");
let a = capture_supersession_round(repo.path(), None);
let b = capture_supersession_round(repo.path(), Some(&a));
let c = capture_supersession_round(repo.path(), Some(&a));
assert_ne!(b, c, "the two successors must be distinct");
let objects = Inspector::spawn(repo.path()).get_json("/api/threads");
(objects, a, b, c)
}
fn forked_objects() -> serde_json::Value {
build_fork().0
}
#[test]
fn laid_out_dag_places_competing_heads_as_equal_peers() {
let objects = forked_objects();
let thread = &objects["threads"][0];
assert_eq!(thread["competing"], true);
let laid = &thread["laidOut"];
let nodes = laid["nodes"].as_array().expect("laidOut.nodes");
assert_eq!(nodes.len(), 3, "three revisions in the fork");
let (bw, bh) = (
laid["bounds"]["w"].as_f64().unwrap(),
laid["bounds"]["h"].as_f64().unwrap(),
);
assert!(bw > 0.0 && bh > 0.0);
const MARGIN: f64 = 1.0;
for n in nodes {
let (x, y, w, h) = (
n["x"].as_f64().unwrap(),
n["y"].as_f64().unwrap(),
n["w"].as_f64().unwrap(),
n["h"].as_f64().unwrap(),
);
assert!(
x - w / 2.0 >= MARGIN && x + w / 2.0 <= bw - MARGIN,
"node box is inset from the viewBox width so its stroke is not clipped"
);
assert!(
y - h / 2.0 >= MARGIN && y + h / 2.0 <= bh - MARGIN,
"node box is inset from the viewBox height so its stroke is not clipped"
);
}
let heads: Vec<&serde_json::Value> = nodes.iter().filter(|n| n["isHead"] == true).collect();
let superseded: Vec<&serde_json::Value> =
nodes.iter().filter(|n| n["isSuperseded"] == true).collect();
assert_eq!(heads.len(), 2, "two competing heads");
assert_eq!(superseded.len(), 1, "one superseded root");
let edges = laid["edges"].as_array().unwrap();
assert_eq!(edges.len(), 2);
let root_id = superseded[0]["id"].as_str().unwrap();
for e in edges {
assert_eq!(e["to"], root_id, "every edge points at the superseded root");
assert!(e["from"] != root_id, "the root supersedes nothing");
assert!(
e["path"].as_array().unwrap().len() >= 2,
"a routed polyline has >=2 points"
);
}
let head_ys: Vec<f64> = heads.iter().map(|n| n["y"].as_f64().unwrap()).collect();
assert!(
(head_ys[0] - head_ys[1]).abs() < 1.0,
"competing heads share a rank (equal y)"
);
let min_head_y = head_ys.iter().cloned().fold(f64::INFINITY, f64::min);
for n in nodes {
if n["isHead"] != true {
assert!(
n["y"].as_f64().unwrap() >= min_head_y,
"no non-head node sits above a head"
);
}
}
}
#[test]
fn forked_thread_payload_surfaces_competing_heads_and_peer_layout() {
let (objects, a, b, c) = build_fork();
assert_eq!(objects["threadCount"], 1);
let thread = &objects["threads"][0];
assert_eq!(thread["competing"], true);
let heads: Vec<&str> = thread["heads"]
.as_array()
.unwrap()
.iter()
.map(|h| h.as_str().unwrap())
.collect();
assert_eq!(heads.len(), 2);
assert!(heads.contains(&b.as_str()) && heads.contains(&c.as_str()));
let superseders: Vec<&str> = objects["supersededBy"][&a]
.as_array()
.unwrap()
.iter()
.map(|s| s.as_str().unwrap())
.collect();
assert!(superseders.contains(&b.as_str()) && superseders.contains(&c.as_str()));
let laid = &thread["laidOut"];
let nodes = laid["nodes"].as_array().unwrap();
assert_eq!(nodes.len(), 3);
let head_nodes: Vec<&serde_json::Value> =
nodes.iter().filter(|n| n["isHead"] == true).collect();
assert_eq!(head_nodes.len(), 2, "two competing head nodes");
let head_ys: Vec<f64> = head_nodes
.iter()
.map(|n| n["y"].as_f64().unwrap())
.collect();
assert!(
(head_ys[0] - head_ys[1]).abs() < 1.0,
"competing heads share a rank"
);
let min_head_y = head_ys.iter().cloned().fold(f64::INFINITY, f64::min);
for n in nodes {
if n["isHead"] != true {
assert!(
n["y"].as_f64().unwrap() >= min_head_y,
"no non-head node sits above a head"
);
}
}
let edges = laid["edges"].as_array().unwrap();
assert_eq!(edges.len(), 2);
let root_id = nodes.iter().find(|n| n["isSuperseded"] == true).unwrap()["id"]
.as_str()
.unwrap();
for e in edges {
assert_eq!(
e["to"], root_id,
"`from` supersedes `to` = the superseded root"
);
}
}
#[test]
fn api_revision_shows_a_superseded_revision_exactly() {
let repo = GitRepo::new();
repo.write("src/lib.rs", "pub fn value() -> u32 { 1 }\n");
repo.commit_all("base");
repo.write("src/lib.rs", "pub fn value() -> u32 { 2 }\n");
let root = capture_supersession_round(repo.path(), None);
let _b = capture_supersession_round(repo.path(), Some(&root));
let _c = capture_supersession_round(repo.path(), Some(&root));
let inspector = Inspector::spawn(repo.path());
let shown = inspector.get_json(&format!("/api/revisions/{}", root.replace(':', "%3A")));
assert_eq!(
shown["revision"]["id"], root,
"the superseded root shows itself exactly, not a forward-resolved head: {shown}"
);
}
#[test]
fn laid_out_dag_nodes_are_sized_to_the_short_label_not_the_full_id() {
let objects = forked_objects();
let nodes = objects["threads"][0]["laidOut"]["nodes"]
.as_array()
.unwrap();
for n in nodes {
assert!(
n["w"].as_f64().unwrap() < 400.0,
"node box sized to the short label, not the full id: w={}",
n["w"]
);
}
}
#[test]
fn laid_out_dag_degenerate_single_node_thread_has_no_edges() {
let repo = GitRepo::new();
repo.write("src/lib.rs", "pub fn value() -> u32 { 1 }\n");
repo.commit_all("base");
repo.write("src/lib.rs", "pub fn value() -> u32 { 2 }\n");
let _root = capture_supersession_round(repo.path(), None);
let objects = Inspector::spawn(repo.path()).get_json("/api/threads");
let laid = &objects["threads"][0]["laidOut"];
assert_eq!(laid["nodes"].as_array().unwrap().len(), 1);
assert_eq!(laid["edges"].as_array().unwrap().len(), 0);
assert_eq!(laid["nodes"][0]["isHead"], true);
}
#[test]
fn dag_edges_carry_a_directional_arrowhead_marker() {
let repo = GitRepo::new();
repo.write("src/lib.rs", "pub fn value() -> u32 { 1 }\n");
repo.commit_all("base");
let _ = capture_supersession_round(repo.path(), None);
let insp = Inspector::spawn(repo.path());
let css = insp.get_text("/app.css");
assert!(
css.contains("dag-arrow-head-traced"),
"the traced arrowhead marker is styled with the accent fill"
);
}
#[test]
fn dag_default_edges_use_a_dedicated_contrast_token() {
let repo = GitRepo::new();
let insp = Inspector::spawn(repo.path());
let css = insp.get_text("/app.css");
assert!(
css.contains("--dag-edge"),
"the default DAG edge/arrow should have a dedicated contrast token"
);
let edge_block = css
.split(".dag-edge {")
.nth(1)
.and_then(|rest| rest.split('}').next())
.expect(".dag-edge block");
assert!(
edge_block.contains("stroke: var(--dag-edge)")
&& !edge_block.contains("stroke: var(--border)"),
"default DAG edges should not use only the quiet border token: {edge_block}"
);
let arrow_block = css
.split(".dag-arrow-head {")
.nth(1)
.and_then(|rest| rest.split('}').next())
.expect(".dag-arrow-head block");
assert!(
arrow_block.contains("fill: var(--dag-edge)")
&& !arrow_block.contains("fill: var(--border)"),
"default DAG arrowheads should share the stronger edge token: {arrow_block}"
);
}