feat: show relationship <name> renders a styled two-table diagram (ADR-0044)
The first wired slice of relationship visualization (V1). `show relationship <name>` now renders the relationship as two full structure boxes joined by a width-jogging connector (child-left / parent-right, n…1 cardinality, on delete/update actions), styled App-side, with a vertical-stack fallback for narrow terminals. - db.rs: RelationshipDiagramData + show_relationship worker path (structured data: the relationship + both endpoint TableDescriptions) - runtime.rs: named relationships route to the structured outcome (boxed); other show <kind> forms stay prose - app.rs/event.rs/ui.rs: DslShowRelationshipSucceeded rendered App-side; new diagram OutputStyleClass variants; App::last_output_width from ui.rs - output_render.rs: styled Seg layout engine (boxes, connector routing, side-by-side + vertical), composing the ADR-0016 box primitives Tests: 4 unit + 4 integration; full suite 2201 pass / 0 fail / 1 ignored; clippy nursery clean. requirements.md V1 stays [/] (show table diagrams, compound routing, DDL-echo wiring remain).
This commit is contained in:
@@ -357,3 +357,100 @@ fn app_renders_show_list_lines_as_system_output() {
|
||||
"item line rendered",
|
||||
);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// ADR-0044 — `show relationship <name>` renders a diagram
|
||||
// =================================================================
|
||||
|
||||
#[test]
|
||||
fn show_relationship_worker_returns_structured_diagram_data() {
|
||||
let (_p, db, _dir) = open_project_db();
|
||||
let rt = rt();
|
||||
rt.block_on(seed_schema(&db));
|
||||
let data = rt
|
||||
.block_on(db.show_relationship("orders_customer".to_string()))
|
||||
.expect("show_relationship ok")
|
||||
.expect("relationship found");
|
||||
assert_eq!(data.rel.name, "orders_customer");
|
||||
// child = FK holder, parent = referenced (ADR-0044 left/right).
|
||||
assert_eq!(data.child.name, "Orders");
|
||||
assert_eq!(data.parent.name, "Customers");
|
||||
assert_eq!(data.rel.child_columns, vec!["customer_id".to_string()]);
|
||||
assert_eq!(data.rel.parent_columns, vec!["id".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_relationship_worker_returns_none_for_unknown_name() {
|
||||
let (_p, db, _dir) = open_project_db();
|
||||
let rt = rt();
|
||||
rt.block_on(seed_schema(&db));
|
||||
assert!(
|
||||
rt.block_on(db.show_relationship("nope".to_string()))
|
||||
.expect("ok")
|
||||
.is_none(),
|
||||
"unknown relationship → None",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_renders_show_relationship_as_a_styled_diagram() {
|
||||
let (_p, db, _dir) = open_project_db();
|
||||
let rt = rt();
|
||||
rt.block_on(seed_schema(&db));
|
||||
let data = rt
|
||||
.block_on(db.show_relationship("orders_customer".to_string()))
|
||||
.expect("ok")
|
||||
.expect("found");
|
||||
|
||||
let mut app = App::new();
|
||||
app.output.push_back(rdbms_playground::app::OutputLine::echo(
|
||||
"show relationship orders_customer",
|
||||
Mode::Simple,
|
||||
));
|
||||
app.update(AppEvent::DslShowRelationshipSucceeded {
|
||||
command: Command::ShowList {
|
||||
kind: ShowListKind::Relationships,
|
||||
name: Some("orders_customer".to_string()),
|
||||
},
|
||||
data: Some(data),
|
||||
});
|
||||
let text: String = app
|
||||
.output
|
||||
.iter()
|
||||
.map(|l| l.text.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
// Both tables, box-drawing, the connector arrow, the actions line.
|
||||
assert!(text.contains("Orders"), "child box: {text}");
|
||||
assert!(text.contains("Customers"), "parent box: {text}");
|
||||
assert!(text.contains('┌') && text.contains('│'), "box drawing: {text}");
|
||||
assert!(text.contains('▶'), "connector arrow: {text}");
|
||||
assert!(text.contains("on delete cascade"), "actions: {text}");
|
||||
// The diagram lines are styled (per-span runs), not plain system.
|
||||
assert!(
|
||||
app.output.iter().any(|l| l.styled_runs.is_some()),
|
||||
"diagram lines carry styled runs",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_show_relationship_not_found_shows_friendly_line() {
|
||||
let mut app = App::new();
|
||||
app.output.push_back(rdbms_playground::app::OutputLine::echo(
|
||||
"show relationship nope",
|
||||
Mode::Simple,
|
||||
));
|
||||
app.update(AppEvent::DslShowRelationshipSucceeded {
|
||||
command: Command::ShowList {
|
||||
kind: ShowListKind::Relationships,
|
||||
name: Some("nope".to_string()),
|
||||
},
|
||||
data: None,
|
||||
});
|
||||
assert!(
|
||||
app.output
|
||||
.iter()
|
||||
.any(|l| l.text == "No relationship named `nope`."),
|
||||
"friendly not-found line",
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user