use nodedb_sql::SqlError;
use nodedb_sql::ddl_ast::parse as ddl_parse;
fn parse_ok(sql: &str) -> nodedb_sql::ddl_ast::NodedbStatement {
ddl_parse(sql)
.unwrap_or_else(|| panic!("expected Some from parse, got None for: {sql}"))
.unwrap_or_else(|e| panic!("expected Ok from parse, got Err({e}) for: {sql}"))
}
fn parse_constraint_err(sql: &str) -> (String, String) {
match ddl_parse(sql) {
Some(Err(SqlError::UnsupportedConstraint { feature, hint })) => (feature, hint),
Some(Err(other)) => panic!("expected UnsupportedConstraint, got {other:?} for: {sql}"),
Some(Ok(stmt)) => {
panic!("expected Err(UnsupportedConstraint), got Ok({stmt:?}) for: {sql}")
}
None => panic!("expected Some, got None for: {sql}"),
}
}
#[test]
fn table_level_primary_key_rejected() {
let (feature, hint) = parse_constraint_err("CREATE TABLE x (id INT, PRIMARY KEY (id))");
assert!(
feature.contains("PRIMARY KEY"),
"feature should name PRIMARY KEY, got: {feature}"
);
assert!(
hint.contains("inline") || hint.contains("PRIMARY KEY"),
"hint should point at inline form, got: {hint}"
);
}
#[test]
fn inline_primary_key_accepted() {
use nodedb_sql::ddl_ast::NodedbStatement;
let stmt = parse_ok("CREATE TABLE x (id INT PRIMARY KEY)");
if let NodedbStatement::CreateTable { columns, .. } = stmt {
assert_eq!(columns.len(), 1, "expected 1 column");
let (name, type_str) = &columns[0];
assert_eq!(name, "id", "column name should be 'id'");
let upper = type_str.to_uppercase();
assert!(
upper.contains("PRIMARY"),
"type_str should retain PRIMARY KEY marker, got: {type_str}"
);
} else {
panic!("expected CreateTable, got: {stmt:?}");
}
}
#[test]
fn inline_unique_rejected() {
let (feature, hint) = parse_constraint_err("CREATE TABLE x (id INT UNIQUE)");
assert!(
feature.contains("UNIQUE"),
"feature should name UNIQUE, got: {feature}"
);
assert!(
hint.contains("UNIQUE"),
"hint should mention UNIQUE index, got: {hint}"
);
}
#[test]
fn table_level_unique_rejected() {
let (feature, hint) = parse_constraint_err("CREATE TABLE x (id INT, UNIQUE (id))");
assert!(
feature.contains("UNIQUE"),
"feature should name UNIQUE, got: {feature}"
);
assert!(
hint.contains("UNIQUE"),
"hint should mention UNIQUE index, got: {hint}"
);
}
#[test]
fn inline_check_rejected() {
let (feature, hint) = parse_constraint_err("CREATE TABLE x (id INT CHECK (id > 0))");
assert!(
feature.contains("CHECK"),
"feature should name CHECK, got: {feature}"
);
assert!(
hint.contains("application"),
"hint should mention application code, got: {hint}"
);
}
#[test]
fn inline_references_rejected() {
let (feature, hint) = parse_constraint_err("CREATE TABLE x (id INT REFERENCES other(id))");
assert!(
feature.to_uppercase().contains("REFERENCES") || feature.contains("FOREIGN"),
"feature should name REFERENCES or FOREIGN KEY, got: {feature}"
);
assert!(
hint.contains("application"),
"hint should mention application code, got: {hint}"
);
}
#[test]
fn table_level_foreign_key_rejected() {
let (feature, hint) =
parse_constraint_err("CREATE TABLE x (id INT, FOREIGN KEY (id) REFERENCES other(id))");
assert!(
feature.contains("FOREIGN"),
"feature should name FOREIGN KEY, got: {feature}"
);
assert!(
hint.contains("application"),
"hint should mention application code, got: {hint}"
);
}
#[test]
fn named_constraint_primary_key_rejected() {
let (feature, hint) =
parse_constraint_err("CREATE TABLE x (id INT, CONSTRAINT pk PRIMARY KEY (id))");
assert!(
feature.contains("PRIMARY") || feature.contains("CONSTRAINT"),
"feature should name PRIMARY KEY or CONSTRAINT, got: {feature}"
);
assert!(
hint.contains("inline") || hint.contains("PRIMARY KEY") || hint.contains("NodeDB"),
"hint should point at inline form or NodeDB enforcement, got: {hint}"
);
}
#[test]
fn plain_columns_succeed() {
let stmt = parse_ok("CREATE TABLE x (id INT)");
assert!(
matches!(
stmt,
nodedb_sql::ddl_ast::NodedbStatement::CreateTable { .. }
),
"expected CreateTable statement"
);
}
#[test]
fn engine_override_succeeds() {
let stmt = parse_ok("CREATE TABLE x (id INT) WITH (engine='kv')");
assert!(
matches!(
stmt,
nodedb_sql::ddl_ast::NodedbStatement::CreateTable { .. }
),
"expected CreateTable statement"
);
}
#[test]
fn not_null_and_default_accepted() {
let stmt = parse_ok("CREATE TABLE x (id INT NOT NULL DEFAULT 0)");
if let nodedb_sql::ddl_ast::NodedbStatement::CreateTable { columns, .. } = stmt {
assert_eq!(columns.len(), 1, "expected 1 column");
let (name, type_str) = &columns[0];
assert_eq!(name, "id");
let upper = type_str.to_uppercase();
assert!(
!upper.contains("PRIMARY"),
"type_str should not contain PRIMARY, got: {type_str}"
);
assert!(
!upper.contains("UNIQUE"),
"type_str should not contain UNIQUE, got: {type_str}"
);
} else {
panic!("expected CreateTable");
}
}
#[test]
fn create_collection_succeeds() {
let stmt = parse_ok("CREATE COLLECTION x");
assert!(
matches!(
stmt,
nodedb_sql::ddl_ast::NodedbStatement::CreateCollection { .. }
),
"expected CreateCollection statement"
);
}
#[test]
fn create_table_no_columns_parse_succeeds_with_empty_columns() {
let stmt = parse_ok("CREATE TABLE x");
if let nodedb_sql::ddl_ast::NodedbStatement::CreateTable { columns, .. } = stmt {
assert!(
columns.is_empty(),
"expected empty columns for no-column CREATE TABLE"
);
} else {
panic!("expected CreateTable");
}
}