diff --git a/src/app.rs b/src/app.rs index 4ff1832..d503771 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3011,6 +3011,19 @@ mod tests { .expect("an echo line"); assert_eq!(echo_idx, ok_idx + 1, "echo sits immediately beneath [ok]: {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 { @@ -3122,6 +3135,79 @@ mod tests { 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] fn polished_echo_carries_teaching_echo_kind_and_caveat_a_hint_span() { // 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"), "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!( texts[ok_idx + 3] .contains("Executing SQL: ALTER TABLE Customers DROP COLUMN Email"),