an ORM-free SQL experience
1package norm 2 3import ( 4 "slices" 5 "testing" 6 7 _ "github.com/mattn/go-sqlite3" 8) 9 10func TestUpdateBuild_Success(t *testing.T) { 11 tests := []struct { 12 name string 13 stmt Compiler 14 expectedSql string 15 expectedArgs []any 16 }{ 17 { 18 name: "Simple update", 19 stmt: Update("users").Set("name", "John"), 20 expectedSql: "UPDATE users SET name = ?", 21 expectedArgs: []any{"John"}, 22 }, 23 { 24 name: "Update with WHERE", 25 stmt: Update("users").Set("name", "John").Where(Eq("id", 1)), 26 expectedSql: "UPDATE users SET name = ? WHERE (id) = (?)", 27 expectedArgs: []any{"John", 1}, 28 }, 29 { 30 name: "Abort clause", 31 stmt: Update("users").Or(UpdateAbort).Set("name", "John"), 32 expectedSql: "UPDATE OR ABORT users SET name = ?", 33 expectedArgs: []any{"John"}, 34 }, 35 { 36 name: "Ignore clause", 37 stmt: Update("users").Or(UpdateIgnore).Set("name", "John"), 38 expectedSql: "UPDATE OR IGNORE users SET name = ?", 39 expectedArgs: []any{"John"}, 40 }, 41 { 42 name: "Fail clause", 43 stmt: Update("users").Or(UpdateFail).Set("name", "John"), 44 expectedSql: "UPDATE OR FAIL users SET name = ?", 45 expectedArgs: []any{"John"}, 46 }, 47 { 48 name: "Replace clause", 49 stmt: Update("users").Or(UpdateReplace).Set("name", "John"), 50 expectedSql: "UPDATE OR REPLACE users SET name = ?", 51 expectedArgs: []any{"John"}, 52 }, 53 { 54 name: "Rollback clause", 55 stmt: Update("users").Or(UpdateRollback).Set("name", "John"), 56 expectedSql: "UPDATE OR ROLLBACK users SET name = ?", 57 expectedArgs: []any{"John"}, 58 }, 59 { 60 name: "Default clause", 61 stmt: Update("users").Or(UpdateOr(10)).Set("name", "John"), 62 expectedSql: "UPDATE users SET name = ?", 63 expectedArgs: []any{"John"}, 64 }, 65 { 66 name: "Multiple sets", 67 stmt: Update("users").Set("name", "John").Set("age", 35), 68 expectedSql: "UPDATE users SET name = ?, age = ?", 69 expectedArgs: []any{"John", 35}, 70 }, 71 { 72 name: "Multiple WHERE conditions", 73 stmt: Update("users").Set("salary", 90000.0).Where(Eq("department", "Engineering").And(Eq("active", true))), 74 expectedSql: "UPDATE users SET salary = ? WHERE ((department) = (?)) AND ((active) = (?))", 75 expectedArgs: []any{90000.0, "Engineering", true}, 76 }, 77 { 78 name: "Complex update", 79 stmt: Update("users").Or(UpdateIgnore).Set("name", "Updated").Set("salary", 100000.0).Where(Gt("age", 30).And(Eq("active", true))), 80 expectedSql: "UPDATE OR IGNORE users SET name = ?, salary = ? WHERE ((age) > (?)) AND ((active) = (?))", 81 expectedArgs: []any{"Updated", 100000.0, 30, true}, 82 }, 83 } 84 85 for _, test := range tests { 86 t.Run(test.name, func(t *testing.T) { 87 sql, args := test.stmt.MustCompile() 88 89 if sql != test.expectedSql { 90 t.Errorf("Expected '%s', got '%s'", test.expectedSql, sql) 91 } 92 93 if len(args) != len(test.expectedArgs) { 94 t.Errorf("Expected '%d' args, got '%d' args", len(test.expectedArgs), len(args)) 95 } 96 97 for i := range len(args) { 98 if args[i] != test.expectedArgs[i] { 99 t.Errorf("Expected '%v', got '%v' at index %d", test.expectedArgs[i], args[i], i) 100 } 101 } 102 }) 103 } 104} 105 106func TestUpdateSetMap_Build(t *testing.T) { 107 tests := []struct { 108 name string 109 stmt Compiler 110 expectedConfig []struct { 111 sql string 112 args []any 113 } 114 }{ 115 { 116 name: "Sets with map", 117 stmt: Update("users").Sets(map[string]any{ 118 "name": "John", 119 "age": 25, 120 }).Where(Eq("id", 1)), 121 expectedConfig: []struct { 122 sql string 123 args []any 124 }{ 125 { 126 sql: "UPDATE users SET name = ?, age = ? WHERE (id) = (?)", 127 args: []any{"John", 25, 1}, 128 }, 129 { 130 sql: "UPDATE users SET age = ?, name = ? WHERE (id) = (?)", 131 args: []any{25, "John", 1}, 132 }, 133 }, 134 }, 135 { 136 name: "Mixed individual and map sets", 137 stmt: Update("users").Set("active", false).Sets(map[string]any{ 138 "name": "Updated User", 139 "salary": 95000.0, 140 }), 141 expectedConfig: []struct { 142 sql string 143 args []any 144 }{ 145 { 146 sql: "UPDATE users SET active = ?, name = ?, salary = ?", 147 args: []any{false, "Updated User", 95000.0}, 148 }, 149 { 150 sql: "UPDATE users SET active = ?, salary = ?, name = ?", 151 args: []any{false, 95000.0, "Updated User"}, 152 }, 153 }, 154 }, 155 } 156 157 for _, test := range tests { 158 t.Run(test.name, func(t *testing.T) { 159 sql, args := test.stmt.MustCompile() 160 161 any := false 162 idx := 0 163 for i, config := range test.expectedConfig { 164 idx = i 165 equalSql := config.sql == sql 166 equalArgs := slices.Equal(config.args, args) 167 if equalSql && equalArgs { 168 any = true 169 } 170 } 171 172 if !any { 173 t.Errorf("Config did not match: %d: %q; got %q, %q", idx, test.expectedConfig[idx], sql, args) 174 } 175 176 }) 177 } 178} 179 180func TestUpdateCompileFail(t *testing.T) { 181 tests := []struct { 182 name string 183 stmt Compiler 184 expectedError string 185 }{ 186 { 187 name: "No SET clauses", 188 stmt: Update("users"), 189 expectedError: "no SET clauses supplied", 190 }, 191 } 192 193 for _, test := range tests { 194 t.Run(test.name, func(t *testing.T) { 195 sql, args, err := test.stmt.Compile() 196 if err == nil { 197 t.Error("Expected error, got nil") 198 } 199 200 if err.Error() != test.expectedError { 201 t.Errorf("Expected error '%s', got '%s'", test.expectedError, err.Error()) 202 } 203 204 if sql != "" { 205 t.Errorf("Expected empty SQL on error, got '%s'", sql) 206 } 207 208 if args != nil { 209 t.Errorf("Expected empty args on error, got '%q'", args) 210 } 211 }) 212 } 213} 214 215func TestUpdateIntegration(t *testing.T) { 216 tests := []struct { 217 name string 218 stmt Execer 219 expectedRows int64 220 }{ 221 { 222 name: "Update all users salary", 223 stmt: Update("users").Set("salary", 100000.0), 224 expectedRows: 6, 225 }, 226 { 227 name: "Update active users only", 228 stmt: Update("users"). 229 Set("department", "Updated Department"). 230 Where(Eq("active", true)), 231 expectedRows: 4, 232 }, 233 { 234 name: "Update users in Engineering", 235 stmt: Update("users"). 236 Set("salary", 95000.0). 237 Where(Eq("department", "Engineering")), 238 expectedRows: 3, 239 }, 240 { 241 name: "Update users with age > 30", 242 stmt: Update("users"). 243 Set("active", false). 244 Where(Gt("age", 30)), 245 expectedRows: 2, 246 }, 247 { 248 name: "Update users with multiple conditions", 249 stmt: Update("users"). 250 Set("salary", 120000.0). 251 Where(Eq("department", "Engineering").And(Eq("active", true))), 252 expectedRows: 2, 253 }, 254 { 255 name: "Update with OR IGNORE (no conflict expected)", 256 stmt: Update("users"). 257 Or(UpdateIgnore). 258 Set("name", "Updated Name"). 259 Where(Eq("id", 1)), 260 expectedRows: 1, 261 }, 262 { 263 name: "Update multiple fields", 264 stmt: Update("users"). 265 Sets(map[string]any{ 266 "active": true, 267 "salary": 110000.0, 268 }). 269 Where(Eq("department", "Marketing")), 270 expectedRows: 2, 271 }, 272 { 273 name: "Update with no matching rows", 274 stmt: Update("users"). 275 Set("name", "Non-existent"). 276 Where(Eq("id", 999)), 277 expectedRows: 0, 278 }, 279 } 280 281 for _, test := range tests { 282 t.Run(test.name, func(t *testing.T) { 283 db := setupTestDB(t) 284 defer db.Close() 285 286 res, err := test.stmt.Exec(db) 287 if err != nil { 288 t.Fatalf("Failed to execute query: %v", err) 289 } 290 291 count, err := res.RowsAffected() 292 if err != nil { 293 t.Fatalf("Failed to get rows affected: %v", err) 294 } 295 296 if count != test.expectedRows { 297 t.Errorf("Expected %d rows, got %d", test.expectedRows, count) 298 } 299 }) 300 } 301}