5e97f6ac6a
Completes ADR-0029's implementation: the friendly-error layer now names the rule a CHECK violation broke, and the typing-surface matrix covers the whole constraint grammar. CHECK-violation friendly error (ADR-0029 §10): - enrich_dsl_failure gains a CHECK branch — it reads the column from the engine's `CHECK constraint failed: <column>` message, then resolves the table, the offending value, and the column's compiled CHECK expression. - FailureContext / TranslateContext carry the resolved check_rule; translate_check renders "the value <v> breaks the rule `<rule>`" when it is known, falling back to the plain hint otherwise. Typing-surface matrix: a new `constraints` submodule, 14 cells covering the create-table / add-column constraint suffix and the add-constraint / drop-constraint commands (174 → 188). 16 tests added (1 translate unit, 1 enrichment integration, 14 matrix cells).
150 lines
4.9 KiB
Rust
150 lines
4.9 KiB
Rust
//! Matrix coverage for ADR-0029 column constraints — the
|
|
//! `not null` / `unique` / `default` / `check` suffix on
|
|
//! `create table` & `add column`, and the `add constraint` /
|
|
//! `drop constraint` commands (ADR-0029 §2).
|
|
|
|
use crate::typing_surface::*;
|
|
use rdbms_playground::input_render::InputState;
|
|
|
|
// --- create table / add column constraint suffix --------------
|
|
|
|
#[test]
|
|
fn create_table_default_suffix_parses() {
|
|
let a = assess_at_end(
|
|
"create table Books with pk isbn(text) default '000'",
|
|
&schema_empty(),
|
|
);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
assert_eq!(a.parse_result.as_deref(), Ok("CreateTable"));
|
|
crate::snap!("create_table_default_suffix", a);
|
|
}
|
|
|
|
#[test]
|
|
fn create_table_check_suffix_parses() {
|
|
let a = assess_at_end(
|
|
"create table Ages with pk age(int) check (age >= 0)",
|
|
&schema_empty(),
|
|
);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
assert_eq!(a.parse_result.as_deref(), Ok("CreateTable"));
|
|
crate::snap!("create_table_check_suffix", a);
|
|
}
|
|
|
|
#[test]
|
|
fn add_column_check_suffix_parses() {
|
|
let a = assess_at_end(
|
|
"add column to Customers: note (text) check (note like 'A%')",
|
|
&schema_multi_table(),
|
|
);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
assert_eq!(a.parse_result.as_deref(), Ok("AddColumn"));
|
|
crate::snap!("add_column_check_suffix", a);
|
|
}
|
|
|
|
#[test]
|
|
fn add_column_after_type_offers_constraint_keywords() {
|
|
let a = assess_at_end(
|
|
"add column to Customers: note (text) ",
|
|
&schema_multi_table(),
|
|
);
|
|
assert_candidate_present(&a, &["not", "unique", "default", "check"]);
|
|
crate::snap!("add_column_constraint_suffix", a);
|
|
}
|
|
|
|
// --- add constraint -------------------------------------------
|
|
|
|
#[test]
|
|
fn after_add_offers_constraint_branch() {
|
|
let a = assess_at_end("add ", &schema_multi_table());
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(&a, &["constraint"]);
|
|
crate::snap!("after_add_constraint_branch", a);
|
|
}
|
|
|
|
#[test]
|
|
fn add_constraint_offers_the_four_kinds() {
|
|
let a = assess_at_end("add constraint ", &schema_multi_table());
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(&a, &["not", "unique", "default", "check"]);
|
|
crate::snap!("add_constraint_kinds", a);
|
|
}
|
|
|
|
#[test]
|
|
fn add_constraint_to_offers_table_names() {
|
|
let a = assess_at_end("add constraint not null to ", &schema_multi_table());
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(&a, &["Customers", "Orders"]);
|
|
crate::snap!("add_constraint_to_tables", a);
|
|
}
|
|
|
|
#[test]
|
|
fn add_constraint_dot_narrows_to_table_columns() {
|
|
let a = assess_at_end("add constraint unique to Orders.", &schema_multi_table());
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(&a, &["OrderId", "CustId", "Total"]);
|
|
assert_no_candidate_named(&a, &["id", "Name"]);
|
|
crate::snap!("add_constraint_dot_columns", a);
|
|
}
|
|
|
|
#[test]
|
|
fn complete_add_constraint_not_null_parses() {
|
|
let a = assess_at_end(
|
|
"add constraint not null to Customers.Name",
|
|
&schema_multi_table(),
|
|
);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
assert_eq!(a.parse_result.as_deref(), Ok("AddConstraint"));
|
|
crate::snap!("add_constraint_not_null_complete", a);
|
|
}
|
|
|
|
#[test]
|
|
fn complete_add_constraint_default_parses() {
|
|
let a = assess_at_end(
|
|
"add constraint default 0 to Orders.Total",
|
|
&schema_multi_table(),
|
|
);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
assert_eq!(a.parse_result.as_deref(), Ok("AddConstraint"));
|
|
crate::snap!("add_constraint_default_complete", a);
|
|
}
|
|
|
|
#[test]
|
|
fn complete_add_constraint_check_parses() {
|
|
let a = assess_at_end(
|
|
"add constraint check (Total >= 0) to Orders.Total",
|
|
&schema_multi_table(),
|
|
);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
assert_eq!(a.parse_result.as_deref(), Ok("AddConstraint"));
|
|
crate::snap!("add_constraint_check_complete", a);
|
|
}
|
|
|
|
// --- drop constraint ------------------------------------------
|
|
|
|
#[test]
|
|
fn after_drop_offers_constraint_branch() {
|
|
let a = assess_at_end("drop ", &schema_multi_table());
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(&a, &["constraint"]);
|
|
crate::snap!("after_drop_constraint_branch", a);
|
|
}
|
|
|
|
#[test]
|
|
fn drop_constraint_offers_the_four_kinds() {
|
|
let a = assess_at_end("drop constraint ", &schema_multi_table());
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(&a, &["not", "unique", "default", "check"]);
|
|
crate::snap!("drop_constraint_kinds", a);
|
|
}
|
|
|
|
#[test]
|
|
fn complete_drop_constraint_parses() {
|
|
let a = assess_at_end(
|
|
"drop constraint unique from Customers.Name",
|
|
&schema_multi_table(),
|
|
);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
assert_eq!(a.parse_result.as_deref(), Ok("DropConstraint"));
|
|
crate::snap!("drop_constraint_complete", a);
|
|
}
|