an ORM-free SQL experience
at main 339 lines 10 kB view raw
1package norm 2 3import ( 4 "database/sql" 5 "testing" 6 7 _ "github.com/mattn/go-sqlite3" 8) 9 10func TestCreateTableCompileSuccess(t *testing.T) { 11 tests := []struct { 12 name string 13 stmt Compiler 14 expectedSql string 15 }{ 16 { 17 name: "Simple table", 18 stmt: CreateTable("users"). 19 Column("id", Integer), 20 expectedSql: "CREATE TABLE users (id INTEGER)", 21 }, 22 { 23 name: "Table with primary key", 24 stmt: CreateTable("users"). 25 Column("id", Integer, PrimaryKey), 26 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY)", 27 }, 28 { 29 name: "Table with autoincrement", 30 stmt: CreateTable("users"). 31 Column("id", Integer, PrimaryKey, AutoIncrement), 32 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT)", 33 }, 34 { 35 name: "Table with multiple columns", 36 stmt: CreateTable("users"). 37 Column("id", Integer, PrimaryKey). 38 Column("name", Text, NotNull). 39 Column("age", Integer), 40 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER)", 41 }, 42 { 43 name: "Table with IF NOT EXISTS", 44 stmt: CreateTable("users"). 45 IfNotExists(). 46 Column("id", Integer, PrimaryKey), 47 expectedSql: "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY)", 48 }, 49 { 50 name: "Table with unique constraint", 51 stmt: CreateTable("users"). 52 Column("id", Integer, PrimaryKey). 53 Column("email", Text, Unique), 54 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT UNIQUE)", 55 }, 56 { 57 name: "Table with default value", 58 stmt: CreateTable("users"). 59 Column("id", Integer, PrimaryKey). 60 Column("active", Integer, Default(1)), 61 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY, active INTEGER DEFAULT 1)", 62 }, 63 { 64 name: "Table with check constraint", 65 stmt: CreateTable("users"). 66 Column("id", Integer, PrimaryKey). 67 Column("age", Integer, Check("age >= 18")), 68 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY, age INTEGER CHECK (age >= 18))", 69 }, 70 { 71 name: "Table with collate", 72 stmt: CreateTable("users"). 73 Column("id", Integer, PrimaryKey). 74 Column("name", Text, Collate("NOCASE")), 75 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE)", 76 }, 77 { 78 name: "Table with composite primary key", 79 stmt: CreateTable("user_roles"). 80 Column("user_id", Integer). 81 Column("role_id", Integer). 82 PrimaryKey("user_id", "role_id"), 83 expectedSql: "CREATE TABLE user_roles (user_id INTEGER, role_id INTEGER, PRIMARY KEY (user_id, role_id))", 84 }, 85 { 86 name: "Table with table-level unique constraint", 87 stmt: CreateTable("users"). 88 Column("id", Integer, PrimaryKey). 89 Column("email", Text). 90 Column("username", Text). 91 UniqueConstraint("email", "username"), 92 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT, username TEXT, UNIQUE (email, username))", 93 }, 94 { 95 name: "Table with table-level check constraint", 96 stmt: CreateTable("users"). 97 Column("id", Integer, PrimaryKey). 98 Column("age", Integer). 99 CheckConstraint("age >= 0 AND age <= 150"), 100 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY, age INTEGER, CHECK (age >= 0 AND age <= 150))", 101 }, 102 { 103 name: "Table with foreign key", 104 stmt: CreateTable("posts"). 105 Column("id", Integer, PrimaryKey). 106 Column("user_id", Integer, NotNull). 107 ForeignKey("user_id", "users", "id"), 108 expectedSql: "CREATE TABLE posts (id INTEGER PRIMARY KEY, user_id INTEGER NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id))", 109 }, 110 { 111 name: "Table WITHOUT ROWID", 112 stmt: CreateTable("cache"). 113 Column("key", Text, PrimaryKey). 114 Column("value", Blob). 115 WithoutRowid(), 116 expectedSql: "CREATE TABLE cache (key TEXT PRIMARY KEY, value BLOB) WITHOUT ROWID", 117 }, 118 { 119 name: "Table STRICT", 120 stmt: CreateTable("users"). 121 Column("id", Integer, PrimaryKey). 122 Column("name", Text). 123 Strict(), 124 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT) STRICT", 125 }, 126 { 127 name: "Table STRICT WITHOUT ROWID", 128 stmt: CreateTable("cache"). 129 Column("key", Text, PrimaryKey). 130 Column("value", Blob). 131 Strict(). 132 WithoutRowid(), 133 expectedSql: "CREATE TABLE cache (key TEXT PRIMARY KEY, value BLOB) STRICT WITHOUT ROWID", 134 }, 135 { 136 name: "All data types", 137 stmt: CreateTable("types_test"). 138 Column("col_int", Integer). 139 Column("col_text", Text). 140 Column("col_real", Real). 141 Column("col_blob", Blob). 142 Column("col_numeric", Numeric), 143 expectedSql: "CREATE TABLE types_test (col_int INTEGER, col_text TEXT, col_real REAL, col_blob BLOB, col_numeric NUMERIC)", 144 }, 145 { 146 name: "Multiple constraints on column", 147 stmt: CreateTable("users"). 148 Column("id", Integer, PrimaryKey, AutoIncrement). 149 Column("email", Text, NotNull, Unique), 150 expectedSql: "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE)", 151 }, 152 { 153 name: "Complex table", 154 stmt: CreateTable("users"). 155 IfNotExists(). 156 Column("id", Integer, PrimaryKey, AutoIncrement). 157 Column("email", Text, NotNull). 158 Column("username", Text, NotNull). 159 Column("age", Integer, Check("age >= 18")). 160 Column("created_at", Text, Default("CURRENT_TIMESTAMP")). 161 UniqueConstraint("email", "username"). 162 CheckConstraint("age < 150"), 163 expectedSql: "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL, username TEXT NOT NULL, age INTEGER CHECK (age >= 18), created_at TEXT DEFAULT CURRENT_TIMESTAMP, UNIQUE (email, username), CHECK (age < 150))", 164 }, 165 } 166 167 for _, test := range tests { 168 t.Run(test.name, func(t *testing.T) { 169 sql, args := test.stmt.MustCompile() 170 171 if sql != test.expectedSql { 172 t.Errorf("Expected SQL:\n%s\nGot:\n%s", test.expectedSql, sql) 173 } 174 175 if len(args) != 0 { 176 t.Errorf("Expected 0 args, got %d args", len(args)) 177 } 178 }) 179 } 180} 181 182func TestCreateTableCompileFail(t *testing.T) { 183 tests := []struct { 184 name string 185 stmt Compiler 186 expectedError string 187 }{ 188 { 189 name: "No table name", 190 stmt: CreateTable(""), 191 expectedError: "table name is required", 192 }, 193 { 194 name: "No columns", 195 stmt: CreateTable("users"), 196 expectedError: "at least one column is required", 197 }, 198 } 199 200 for _, test := range tests { 201 t.Run(test.name, func(t *testing.T) { 202 _, _, err := test.stmt.Compile() 203 if err == nil { 204 t.Error("Expected error, got nil") 205 } 206 if err.Error() != test.expectedError { 207 t.Errorf("Expected error '%s', got '%s'", test.expectedError, err.Error()) 208 } 209 }) 210 } 211} 212 213func TestCreateTableIntegration(t *testing.T) { 214 tests := []struct { 215 name string 216 createStmt Execer 217 insertStmt Execer 218 selectStmt Querier 219 verify func(t *testing.T, db *sql.DB) 220 }{ 221 { 222 name: "Create simple table and insert", 223 createStmt: CreateTable("test_users"). 224 Column("id", Integer, PrimaryKey). 225 Column("name", Text), 226 insertStmt: Insert().Into("test_users").Value("id", 1).Value("name", "Alice"), 227 selectStmt: Select("id", "name").From("test_users"), 228 verify: func(t *testing.T, db *sql.DB) { 229 var count int 230 err := db.QueryRow("SELECT COUNT(*) FROM test_users").Scan(&count) 231 if err != nil { 232 t.Fatalf("Failed to count rows: %v", err) 233 } 234 if count != 1 { 235 t.Errorf("Expected 1 row, got %d", count) 236 } 237 }, 238 }, 239 { 240 name: "Create table with IF NOT EXISTS", 241 createStmt: CreateTable("test_users2"). 242 IfNotExists(). 243 Column("id", Integer, PrimaryKey), 244 verify: func(t *testing.T, db *sql.DB) { 245 _, err := CreateTable("test_users2"). 246 IfNotExists(). 247 Column("id", Integer, PrimaryKey). 248 Exec(db) 249 if err != nil { 250 t.Errorf("Second create with IF NOT EXISTS should not fail: %v", err) 251 } 252 }, 253 }, 254 { 255 name: "Create table with autoincrement", 256 createStmt: CreateTable("test_users3"). 257 Column("id", Integer, PrimaryKey, AutoIncrement). 258 Column("name", Text), 259 insertStmt: Insert().Into("test_users3").Value("name", "Bob"), 260 verify: func(t *testing.T, db *sql.DB) { 261 var id int 262 var name string 263 err := db.QueryRow("SELECT id, name FROM test_users3").Scan(&id, &name) 264 if err != nil { 265 t.Fatalf("Failed to query: %v", err) 266 } 267 if id != 1 { 268 t.Errorf("Expected auto-incremented id=1, got %d", id) 269 } 270 if name != "Bob" { 271 t.Errorf("Expected name=Bob, got %s", name) 272 } 273 }, 274 }, 275 { 276 name: "Create table with default value", 277 createStmt: CreateTable("test_users4"). 278 Column("id", Integer, PrimaryKey). 279 Column("active", Integer, Default(1)), 280 insertStmt: Insert().Into("test_users4").Value("id", 1), 281 verify: func(t *testing.T, db *sql.DB) { 282 var active int 283 err := db.QueryRow("SELECT active FROM test_users4 WHERE id = 1").Scan(&active) 284 if err != nil { 285 t.Fatalf("Failed to query: %v", err) 286 } 287 if active != 1 { 288 t.Errorf("Expected default active=1, got %d", active) 289 } 290 }, 291 }, 292 { 293 name: "Create table with unique constraint", 294 createStmt: CreateTable("test_users5"). 295 Column("id", Integer, PrimaryKey). 296 Column("email", Text, Unique), 297 verify: func(t *testing.T, db *sql.DB) { 298 _, err := Insert().Into("test_users5"). 299 Value("id", 1). 300 Value("email", "test@example.com"). 301 Exec(db) 302 if err != nil { 303 t.Fatalf("First insert should succeed: %v", err) 304 } 305 306 _, err = Insert().Into("test_users5"). 307 Value("id", 2). 308 Value("email", "test@example.com"). 309 Exec(db) 310 if err == nil { 311 t.Error("Expected unique constraint violation, got nil") 312 } 313 }, 314 }, 315 } 316 317 for _, test := range tests { 318 t.Run(test.name, func(t *testing.T) { 319 db := setupTestDB(t) 320 defer db.Close() 321 322 _, err := test.createStmt.Exec(db) 323 if err != nil { 324 t.Fatalf("Failed to create table: %v", err) 325 } 326 327 if test.insertStmt != nil { 328 _, err = test.insertStmt.Exec(db) 329 if err != nil { 330 t.Fatalf("Failed to insert: %v", err) 331 } 332 } 333 334 if test.verify != nil { 335 test.verify(t, db) 336 } 337 }) 338 } 339}