test: /runda round 2 — pin TeachingEcho kind + cat-3 prose wiring

Second Devil's-Advocate pass over the now-complete ADR-0038 closes
three minor test-coverage gaps the polish (2aab457) left behind. No
production code changes; src/app.rs only, +98 lines of tests.

* bucket_a_success_events_render_the_teaching_echo_beneath_ok and
  bucket_b_multi_line_echo_renders_one_line_per_statement_beneath_ok
  only checked line *text*. After the polish, every echo line is
  pushed via `push_teaching_echo` and carries
  `OutputKind::TeachingEcho` — the kind is what makes
  `ui::render_output_line` fire the dim-prefix + advanced-lex
  branch. Without a kind assertion, a future refactor that regressed
  `push_teaching_echo` to emit `System` would leave the text intact
  but silently break the styling. The `assert_echo_beneath_ok`
  helper now pins kind == TeachingEcho on every event arm covered
  by the bucket_a test, and the bucket_b test asserts the same for
  each of the three multi-statement lines.

* add_column_client_side_notes_render_as_category_three_prose (new)
  pins the broader-scope polish: `handle_dsl_add_column_success`'s
  illuminating client_side_notes (shortid / serial auto-fill,
  per `client_side.auto_fill_*` keys) now route through
  `push_category_three_prose`, producing a System line with a
  whole-text Hint span. The user-confirmed scope extension was
  recorded in ADR-0038's Status block; this test makes the wiring
  enforceable. The closely-related caveat path was already pinned
  by polished_echo_carries_teaching_echo_kind_and_caveat_a_hint_span;
  this completes the cat-3 prose coverage symmetrically.

Tests: 2020 passed / 0 failed / 1 ignored (pre-existing); clippy
clean (`--all-targets -D warnings`, nursery). Nothing to escalate.
This commit is contained in:
claude@clouddev1
2026-05-28 12:39:25 +00:00
parent 63b2927f10
commit 6840b928f9
+98
View File
@@ -3011,6 +3011,19 @@ mod tests {
.expect("an echo line"); .expect("an echo line");
assert_eq!(echo_idx, ok_idx + 1, "echo sits immediately beneath [ok]: {texts:?}"); assert_eq!(echo_idx, ok_idx + 1, "echo sits immediately beneath [ok]: {texts:?}");
assert!(texts[echo_idx].contains(expected), "echo carries the SQL: {texts:?}"); assert!(texts[echo_idx].contains(expected), "echo carries the SQL: {texts:?}");
// ADR-0038 §4 polish: every success arm now wires the echo as
// `OutputKind::TeachingEcho` so `ui::render_output_line` fires
// the dim-prefix + advanced-lex custom branch. Pinning this
// here guards every event-arm wiring covered by this test —
// a future refactor that regressed `push_teaching_echo` to
// emit `System` would leave the text intact but break the
// styling, and would be caught here.
assert_eq!(
app.output[echo_idx].kind,
OutputKind::TeachingEcho,
"echo line carries TeachingEcho kind: {:?}",
app.output[echo_idx],
);
} }
fn empty_data() -> DataResult { fn empty_data() -> DataResult {
@@ -3122,6 +3135,79 @@ mod tests {
assert_echo_beneath_ok(&app, "ALTER TABLE T ALTER COLUMN c SET DATA TYPE text"); assert_echo_beneath_ok(&app, "ALTER TABLE T ALTER COLUMN c SET DATA TYPE text");
} }
#[test]
fn add_column_client_side_notes_render_as_category_three_prose() {
// ADR-0038 §4 polish (broader scope): the existing illuminating
// `client_side_notes` on an `AddColumnResult` (e.g. the shortid
// / serial auto-fill messages) now route through
// `push_category_three_prose` — a System line with a whole-text
// Hint span — so every category-3 prose line renders dim
// consistently per §6, not just the new DontConvert caveat.
//
// The broader scope was user-confirmed in the polish session
// (see ADR-0038 Status); this test pins that wiring so a
// regression to plain `note_system` would be caught.
use crate::db::{AddColumnResult, ColumnDescription};
let mut app = App::new();
app.update(AppEvent::DslAddColumnSucceeded {
command: Command::AddColumn {
table: "Customers".to_string(),
column: "code".to_string(),
ty: Type::ShortId,
not_null: false,
unique: false,
default: None,
check: None,
},
result: AddColumnResult {
description: TableDescription {
name: "Customers".to_string(),
columns: vec![ColumnDescription {
name: "code".to_string(),
user_type: Some(Type::ShortId),
sqlite_type: "TEXT".to_string(),
notnull: false,
primary_key: false,
unique: false,
default: None,
check: None,
}],
outbound_relationships: Vec::new(),
inbound_relationships: Vec::new(),
indexes: Vec::new(),
unique_constraints: Vec::new(),
check_constraints: Vec::new(),
},
// A representative illuminating note — the wording is
// the one the worker would emit via the
// `client_side.auto_fill_add_shortid` i18n key.
client_side_notes: vec![
"[client-side] 5 row(s) given auto-generated shortid values. In raw SQL this would need an explicit UPDATE to populate.".to_string(),
],
},
echo: None, // simple-mode submission, no echo
});
let note_line = app
.output
.iter()
.find(|l| l.text.starts_with("[client-side]"))
.expect("the illuminating client_side note");
assert_eq!(note_line.kind, OutputKind::System);
let runs = note_line
.styled_runs
.as_ref()
.expect("client_side note carries a styled-runs payload after the polish");
assert_eq!(runs.len(), 1);
assert_eq!(runs[0].class, OutputStyleClass::Hint);
assert_eq!(
runs[0].byte_range,
(0, note_line.text.len()),
"the dim span covers the whole prose body — same shape as the caveat",
);
}
#[test] #[test]
fn polished_echo_carries_teaching_echo_kind_and_caveat_a_hint_span() { fn polished_echo_carries_teaching_echo_kind_and_caveat_a_hint_span() {
// ADR-0038 §4 styled-runs polish: the App-side wiring places // ADR-0038 §4 styled-runs polish: the App-side wiring places
@@ -3310,6 +3396,18 @@ mod tests {
texts[ok_idx + 2].contains("Executing SQL: DROP INDEX Customers_Email_Day_idx"), texts[ok_idx + 2].contains("Executing SQL: DROP INDEX Customers_Email_Day_idx"),
"second echo line: {texts:?}", "second echo line: {texts:?}",
); );
// ADR-0038 §4 polish: every one of the multi-statement echo lines
// carries TeachingEcho — the polish styling fires per line. A
// regression that pushed the multi-line case as System would
// leave the text intact but break the per-line styling.
for (offset, label) in [(1, "first"), (2, "second"), (3, "third")] {
assert_eq!(
app.output[ok_idx + offset].kind,
OutputKind::TeachingEcho,
"{label} echo line carries TeachingEcho: {:?}",
app.output[ok_idx + offset],
);
}
assert!( assert!(
texts[ok_idx + 3] texts[ok_idx + 3]
.contains("Executing SQL: ALTER TABLE Customers DROP COLUMN Email"), .contains("Executing SQL: ALTER TABLE Customers DROP COLUMN Email"),