use spg_engine::Engine;
#[test]
fn create_type_then_use_in_column() {
let mut e = Engine::new();
e.execute("CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy')")
.unwrap();
e.execute("CREATE TABLE p (id INT NOT NULL, m mood)")
.unwrap();
e.execute("INSERT INTO p VALUES (1, 'happy')").unwrap();
e.execute("INSERT INTO p VALUES (2, 'sad')").unwrap();
let r = e.execute("SELECT id, m FROM p ORDER BY id").unwrap();
let rows = match r {
spg_engine::QueryResult::Rows { rows, .. } => rows,
_ => panic!("expected rows"),
};
assert_eq!(rows.len(), 2);
assert_eq!(rows[1].values[1], spg_storage::Value::Text("sad".into()));
}
#[test]
fn insert_invalid_enum_label_rejected() {
let mut e = Engine::new();
e.execute("CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy')")
.unwrap();
e.execute("CREATE TABLE p (id INT NOT NULL, m mood)")
.unwrap();
let err = e.execute("INSERT INTO p VALUES (1, 'angry')");
assert!(err.is_err(), "non-label value should be rejected");
}
#[test]
fn unknown_type_ident_in_create_table_rejected() {
let mut e = Engine::new();
let err = e.execute("CREATE TABLE p (id INT NOT NULL, m my_unknown_type)");
assert!(err.is_err(), "unknown column type ident should error");
}
#[test]
fn null_value_allowed_when_nullable() {
let mut e = Engine::new();
e.execute("CREATE TYPE mood AS ENUM ('sad', 'happy')")
.unwrap();
e.execute("CREATE TABLE p (id INT NOT NULL, m mood)")
.unwrap();
e.execute("INSERT INTO p VALUES (1, NULL)").unwrap();
e.execute("INSERT INTO p (id) VALUES (2)").unwrap();
}
#[test]
fn duplicate_create_type_errors() {
let mut e = Engine::new();
e.execute("CREATE TYPE mood AS ENUM ('sad')").unwrap();
let err = e.execute("CREATE TYPE mood AS ENUM ('happy')");
assert!(err.is_err(), "duplicate CREATE TYPE should error");
}
#[test]
fn duplicate_label_in_enum_rejected() {
let mut e = Engine::new();
let err = e.execute("CREATE TYPE mood AS ENUM ('a', 'b', 'a')");
assert!(err.is_err(), "duplicate enum label should be rejected");
}
#[test]
fn drop_type_removes_it() {
let mut e = Engine::new();
e.execute("CREATE TYPE mood AS ENUM ('happy')").unwrap();
e.execute("DROP TYPE mood").unwrap();
e.execute("CREATE TYPE mood AS ENUM ('sad')").unwrap();
}
#[test]
fn drop_type_if_exists_silent_on_missing() {
let mut e = Engine::new();
e.execute("DROP TYPE IF EXISTS missing").unwrap();
}
#[test]
fn enum_type_round_trips_catalog() {
let mut e = Engine::new();
e.execute("CREATE TYPE mood AS ENUM ('sad', 'happy')")
.unwrap();
e.execute("CREATE TABLE p (id INT NOT NULL, m mood)")
.unwrap();
e.execute("INSERT INTO p VALUES (1, 'happy')").unwrap();
let snapshot = e.catalog().serialize();
let restored = spg_storage::Catalog::deserialize(&snapshot).expect("round-trip");
let enum_def = restored.enum_types().get("mood").expect("enum persisted");
assert_eq!(
enum_def.labels,
vec!["sad".to_string(), "happy".to_string()]
);
let table = restored.get("p").expect("table");
let col = table
.schema()
.columns
.iter()
.find(|c| c.name == "m")
.expect("column");
assert_eq!(col.user_enum_type.as_deref(), Some("mood"));
}
#[test]
fn enum_collision_with_table_name_rejected() {
let mut e = Engine::new();
e.execute("CREATE TABLE mood (id INT NOT NULL)").unwrap();
let err = e.execute("CREATE TYPE mood AS ENUM ('happy')");
assert!(err.is_err());
}
#[test]
fn update_to_invalid_label_skipped_for_phase14() {
let mut e = Engine::new();
e.execute("CREATE TYPE mood AS ENUM ('happy', 'sad')")
.unwrap();
e.execute("CREATE TABLE p (id INT NOT NULL, m mood)")
.unwrap();
e.execute("INSERT INTO p VALUES (1, 'happy')").unwrap();
let _ = e.execute("UPDATE p SET m = 'angry' WHERE id = 1");
}