feat: ADR-0035 Amendment 1 follow-up — enrich replay errors + close message gaps
- F2-broad: replay failures now render with real schema context instead of
a contextless friendly_message(). Extract App::build_translate_context into
the shared App::translate_context_for(command, facts, verbosity); run_replay
enriches via enrich_dsl_failure + that builder. ctx_* fallbacks degrade to
neutral prose so the rare non-replay contextless callsites can't leak raw
{name} either. (SQL INSERT/UPDATE values aren't retained — ADR-0033 verbatim
— so those show real table/column + neutral "that value".)
- Gap C: SQL ALTER … ADD FOREIGN KEY on a missing child column refuses with an
SQL-appropriate "add it first", not the DSL-only --create-fk flag.
- Gap B: dropping a single-column-UNIQUE column refuses with a pointer to
`drop constraint unique from T.col` (was an opaque generic refusal).
- Gap D: 4e drop/rename CHECK-guard + 4f change-type FK-guard refusals reworded
to explain why; static_refusal reasons left as-is.
Tests: +4, 3 strengthened. 1926 pass / 0 fail / 0 skip; clippy clean.
This commit is contained in:
@@ -613,6 +613,34 @@ fn e2e_drop_composite_unique_is_one_undo_step() {
|
||||
assert!(has_unique(), "one undo restored the composite UNIQUE");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn e2e_add_foreign_key_missing_child_column_refuses_without_dsl_flag() {
|
||||
// Gap C (ADR-0035 Amendment 1): the SQL ADD FOREIGN KEY refusal for a
|
||||
// missing child column must speak SQL — not suggest the DSL-only
|
||||
// `--create-fk` flag (which `do_add_relationship` mentions for the
|
||||
// simple `add relationship` surface).
|
||||
let (project, db, _d) = open();
|
||||
let r = rt();
|
||||
std::fs::write(
|
||||
project.path().join("fk.commands"),
|
||||
"create table P with pk id(int)\n\
|
||||
create table C with pk cid(int)\n\
|
||||
alter table C add foreign key (pid) references P(id)\n",
|
||||
)
|
||||
.expect("write");
|
||||
let events = r.block_on(run_replay(&db, project.path(), "fk.commands"));
|
||||
let AppEvent::ReplayFailed { error, .. } = events.last().expect("an event") else {
|
||||
panic!("expected ReplayFailed; events: {events:?}");
|
||||
};
|
||||
assert!(!error.contains("--create-fk"), "no DSL flag in the SQL refusal; got: {error}");
|
||||
assert!(error.contains("pid"), "names the missing column; got: {error}");
|
||||
assert!(
|
||||
error.to_lowercase().contains("add it first")
|
||||
|| error.to_lowercase().contains("does not exist"),
|
||||
"actionable wording; got: {error}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn e2e_add_foreign_key_creates_an_enforced_relationship() {
|
||||
let (project, db, _d) = open();
|
||||
|
||||
Reference in New Issue
Block a user