d9a98bbd49
`create table … with pk` parsed column types as `name:type`,
while `add column` uses `name(type)`. Unify on the parens
form so column-type syntax is consistent across the DSL:
create table T with pk id(serial), name(text)
Only `COL_SPEC` changes (`:` → `( … )`); `build_create_table`
reads columns by role, so it is unaffected. The `:` that
separates table from column in `add column` / `drop column`
is unchanged. Sweeps the test suite, the typing-surface
matrix (two `after_colon` cells renamed to `after_paren`,
4 snapshots regenerated), the friendly catalog's usage
templates, ADR-0009's example, and requirements.md.
1039 passing / 0 failing / 1 ignored; clippy clean.
136 lines
4.3 KiB
Rust
136 lines
4.3 KiB
Rust
//! Matrix coverage for `create table T with pk [<col>(<type>)[, ...]]`
|
|
//! (ADR-0005, ADR-0009).
|
|
|
|
use crate::typing_surface::*;
|
|
use rdbms_playground::input_render::{AmbientHint, InputState};
|
|
|
|
#[test]
|
|
fn after_create_expects_table() {
|
|
let schema = schema_empty();
|
|
let a = assess_at_end("create ", &schema);
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(&a, &["table"]);
|
|
crate::snap!("after_create", a);
|
|
}
|
|
|
|
#[test]
|
|
fn after_table_keyword_offers_no_candidates_for_new_name() {
|
|
// The table-name slot is IdentSource::NewName — the user
|
|
// invents the name, so no candidates from the schema. The
|
|
// hint resolver surfaces a ForceProse "Type a name [then ...]"
|
|
// affordance instead.
|
|
let schema = schema_empty();
|
|
let a = assess_at_end("create table ", &schema);
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert!(
|
|
matches!(&a.hint, Some(AmbientHint::Prose(_))),
|
|
"expected Prose at NewName slot, got {:?}",
|
|
a.hint,
|
|
);
|
|
crate::snap!("after_table_keyword", a);
|
|
}
|
|
|
|
#[test]
|
|
fn after_new_table_name_expects_with() {
|
|
let schema = schema_empty();
|
|
let a = assess_at_end("create table Customers ", &schema);
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(&a, &["with"]);
|
|
crate::snap!("after_new_table_name", a);
|
|
}
|
|
|
|
#[test]
|
|
fn after_with_expects_pk() {
|
|
let schema = schema_empty();
|
|
let a = assess_at_end("create table Customers with ", &schema);
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(&a, &["pk"]);
|
|
crate::snap!("after_with", a);
|
|
}
|
|
|
|
#[test]
|
|
fn create_table_with_pk_default_parses() {
|
|
let schema = schema_empty();
|
|
let a = assess_at_end("create table Customers with pk", &schema);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
assert_eq!(a.parse_result.as_deref(), Ok("CreateTable"));
|
|
crate::snap!("with_pk_default", a);
|
|
}
|
|
|
|
#[test]
|
|
fn after_pk_word_does_not_re_offer_pk() {
|
|
// Handoff-12 regression: at the cursor position right after
|
|
// `with pk`, the completion engine must NOT re-offer `pk` as
|
|
// a candidate. The full input parses and the partial-prefix
|
|
// filter drops it.
|
|
let schema = schema_empty();
|
|
let a = assess_at_end("create table Customers with pk", &schema);
|
|
let cands = completion_candidate_texts(&a);
|
|
assert!(
|
|
!cands.contains(&"pk".to_string()),
|
|
"should NOT re-suggest `pk` (handoff-12 regression), got: {cands:?}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn after_pk_space_with_col_name_typed_expects_paren() {
|
|
let schema = schema_empty();
|
|
let a = assess_at_end("create table Customers with pk Code", &schema);
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
crate::snap!("after_col_name", a);
|
|
}
|
|
|
|
#[test]
|
|
fn after_paren_expects_type_candidates() {
|
|
let schema = schema_empty();
|
|
let a = assess_at_end(
|
|
"create table Customers with pk Code(",
|
|
&schema,
|
|
);
|
|
assert!(matches!(a.state, InputState::IncompleteAtEof));
|
|
assert_candidate_present(
|
|
&a,
|
|
&["text", "int", "serial", "shortid", "bool"],
|
|
);
|
|
crate::snap!("after_paren", a);
|
|
}
|
|
|
|
#[test]
|
|
fn create_table_with_explicit_pk_parses() {
|
|
let schema = schema_empty();
|
|
let a = assess_at_end(
|
|
"create table Customers with pk Code(text)",
|
|
&schema,
|
|
);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
crate::snap!("with_explicit_pk", a);
|
|
}
|
|
|
|
#[test]
|
|
fn create_table_with_compound_pk_parses() {
|
|
let schema = schema_empty();
|
|
let a = assess_at_end(
|
|
"create table Memberships with pk UserId(int), GroupId(int)",
|
|
&schema,
|
|
);
|
|
assert!(matches!(a.state, InputState::Valid));
|
|
crate::snap!("with_compound_pk", a);
|
|
}
|
|
|
|
#[test]
|
|
fn create_table_without_with_pk_is_incomplete() {
|
|
// Tables need at least one column (parse.custom.
|
|
// create_table_needs_pk) — submitting `create table T`
|
|
// alone is rejected. Classification depends on whether the
|
|
// walker treats `with pk` as required or optional at this
|
|
// position.
|
|
let schema = schema_empty();
|
|
let a = assess_at_end("create table Customers", &schema);
|
|
assert!(
|
|
!matches!(a.state, InputState::Valid),
|
|
"create table without `with pk` must not be Valid, got {:?}",
|
|
a.state,
|
|
);
|
|
crate::snap!("no_with_pk", a);
|
|
}
|