diff --git a/tests/typing_surface/delete_all_rows.rs b/tests/typing_surface/delete_all_rows.rs index b2d5ff4..c49fdd5 100644 --- a/tests/typing_surface/delete_all_rows.rs +++ b/tests/typing_surface/delete_all_rows.rs @@ -1 +1,30 @@ -//! Submodule stub — populated in subsequent tasks. +//! Matrix coverage for `delete from T --all-rows` (ADR-0014). + +use crate::typing_surface::*; +use rdbms_playground::input_render::InputState; + +#[test] +fn complete_delete_all_rows_parses() { + let schema = schema_serial_pk(); + let a = assess_at_end("delete from Customers --all-rows", &schema); + assert!(matches!(a.state, InputState::Valid)); + assert_eq!(a.parse_result.as_deref(), Ok("Delete")); + crate::snap!("complete_all_rows", a); +} + +#[test] +fn delete_without_filter_clause_is_incomplete() { + // Per ADR-0014 — delete requires WHERE or --all-rows. + let schema = schema_serial_pk(); + let a = assess_at_end("delete from Customers", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + crate::snap!("no_filter", a); +} + +#[test] +fn delete_partial_flag_is_incomplete() { + let schema = schema_serial_pk(); + let a = assess_at_end("delete from Customers --all", &schema); + assert!(!matches!(a.state, InputState::Valid)); + crate::snap!("partial_flag", a); +} diff --git a/tests/typing_surface/delete_with_where.rs b/tests/typing_surface/delete_with_where.rs index b2d5ff4..000f623 100644 --- a/tests/typing_surface/delete_with_where.rs +++ b/tests/typing_surface/delete_with_where.rs @@ -1 +1,87 @@ -//! Submodule stub — populated in subsequent tasks. +//! Matrix coverage for `delete from T where col=val` (ADR-0014). + +use crate::typing_surface::*; +use rdbms_playground::input_render::InputState; + +#[test] +fn after_delete_expects_from() { + let schema = schema_serial_pk(); + let a = assess_at_end("delete ", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + assert_candidate_present(&a, &["from"]); + crate::snap!("after_delete", a); +} + +#[test] +fn after_from_offers_table_names() { + let schema = schema_multi_table(); + let a = assess_at_end("delete from ", &schema); + assert_candidate_present(&a, &["Customers", "Orders"]); + crate::snap!("after_from", a); +} + +#[test] +fn after_table_expects_filter_clause() { + let schema = schema_serial_pk(); + let a = assess_at_end("delete from Customers ", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + assert_candidate_present(&a, &["where", "--all-rows"]); + crate::snap!("after_table_name", a); +} + +#[test] +fn after_where_offers_active_table_columns_no_leakage() { + let schema = schema_multi_table(); + let a = assess_at_end("delete from Customers where ", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + assert_candidate_present(&a, &["id", "Name"]); + assert_no_candidate_named(&a, &["OrderId", "CustId", "Total"]); + crate::snap!("after_where", a); +} + +#[test] +fn after_where_column_equals_offers_typed_prose() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "delete from Customers where Email=", + &schema, + ); + let prose = hint_prose(&a).unwrap_or_else(|| { + panic!("expected Prose, got {:?}", a.hint) + }); + assert!( + prose.contains("Email"), + "should name `Email`, got prose: {prose:?}", + ); + assert!( + prose.contains("quoted string"), + "should say `quoted string`, got prose: {prose:?}", + ); + crate::snap!("after_where_equals", a); +} + +#[test] +fn complete_delete_with_where_parses() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "delete from Customers where id=1", + &schema, + ); + assert!(matches!(a.state, InputState::Valid)); + assert_eq!(a.parse_result.as_deref(), Ok("Delete")); + crate::snap!("complete_delete", a); +} + +#[test] +fn delete_with_datetime_column_says_yyyy_mm_dd_t() { + let schema = schema_every_type(); + let a = assess_at_end("delete from Things where ts=", &schema); + let prose = hint_prose(&a).unwrap_or_else(|| { + panic!("expected Prose, got {:?}", a.hint) + }); + assert!( + prose.contains("ts"), + "should name `ts`, got prose: {prose:?}", + ); + crate::snap!("datetime_column", a); +} diff --git a/tests/typing_surface/insert_form_c.rs b/tests/typing_surface/insert_form_c.rs index b2d5ff4..b719230 100644 --- a/tests/typing_surface/insert_form_c.rs +++ b/tests/typing_surface/insert_form_c.rs @@ -1 +1,109 @@ -//! Submodule stub — populated in subsequent tasks. +//! Matrix coverage for `insert into T (vals)` (Form C — bare +//! value list, no `values` keyword). +//! +//! Form C shares the `( ... )` opener with Form A but resolves +//! the paren contents as values rather than column names. Per +//! handoff-12 §2.2 the Form C path is *type-unaware* — its +//! grammar uses the schemaless `INSERT_PAREN_LIST` shape, not +//! the typed `column_value_list`. Type validation happens at +//! bind time, not parse time. +//! +//! The previous commit's Form C/A disambiguation means +//! column-shaped items (idents) inside the parens now flag as +//! "did you mean Form A?". This file pins both the happy-path +//! (literals only) and the Form-A-recovery (column-shaped +//! items). + +use crate::typing_surface::*; +use rdbms_playground::input_render::InputState; + +#[test] +fn form_c_with_text_literals_parses() { + let schema = schema_text_pk(); + let a = assess_at_end( + "insert into Items ('SKU-1', 'Widget')", + &schema, + ); + assert!(matches!(a.state, InputState::Valid)); + assert_eq!(a.parse_result.as_deref(), Ok("Insert")); + crate::snap!("form_c_text_literals", a); +} + +#[test] +fn form_c_with_mixed_literals_parses() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "insert into Customers (1, 'Alice', 'a@b.c')", + &schema, + ); + assert!(matches!(a.state, InputState::Valid)); + assert_eq!(a.parse_result.as_deref(), Ok("Insert")); + crate::snap!("form_c_mixed_literals", a); +} + +#[test] +fn form_c_with_null_first_parses() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "insert into Customers (null, 'Alice', 'a@b.c')", + &schema, + ); + assert!(matches!(a.state, InputState::Valid)); + crate::snap!("form_c_null_first", a); +} + +#[test] +fn form_c_with_column_shaped_item_flags_as_form_a_in_progress() { + let schema = schema_serial_pk(); + let a = assess_at_end("insert into Customers (Name)", &schema); + assert!( + matches!(a.state, InputState::IncompleteAtEof), + "expected IncompleteAtEof (Form A recovery), got {:?}", + a.state, + ); + assert_candidate_present(&a, &["values"]); + crate::snap!("form_c_column_shaped_recovery", a); +} + +#[test] +fn form_c_with_two_columns_flags_as_form_a_in_progress() { + let schema = schema_serial_pk(); + let a = assess_at_end("insert into Customers (Name, Email)", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + assert_candidate_present(&a, &["values"]); + crate::snap!("form_c_two_columns_recovery", a); +} + +#[test] +fn form_c_type_unaware_grammar_accepts_decimal_for_int_column() { + // Form C's grammar uses INSERT_PAREN_LIST (the pre-Phase-D + // schemaless choice), so type mismatches aren't caught at + // parse time. Bind time catches them. Handoff §2.2 + // documents this as known. + let schema = schema_serial_pk(); + let a = assess_at_end( + "insert into Customers (3.14, 'Alice', 'a@b.c')", + &schema, + ); + assert!(matches!(a.state, InputState::Valid)); + crate::snap!("form_c_type_unaware", a); +} + +#[test] +fn form_c_in_progress_after_comma_is_incomplete() { + let schema = schema_serial_pk(); + let a = assess_at_end("insert into Customers (1, ", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + crate::snap!("form_c_in_progress_after_comma", a); +} + +#[test] +fn form_c_in_progress_without_close_paren_is_incomplete() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "insert into Customers (1, 'Alice'", + &schema, + ); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + crate::snap!("form_c_in_progress_no_close", a); +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_all_rows__complete_delete_all_rows_parses@complete_all_rows.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_all_rows__complete_delete_all_rows_parses@complete_all_rows.snap new file mode 100644 index 0000000..6fd3566 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_all_rows__complete_delete_all_rows_parses@complete_all_rows.snap @@ -0,0 +1,19 @@ +--- +source: tests/typing_surface/delete_all_rows.rs +description: "input=\"delete from Customers --all-rows\" cursor=32" +expression: "& a" +--- +Assessment { + input: "delete from Customers --all-rows", + cursor: 32, + state: Valid, + hint: Some( + Prose( + "Submit with Enter", + ), + ), + completion: None, + parse_result: Ok( + "Delete", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_all_rows__delete_partial_flag_is_incomplete@partial_flag.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_all_rows__delete_partial_flag_is_incomplete@partial_flag.snap new file mode 100644 index 0000000..d90444f --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_all_rows__delete_partial_flag_is_incomplete@partial_flag.snap @@ -0,0 +1,41 @@ +--- +source: tests/typing_surface/delete_all_rows.rs +description: "input=\"delete from Customers --all\" cursor=27" +expression: "& a" +--- +Assessment { + input: "delete from Customers --all", + cursor: 27, + state: DefiniteErrorAt( + 22, + ), + hint: Some( + Candidates { + items: [ + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 24, + 27, + ), + partial_prefix: "all", + candidates: [ + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + }, + ), + parse_result: Err( + "Invalid(definite)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_all_rows__delete_without_filter_clause_is_incomplete@no_filter.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_all_rows__delete_without_filter_clause_is_incomplete@no_filter.snap new file mode 100644 index 0000000..bb266dd --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_all_rows__delete_without_filter_clause_is_incomplete@no_filter.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/delete_all_rows.rs +description: "input=\"delete from Customers\" cursor=21" +expression: "& a" +--- +Assessment { + input: "delete from Customers", + cursor: 21, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "Customers", + kind: Identifier, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 12, + 21, + ), + partial_prefix: "Customers", + candidates: [ + Candidate { + text: "Customers", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_delete_expects_from@after_delete.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_delete_expects_from@after_delete.snap new file mode 100644 index 0000000..37b0690 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_delete_expects_from@after_delete.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/delete_with_where.rs +description: "input=\"delete \" cursor=7" +expression: "& a" +--- +Assessment { + input: "delete ", + cursor: 7, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "from", + kind: Keyword, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 7, + 7, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "from", + kind: Keyword, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_from_offers_table_names@after_from.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_from_offers_table_names@after_from.snap new file mode 100644 index 0000000..d7bc096 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_from_offers_table_names@after_from.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/delete_with_where.rs +description: "input=\"delete from \" cursor=12" +expression: "& a" +--- +Assessment { + input: "delete from ", + cursor: 12, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "Customers", + kind: Identifier, + }, + Candidate { + text: "Orders", + kind: Identifier, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 12, + 12, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "Customers", + kind: Identifier, + }, + Candidate { + text: "Orders", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_table_expects_filter_clause@after_table_name.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_table_expects_filter_clause@after_table_name.snap new file mode 100644 index 0000000..6affbf3 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_table_expects_filter_clause@after_table_name.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/delete_with_where.rs +description: "input=\"delete from Customers \" cursor=22" +expression: "& a" +--- +Assessment { + input: "delete from Customers ", + cursor: 22, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "where", + kind: Keyword, + }, + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 22, + 22, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "where", + kind: Keyword, + }, + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_where_column_equals_offers_typed_prose@after_where_equals.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_where_column_equals_offers_typed_prose@after_where_equals.snap new file mode 100644 index 0000000..5391ec0 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_where_column_equals_offers_typed_prose@after_where_equals.snap @@ -0,0 +1,33 @@ +--- +source: tests/typing_surface/delete_with_where.rs +description: "input=\"delete from Customers where Email=\" cursor=34" +expression: "& a" +--- +Assessment { + input: "delete from Customers where Email=", + cursor: 34, + state: IncompleteAtEof, + hint: Some( + Prose( + "for `Email`: Type a quoted string (e.g. 'Alice') or null", + ), + ), + completion: Some( + Completion { + replaced_range: ( + 34, + 34, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "null", + kind: Keyword, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_where_offers_active_table_columns_no_leakage@after_where.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_where_offers_active_table_columns_no_leakage@after_where.snap new file mode 100644 index 0000000..2b3d80f --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__after_where_offers_active_table_columns_no_leakage@after_where.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/delete_with_where.rs +description: "input=\"delete from Customers where \" cursor=28" +expression: "& a" +--- +Assessment { + input: "delete from Customers where ", + cursor: 28, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 28, + 28, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__complete_delete_with_where_parses@complete_delete.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__complete_delete_with_where_parses@complete_delete.snap new file mode 100644 index 0000000..cb3a6a4 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__complete_delete_with_where_parses@complete_delete.snap @@ -0,0 +1,19 @@ +--- +source: tests/typing_surface/delete_with_where.rs +description: "input=\"delete from Customers where id=1\" cursor=32" +expression: "& a" +--- +Assessment { + input: "delete from Customers where id=1", + cursor: 32, + state: Valid, + hint: Some( + Prose( + "Submit with Enter", + ), + ), + completion: None, + parse_result: Ok( + "Delete", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__delete_with_datetime_column_says_yyyy_mm_dd_t@datetime_column.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__delete_with_datetime_column_says_yyyy_mm_dd_t@datetime_column.snap new file mode 100644 index 0000000..af8524f --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__delete_with_where__delete_with_datetime_column_says_yyyy_mm_dd_t@datetime_column.snap @@ -0,0 +1,33 @@ +--- +source: tests/typing_surface/delete_with_where.rs +description: "input=\"delete from Things where ts=\" cursor=28" +expression: "& a" +--- +Assessment { + input: "delete from Things where ts=", + cursor: 28, + state: IncompleteAtEof, + hint: Some( + Prose( + "for `ts`: Type a quoted datetime as 'YYYY-MM-DD HH:MM:SS' or null", + ), + ), + completion: Some( + Completion { + replaced_range: ( + 28, + 28, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "null", + kind: Keyword, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_in_progress_after_comma_is_incomplete@form_c_in_progress_after_comma.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_in_progress_after_comma_is_incomplete@form_c_in_progress_after_comma.snap new file mode 100644 index 0000000..1c6bde2 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_in_progress_after_comma_is_incomplete@form_c_in_progress_after_comma.snap @@ -0,0 +1,53 @@ +--- +source: tests/typing_surface/insert_form_c.rs +description: "input=\"insert into Customers (1, \" cursor=26" +expression: "& a" +--- +Assessment { + input: "insert into Customers (1, ", + cursor: 26, + state: IncompleteAtEof, + hint: Some( + Prose( + "Type a value: number, 'text', true/false, null (dates as 'YYYY-MM-DD', datetimes as 'YYYY-MM-DDTHH:MM:SS')", + ), + ), + completion: Some( + Completion { + replaced_range: ( + 26, + 26, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "null", + kind: Keyword, + }, + Candidate { + text: "true", + kind: Keyword, + }, + Candidate { + text: "false", + kind: Keyword, + }, + Candidate { + text: "Email", + kind: Identifier, + }, + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_in_progress_without_close_paren_is_incomplete@form_c_in_progress_no_close.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_in_progress_without_close_paren_is_incomplete@form_c_in_progress_no_close.snap new file mode 100644 index 0000000..255428b --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_in_progress_without_close_paren_is_incomplete@form_c_in_progress_no_close.snap @@ -0,0 +1,19 @@ +--- +source: tests/typing_surface/insert_form_c.rs +description: "input=\"insert into Customers (1, 'Alice'\" cursor=33" +expression: "& a" +--- +Assessment { + input: "insert into Customers (1, 'Alice'", + cursor: 33, + state: IncompleteAtEof, + hint: Some( + Prose( + "Next: `)`", + ), + ), + completion: None, + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_type_unaware_grammar_accepts_decimal_for_int_column@form_c_type_unaware.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_type_unaware_grammar_accepts_decimal_for_int_column@form_c_type_unaware.snap new file mode 100644 index 0000000..4f6daa8 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_type_unaware_grammar_accepts_decimal_for_int_column@form_c_type_unaware.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/insert_form_c.rs +description: "input=\"insert into Customers (3.14, 'Alice', 'a@b.c')\" cursor=46" +expression: "& a" +--- +Assessment { + input: "insert into Customers (3.14, 'Alice', 'a@b.c')", + cursor: 46, + state: Valid, + hint: Some( + Candidates { + items: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 46, + 46, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + }, + ), + parse_result: Ok( + "Insert", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_column_shaped_item_flags_as_form_a_in_progress@form_c_column_shaped_recovery.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_column_shaped_item_flags_as_form_a_in_progress@form_c_column_shaped_recovery.snap new file mode 100644 index 0000000..2589a96 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_column_shaped_item_flags_as_form_a_in_progress@form_c_column_shaped_recovery.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/insert_form_c.rs +description: "input=\"insert into Customers (Name)\" cursor=28" +expression: "& a" +--- +Assessment { + input: "insert into Customers (Name)", + cursor: 28, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 28, + 28, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_mixed_literals_parses@form_c_mixed_literals.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_mixed_literals_parses@form_c_mixed_literals.snap new file mode 100644 index 0000000..98c5eea --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_mixed_literals_parses@form_c_mixed_literals.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/insert_form_c.rs +description: "input=\"insert into Customers (1, 'Alice', 'a@b.c')\" cursor=43" +expression: "& a" +--- +Assessment { + input: "insert into Customers (1, 'Alice', 'a@b.c')", + cursor: 43, + state: Valid, + hint: Some( + Candidates { + items: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 43, + 43, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + }, + ), + parse_result: Ok( + "Insert", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_null_first_parses@form_c_null_first.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_null_first_parses@form_c_null_first.snap new file mode 100644 index 0000000..4feb267 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_null_first_parses@form_c_null_first.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/insert_form_c.rs +description: "input=\"insert into Customers (null, 'Alice', 'a@b.c')\" cursor=46" +expression: "& a" +--- +Assessment { + input: "insert into Customers (null, 'Alice', 'a@b.c')", + cursor: 46, + state: Valid, + hint: Some( + Candidates { + items: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 46, + 46, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + }, + ), + parse_result: Ok( + "Insert", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_text_literals_parses@form_c_text_literals.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_text_literals_parses@form_c_text_literals.snap new file mode 100644 index 0000000..ed319c4 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_text_literals_parses@form_c_text_literals.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/insert_form_c.rs +description: "input=\"insert into Items ('SKU-1', 'Widget')\" cursor=37" +expression: "& a" +--- +Assessment { + input: "insert into Items ('SKU-1', 'Widget')", + cursor: 37, + state: Valid, + hint: Some( + Candidates { + items: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 37, + 37, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + }, + ), + parse_result: Ok( + "Insert", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_two_columns_flags_as_form_a_in_progress@form_c_two_columns_recovery.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_two_columns_flags_as_form_a_in_progress@form_c_two_columns_recovery.snap new file mode 100644 index 0000000..44f68c2 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__insert_form_c__form_c_with_two_columns_flags_as_form_a_in_progress@form_c_two_columns_recovery.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/insert_form_c.rs +description: "input=\"insert into Customers (Name, Email)\" cursor=35" +expression: "& a" +--- +Assessment { + input: "insert into Customers (Name, Email)", + cursor: 35, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 35, + 35, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "values", + kind: Keyword, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__before_all_rows_flag_is_incomplete@before_all_rows.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__before_all_rows_flag_is_incomplete@before_all_rows.snap new file mode 100644 index 0000000..3539387 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__before_all_rows_flag_is_incomplete@before_all_rows.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/update_all_rows.rs +description: "input=\"update Customers set Email='x' \" cursor=31" +expression: "& a" +--- +Assessment { + input: "update Customers set Email='x' ", + cursor: 31, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "where", + kind: Keyword, + }, + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 31, + 31, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "where", + kind: Keyword, + }, + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__complete_update_all_rows_parses@complete_all_rows.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__complete_update_all_rows_parses@complete_all_rows.snap new file mode 100644 index 0000000..f7f2bdc --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__complete_update_all_rows_parses@complete_all_rows.snap @@ -0,0 +1,19 @@ +--- +source: tests/typing_surface/update_all_rows.rs +description: "input=\"update Customers set Email='new@b.c' --all-rows\" cursor=47" +expression: "& a" +--- +Assessment { + input: "update Customers set Email='new@b.c' --all-rows", + cursor: 47, + state: Valid, + hint: Some( + Prose( + "Submit with Enter", + ), + ), + completion: None, + parse_result: Ok( + "Update", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__update_partial_flag_name_is_incomplete@partial_flag.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__update_partial_flag_name_is_incomplete@partial_flag.snap new file mode 100644 index 0000000..b8642f2 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__update_partial_flag_name_is_incomplete@partial_flag.snap @@ -0,0 +1,41 @@ +--- +source: tests/typing_surface/update_all_rows.rs +description: "input=\"update Customers set Email='x' --all\" cursor=36" +expression: "& a" +--- +Assessment { + input: "update Customers set Email='x' --all", + cursor: 36, + state: DefiniteErrorAt( + 31, + ), + hint: Some( + Candidates { + items: [ + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 33, + 36, + ), + partial_prefix: "all", + candidates: [ + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + }, + ), + parse_result: Err( + "Invalid(definite)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__update_without_filter_clause_is_incomplete@no_filter_clause.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__update_without_filter_clause_is_incomplete@no_filter_clause.snap new file mode 100644 index 0000000..ee13177 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_all_rows__update_without_filter_clause_is_incomplete@no_filter_clause.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/update_all_rows.rs +description: "input=\"update Customers set Email='new@b.c'\" cursor=36" +expression: "& a" +--- +Assessment { + input: "update Customers set Email='new@b.c'", + cursor: 36, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "where", + kind: Keyword, + }, + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 36, + 36, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "where", + kind: Keyword, + }, + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_assignments_expects_where_or_all_rows@after_assignments.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_assignments_expects_where_or_all_rows@after_assignments.snap new file mode 100644 index 0000000..a0af927 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_assignments_expects_where_or_all_rows@after_assignments.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers set Email='new@b.c' \" cursor=37" +expression: "& a" +--- +Assessment { + input: "update Customers set Email='new@b.c' ", + cursor: 37, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "where", + kind: Keyword, + }, + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 37, + 37, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "where", + kind: Keyword, + }, + Candidate { + text: "--all-rows", + kind: Flag, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_equals_for_date_column_says_yyyy_mm_dd@after_equals_date_col.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_equals_for_date_column_says_yyyy_mm_dd@after_equals_date_col.snap new file mode 100644 index 0000000..6e6ac4c --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_equals_for_date_column_says_yyyy_mm_dd@after_equals_date_col.snap @@ -0,0 +1,33 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Things set dt=\" cursor=21" +expression: "& a" +--- +Assessment { + input: "update Things set dt=", + cursor: 21, + state: IncompleteAtEof, + hint: Some( + Prose( + "for `dt`: Type a quoted date as 'YYYY-MM-DD' or null", + ), + ), + completion: Some( + Completion { + replaced_range: ( + 21, + 21, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "null", + kind: Keyword, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_equals_offers_typed_slot_prose_for_column@after_equals_text_col.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_equals_offers_typed_slot_prose_for_column@after_equals_text_col.snap new file mode 100644 index 0000000..6a6e234 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_equals_offers_typed_slot_prose_for_column@after_equals_text_col.snap @@ -0,0 +1,33 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers set Email=\" cursor=27" +expression: "& a" +--- +Assessment { + input: "update Customers set Email=", + cursor: 27, + state: IncompleteAtEof, + hint: Some( + Prose( + "for `Email`: Type a quoted string (e.g. 'Alice') or null", + ), + ), + completion: Some( + Completion { + replaced_range: ( + 27, + 27, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "null", + kind: Keyword, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_set_column_expects_equals@after_set_column.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_set_column_expects_equals@after_set_column.snap new file mode 100644 index 0000000..271c481 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_set_column_expects_equals@after_set_column.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers set Email\" cursor=26" +expression: "& a" +--- +Assessment { + input: "update Customers set Email", + cursor: 26, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "Email", + kind: Identifier, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 21, + 26, + ), + partial_prefix: "Email", + candidates: [ + Candidate { + text: "Email", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_set_offers_only_active_table_columns_no_leakage@after_set_multi_table.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_set_offers_only_active_table_columns_no_leakage@after_set_multi_table.snap new file mode 100644 index 0000000..b2d0c84 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_set_offers_only_active_table_columns_no_leakage@after_set_multi_table.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers set \" cursor=21" +expression: "& a" +--- +Assessment { + input: "update Customers set ", + cursor: 21, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 21, + 21, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_set_serial_pk_offers_all_columns@after_set_serial_pk.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_set_serial_pk_offers_all_columns@after_set_serial_pk.snap new file mode 100644 index 0000000..cd91bce --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_set_serial_pk_offers_all_columns@after_set_serial_pk.snap @@ -0,0 +1,55 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers set \" cursor=21" +expression: "& a" +--- +Assessment { + input: "update Customers set ", + cursor: 21, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "Email", + kind: Identifier, + }, + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 21, + 21, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "Email", + kind: Identifier, + }, + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_table_name_expects_set_keyword@after_table_name.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_table_name_expects_set_keyword@after_table_name.snap new file mode 100644 index 0000000..20c309c --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_table_name_expects_set_keyword@after_table_name.snap @@ -0,0 +1,39 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers \" cursor=17" +expression: "& a" +--- +Assessment { + input: "update Customers ", + cursor: 17, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "set", + kind: Keyword, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 17, + 17, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "set", + kind: Keyword, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_update_keyword_expects_table_name@after_update.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_update_keyword_expects_table_name@after_update.snap new file mode 100644 index 0000000..bc7bab0 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_update_keyword_expects_table_name@after_update.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update \" cursor=7" +expression: "& a" +--- +Assessment { + input: "update ", + cursor: 7, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "Customers", + kind: Identifier, + }, + Candidate { + text: "Orders", + kind: Identifier, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 7, + 7, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "Customers", + kind: Identifier, + }, + Candidate { + text: "Orders", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_where_column_equals_offers_typed_prose@after_where_equals.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_where_column_equals_offers_typed_prose@after_where_equals.snap new file mode 100644 index 0000000..c7f500c --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_where_column_equals_offers_typed_prose@after_where_equals.snap @@ -0,0 +1,33 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers set Email='x' where id=\" cursor=40" +expression: "& a" +--- +Assessment { + input: "update Customers set Email='x' where id=", + cursor: 40, + state: IncompleteAtEof, + hint: Some( + Prose( + "for `id`: Type null to auto-generate, or an explicit integer", + ), + ), + completion: Some( + Completion { + replaced_range: ( + 40, + 40, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "null", + kind: Keyword, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_where_keyword_offers_active_table_columns@after_where.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_where_keyword_offers_active_table_columns@after_where.snap new file mode 100644 index 0000000..74220c1 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__after_where_keyword_offers_active_table_columns@after_where.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers set Name='x' where \" cursor=36" +expression: "& a" +--- +Assessment { + input: "update Customers set Name='x' where ", + cursor: 36, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 36, + 36, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__complete_update_with_where_parses@complete_update.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__complete_update_with_where_parses@complete_update.snap new file mode 100644 index 0000000..e2d539f --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__complete_update_with_where_parses@complete_update.snap @@ -0,0 +1,19 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers set Email='new@b.c' where id=1\" cursor=47" +expression: "& a" +--- +Assessment { + input: "update Customers set Email='new@b.c' where id=1", + cursor: 47, + state: Valid, + hint: Some( + Prose( + "Submit with Enter", + ), + ), + completion: None, + parse_result: Ok( + "Update", + ), +} diff --git a/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__mid_assignment_list_after_comma_offers_remaining_columns@mid_assignment_list.snap b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__mid_assignment_list_after_comma_offers_remaining_columns@mid_assignment_list.snap new file mode 100644 index 0000000..34c4bd1 --- /dev/null +++ b/tests/typing_surface/snapshots/typing_surface_matrix__typing_surface__update_with_where__mid_assignment_list_after_comma_offers_remaining_columns@mid_assignment_list.snap @@ -0,0 +1,47 @@ +--- +source: tests/typing_surface/update_with_where.rs +description: "input=\"update Customers set Name='x', \" cursor=31" +expression: "& a" +--- +Assessment { + input: "update Customers set Name='x', ", + cursor: 31, + state: IncompleteAtEof, + hint: Some( + Candidates { + items: [ + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + selected: None, + }, + ), + completion: Some( + Completion { + replaced_range: ( + 31, + 31, + ), + partial_prefix: "", + candidates: [ + Candidate { + text: "Name", + kind: Identifier, + }, + Candidate { + text: "id", + kind: Identifier, + }, + ], + }, + ), + parse_result: Err( + "Invalid(at_eof)", + ), +} diff --git a/tests/typing_surface/update_all_rows.rs b/tests/typing_surface/update_all_rows.rs index b2d5ff4..ffab249 100644 --- a/tests/typing_surface/update_all_rows.rs +++ b/tests/typing_surface/update_all_rows.rs @@ -1 +1,54 @@ -//! Submodule stub — populated in subsequent tasks. +//! Matrix coverage for `update T set col=val --all-rows` +//! (ADR-0014 opt-in for unfiltered updates). + +use crate::typing_surface::*; +use rdbms_playground::input_render::InputState; + +#[test] +fn before_all_rows_flag_is_incomplete() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "update Customers set Email='x' ", + &schema, + ); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + // Both `where` and `--all-rows` are valid continuations. + assert_candidate_present(&a, &["--all-rows"]); + crate::snap!("before_all_rows", a); +} + +#[test] +fn complete_update_all_rows_parses() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "update Customers set Email='new@b.c' --all-rows", + &schema, + ); + assert!(matches!(a.state, InputState::Valid)); + assert_eq!(a.parse_result.as_deref(), Ok("Update")); + crate::snap!("complete_all_rows", a); +} + +#[test] +fn update_without_filter_clause_is_incomplete() { + // Per ADR-0014, update requires WHERE or --all-rows. + let schema = schema_serial_pk(); + let a = assess_at_end( + "update Customers set Email='new@b.c'", + &schema, + ); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + crate::snap!("no_filter_clause", a); +} + +#[test] +fn update_partial_flag_name_is_incomplete() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "update Customers set Email='x' --all", + &schema, + ); + // Partial flag still in progress. + assert!(!matches!(a.state, InputState::Valid)); + crate::snap!("partial_flag", a); +} diff --git a/tests/typing_surface/update_with_where.rs b/tests/typing_surface/update_with_where.rs index b2d5ff4..efeb8b9 100644 --- a/tests/typing_surface/update_with_where.rs +++ b/tests/typing_surface/update_with_where.rs @@ -1 +1,169 @@ -//! Submodule stub — populated in subsequent tasks. +//! Matrix coverage for `update T set col=val where col=val`. +//! +//! Per ADR-0014, update requires either WHERE or `--all-rows`. +//! Handoff §1 bug E1 (`update T set ` showing every table's +//! columns) was fixed in commit 619a8bd via +//! `completion_probe`; these tests pin the per-table narrowing +//! invariant. + +use crate::typing_surface::*; +use rdbms_playground::input_render::InputState; + +#[test] +fn after_update_keyword_expects_table_name() { + let schema = schema_multi_table(); + let a = assess_at_end("update ", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + assert_candidate_present(&a, &["Customers", "Orders"]); + crate::snap!("after_update", a); +} + +#[test] +fn after_table_name_expects_set_keyword() { + let schema = schema_serial_pk(); + let a = assess_at_end("update Customers ", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + assert_candidate_present(&a, &["set"]); + crate::snap!("after_table_name", a); +} + +// ========================================================= +// Handoff §1 bug E1 — `update T set ` must narrow column +// candidates to the active table. +// ========================================================= + +#[test] +fn after_set_offers_only_active_table_columns_no_leakage() { + let schema = schema_multi_table(); + let a = assess_at_end("update Customers set ", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + // Customers's columns must be offered. + assert_candidate_present(&a, &["id", "Name"]); + // Orders's columns must NOT leak. + assert_no_candidate_named(&a, &["OrderId", "CustId", "Total"]); + crate::snap!("after_set_multi_table", a); +} + +#[test] +fn after_set_serial_pk_offers_all_columns() { + let schema = schema_serial_pk(); + let a = assess_at_end("update Customers set ", &schema); + assert_candidate_present(&a, &["id", "Name", "Email"]); + crate::snap!("after_set_serial_pk", a); +} + +#[test] +fn after_set_column_expects_equals() { + let schema = schema_serial_pk(); + let a = assess_at_end("update Customers set Email", &schema); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + crate::snap!("after_set_column", a); +} + +#[test] +fn after_equals_offers_typed_slot_prose_for_column() { + let schema = schema_serial_pk(); + let a = assess_at_end("update Customers set Email=", &schema); + let prose = hint_prose(&a).unwrap_or_else(|| { + panic!("expected Prose, got {:?}", a.hint) + }); + assert!( + prose.contains("Email"), + "should name `Email`, got prose: {prose:?}", + ); + assert!( + prose.contains("quoted string"), + "should say `quoted string` (text type), got prose: {prose:?}", + ); + crate::snap!("after_equals_text_col", a); +} + +#[test] +fn after_equals_for_date_column_says_yyyy_mm_dd() { + let schema = schema_every_type(); + let a = assess_at_end("update Things set dt=", &schema); + let prose = hint_prose(&a).unwrap_or_else(|| { + panic!("expected Prose, got {:?}", a.hint) + }); + assert!( + prose.contains("YYYY-MM-DD"), + "date-slot prose should reference YYYY-MM-DD, got {prose:?}", + ); + crate::snap!("after_equals_date_col", a); +} + +#[test] +fn mid_assignment_list_after_comma_offers_remaining_columns() { + let schema = schema_multi_table(); + let a = assess_at_end( + "update Customers set Name='x', ", + &schema, + ); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + // Customers columns offered, no leakage. + assert_candidate_present(&a, &["id"]); + assert_no_candidate_named(&a, &["OrderId", "CustId", "Total"]); + crate::snap!("mid_assignment_list", a); +} + +#[test] +fn after_assignments_expects_where_or_all_rows() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "update Customers set Email='new@b.c' ", + &schema, + ); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + assert_candidate_present(&a, &["where", "--all-rows"]); + crate::snap!("after_assignments", a); +} + +// ========================================================= +// WHERE clause column position — also schema-narrowed. +// ========================================================= + +#[test] +fn after_where_keyword_offers_active_table_columns() { + let schema = schema_multi_table(); + let a = assess_at_end( + "update Customers set Name='x' where ", + &schema, + ); + assert!(matches!(a.state, InputState::IncompleteAtEof)); + assert_candidate_present(&a, &["id", "Name"]); + assert_no_candidate_named(&a, &["OrderId", "CustId", "Total"]); + crate::snap!("after_where", a); +} + +#[test] +fn after_where_column_equals_offers_typed_prose() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "update Customers set Email='x' where id=", + &schema, + ); + let prose = hint_prose(&a).unwrap_or_else(|| { + panic!("expected Prose, got {:?}", a.hint) + }); + assert!( + prose.contains("id"), + "should name where column `id`, got prose: {prose:?}", + ); + crate::snap!("after_where_equals", a); +} + +// ========================================================= +// Complete update parses to Update. +// ========================================================= + +#[test] +fn complete_update_with_where_parses() { + let schema = schema_serial_pk(); + let a = assess_at_end( + "update Customers set Email='new@b.c' where id=1", + &schema, + ); + assert!(matches!(a.state, InputState::Valid)); + assert_eq!(a.parse_result.as_deref(), Ok("Update")); + crate::snap!("complete_update", a); +}