Matrix: create table, DDL, and app-command coverage

55 tests covering create table, drop column, drop relationship
(endpoints + by-name), add relationship, rename/change column, and
all app-lifecycle commands. The drop-column and relationship tests
drove the §2.2 writes_table fix in the previous commit.

Documents one UX wrinkle as a flagged finding: partial entry words
(`qu`) classify as DefiniteErrorAt — same as unknown commands —
because the walker only engages on a complete entry word.

859 baseline -> 989 passing; 1 ignored (pre-existing doc-test).
This commit is contained in:
claude@clouddev1
2026-05-15 20:50:56 +00:00
parent 216e7ba61b
commit c7ecc64757
63 changed files with 2656 additions and 6 deletions
+135 -1
View File
@@ -1 +1,135 @@
//! Submodule stub — populated in subsequent tasks.
//! 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_colon() {
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_colon_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_colon", 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);
}