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}