github.com/square/finch@v0.0.0-20240412205204-6530c03e2b96/trx/trx_test.go (about) 1 // Copyright 2024 Block, Inc. 2 3 package trx_test 4 5 import ( 6 "testing" 7 8 "github.com/go-test/deep" 9 10 "github.com/square/finch" 11 "github.com/square/finch/config" 12 "github.com/square/finch/data" 13 "github.com/square/finch/trx" 14 ) 15 16 var p = map[string]string{} 17 18 func TestLoad_001(t *testing.T) { 19 // The most basic test: 1 query, 1 @d, nothing fancy. 20 trxList := []config.Trx{ 21 { 22 Name: "001.sql", // must set because we don't call Validate 23 File: "../test/trx/001.sql", 24 Data: map[string]config.Data{ 25 "id": { 26 Generator: "int", 27 }, 28 }, 29 }, 30 } 31 32 scope := data.NewScope() 33 got, err := trx.Load(trxList, scope, p) 34 if err != nil { 35 t.Fatal(err) 36 } 37 38 expect := &trx.Set{ 39 Order: []string{"001.sql"}, 40 Statements: map[string][]*trx.Statement{ 41 "001.sql": []*trx.Statement{ 42 { 43 Trx: "001.sql", 44 Query: "select c from t where id=%d", 45 Inputs: []string{"@id"}, 46 ResultSet: true, 47 Calls: []byte{0}, 48 }, 49 }, 50 }, 51 Data: &data.Scope{ 52 Keys: map[string]data.Key{ 53 "@id": data.Key{ 54 Name: "@id", 55 Trx: "001.sql", 56 Line: 1, 57 Statement: 1, 58 Column: -1, 59 Scope: finch.SCOPE_STATEMENT, 60 }, 61 }, 62 CopiedAt: map[string]finch.RunLevel{}, 63 }, 64 Meta: map[string]trx.Meta{ 65 "001.sql": {DDL: false}, 66 }, 67 } 68 69 if diff := deep.Equal(got, expect); diff != nil { 70 t.Error(diff) 71 t.Logf("got: %#v", got) 72 } 73 } 74 75 func TestLoad_002(t *testing.T) { 76 // Basic "@d AND @PREV" test: @PREV causes no additional data gen, but it 77 // should be interpolated into query like "%d AND %d", and then the 1 data gen 78 // should return 2 values./ 79 trxList := []config.Trx{ 80 { 81 Name: "002.sql", // must set because we don't call Validate 82 File: "../test/trx/002.sql", 83 Data: map[string]config.Data{ 84 "d": { 85 Generator: "int", 86 }, 87 }, 88 }, 89 } 90 91 scope := data.NewScope() 92 got, err := trx.Load(trxList, scope, p) 93 if err != nil { 94 t.Fatal(err) 95 } 96 97 expect := &trx.Set{ 98 Order: []string{"002.sql"}, 99 Statements: map[string][]*trx.Statement{ 100 "002.sql": []*trx.Statement{ 101 { 102 Trx: "002.sql", 103 Query: "SELECT c FROM t WHERE id BETWEEN %d AND %d", 104 Inputs: []string{"@d", "@PREV"}, 105 ResultSet: true, 106 Calls: []byte{0, 0}, 107 }, 108 }, 109 }, 110 Data: &data.Scope{ 111 Keys: map[string]data.Key{ 112 "@d": data.Key{ 113 Name: "@d", 114 Trx: "002.sql", 115 Line: 2, 116 Statement: 1, 117 Column: -1, 118 Scope: finch.SCOPE_STATEMENT, 119 }, 120 // No key for @PREV, but it is in Inputs ^ 121 }, 122 CopiedAt: map[string]finch.RunLevel{}, 123 }, 124 Meta: map[string]trx.Meta{ 125 "002.sql": {DDL: false}, 126 }, 127 } 128 129 if diff := deep.Equal(got, expect); diff != nil { 130 t.Error(diff) 131 t.Logf("got: %#v", got) 132 } 133 } 134 135 func TestLoad_003(t *testing.T) { 136 // Basic column ref: one stmt saves a col (output), the other uses it as input 137 trxList := []config.Trx{ 138 { 139 Name: "003.sql", // must set because we don't call Validate 140 File: "../test/trx/003.sql", 141 Data: map[string]config.Data{ 142 "c": { 143 Generator: "str-not-null", 144 Params: map[string]string{ 145 "quote-value": "yes", 146 }, 147 }, 148 }, 149 }, 150 } 151 152 scope := data.NewScope() 153 got, err := trx.Load(trxList, scope, p) 154 if err != nil { 155 t.Fatal(err) 156 } 157 158 expect := &trx.Set{ 159 Order: []string{"003.sql"}, 160 Statements: map[string][]*trx.Statement{ 161 "003.sql": []*trx.Statement{ 162 { 163 Trx: "003.sql", 164 Query: "select c from t1 where id=1", 165 Inputs: nil, 166 Outputs: []string{"@c"}, 167 ResultSet: true, 168 // no Calls because this is an output column 169 }, 170 { 171 Trx: "003.sql", 172 Query: "insert into t2 values ('%v')", 173 Inputs: []string{"@c"}, 174 Outputs: nil, 175 Write: true, 176 Calls: []byte{0}, 177 }, 178 }, 179 }, 180 Data: &data.Scope{ 181 Keys: map[string]data.Key{ 182 "@c": data.Key{ 183 Name: "@c", 184 Trx: "003.sql", 185 Line: 4, 186 Statement: 1, 187 Column: 0, 188 Scope: "", 189 }, 190 }, 191 CopiedAt: map[string]finch.RunLevel{}, 192 }, 193 Meta: map[string]trx.Meta{ 194 "003.sql": {DDL: false}, 195 }, 196 } 197 198 if diff := deep.Equal(got, expect); diff != nil { 199 t.Error(diff) 200 t.Logf("got: %#v", got) 201 } 202 } 203 204 func TestLoad_copy3(t *testing.T) { 205 // -- copy: 3 should yield 3x the same query. copy3-1.sql has the copy: 3 206 // mod first, then a prepare mode. copy3-2.sql has the reverse. This is to 207 // test that copy works with other mods in any order. Either way, same result: 208 expect := &trx.Set{ 209 Order: []string{"copy3"}, 210 Statements: map[string][]*trx.Statement{ 211 "copy3": []*trx.Statement{ 212 { 213 Trx: "copy3", 214 Query: "select c from t where id=?", 215 Inputs: []string{"@id"}, 216 ResultSet: true, 217 Prepare: true, 218 PrepareMulti: 3, 219 Calls: []byte{0}, 220 }, 221 { 222 Trx: "copy3", 223 Query: "select c from t where id=?", 224 Inputs: []string{"@id"}, 225 ResultSet: true, 226 Prepare: true, 227 PrepareMulti: 0, 228 Calls: []byte{0}, 229 }, 230 231 { 232 Trx: "copy3", 233 Query: "select c from t where id=?", 234 Inputs: []string{"@id"}, 235 ResultSet: true, 236 Prepare: true, 237 PrepareMulti: 0, 238 Calls: []byte{0}, 239 }, 240 }, 241 }, 242 Data: &data.Scope{ 243 Keys: map[string]data.Key{ 244 "@id": data.Key{ 245 Name: "@id", 246 Trx: "copy3", 247 Line: 4, 248 Statement: 1, 249 Column: -1, 250 Scope: finch.SCOPE_TRX, 251 }, 252 }, 253 CopiedAt: map[string]finch.RunLevel{}, 254 }, 255 Meta: map[string]trx.Meta{ 256 "copy3": {DDL: false}, 257 }, 258 } 259 260 trxList := []config.Trx{ 261 { 262 Name: "copy3", // must set because we don't call Validate 263 File: "../test/trx/copy3-1.sql", 264 Data: map[string]config.Data{ 265 "id": { 266 Generator: "int", 267 Scope: "trx", 268 }, 269 }, 270 }, 271 } 272 273 scope := data.NewScope() 274 got, err := trx.Load(trxList, scope, p) 275 if err != nil { 276 t.Fatal(err) 277 } 278 if diff := deep.Equal(got, expect); diff != nil { 279 t.Error(diff) 280 t.Logf("got: %#v", got) 281 } 282 283 // ---------------------------------------------------------------------- 284 285 trxList = []config.Trx{ 286 { 287 Name: "copy3", 288 File: "../test/trx/copy3-2.sql", 289 Data: map[string]config.Data{ 290 "id": { 291 Generator: "int", 292 Scope: "trx", 293 }, 294 }, 295 }, 296 } 297 298 scope = data.NewScope() 299 got, err = trx.Load(trxList, scope, p) 300 if err != nil { 301 t.Fatal(err) 302 } 303 if diff := deep.Equal(got, expect); diff != nil { 304 t.Error(diff) 305 t.Logf("got: %#v", got) 306 } 307 } 308 309 func TestLoad_COPY_NUMBER(t *testing.T) { 310 // /*!copy-number*/ should be replaced with the copy number 311 expect := &trx.Set{ 312 Order: []string{"copyNo"}, 313 Statements: map[string][]*trx.Statement{ 314 "copyNo": []*trx.Statement{ 315 { 316 Trx: "copyNo", 317 Query: "select c from t1 where id=1", 318 ResultSet: true, 319 }, 320 { 321 Trx: "copyNo", 322 Query: "select c from t2 where id=1", 323 ResultSet: true, 324 }, 325 }, 326 }, 327 Data: &data.Scope{ 328 Keys: map[string]data.Key{}, // no @d 329 CopiedAt: map[string]finch.RunLevel{}, 330 }, 331 Meta: map[string]trx.Meta{ 332 "copyNo": {DDL: false}, 333 }, 334 } 335 336 trxList := []config.Trx{ 337 { 338 Name: "copyNo", // must set because we don't call Validate 339 File: "../test/trx/copy-no.sql", 340 Data: map[string]config.Data{ 341 "id": { 342 Generator: "int", 343 Scope: "trx", 344 }, 345 }, 346 }, 347 } 348 349 scope := data.NewScope() 350 got, err := trx.Load(trxList, scope, p) 351 if err != nil { 352 t.Fatal(err) 353 } 354 if diff := deep.Equal(got, expect); diff != nil { 355 t.Error(diff) 356 t.Logf("got: %#v", got) 357 } 358 } 359 360 func TestLoad_RowScopeCSV(t *testing.T) { 361 file := "rowscope-csv.sql" 362 trxList := []config.Trx{ 363 { 364 Name: file, // must set because we don't call Validate 365 File: "../test/trx/" + file, 366 Data: map[string]config.Data{ 367 "d": { 368 Generator: "auto-inc", 369 }, 370 }, 371 }, 372 } 373 374 scope := data.NewScope() 375 got, err := trx.Load(trxList, scope, p) 376 if err != nil { 377 t.Fatal(err) 378 } 379 380 expect := &trx.Set{ 381 Order: []string{file}, 382 Statements: map[string][]*trx.Statement{ 383 file: []*trx.Statement{ 384 { 385 Trx: file, 386 Query: "SELECT 1 -- (%d, %d %% 1000, '%d', '%d'), (%d, %d %% 1000, '%d', '%d')", 387 Inputs: []string{"@d", "@d", "@d", "@d", "@d", "@d", "@d", "@d"}, 388 Calls: []byte{1, 0, 0, 0, 1, 0, 0, 0}, 389 ResultSet: true, 390 }, 391 392 { 393 Trx: file, 394 Query: "SELECT 1 -- (%d, %d %% 1000, '%d', '%d'), (%d, %d %% 1000, '%d', '%d')", 395 Inputs: []string{"@d", "@d", "@d", "@d", "@d", "@d", "@d", "@d"}, 396 Calls: []byte{1, 0, 0, 0, 1, 0, 0, 0}, 397 ResultSet: true, 398 }, 399 }, 400 }, 401 Data: &data.Scope{ 402 Keys: map[string]data.Key{ 403 "@d": data.Key{ 404 Name: "@d", 405 Trx: file, 406 Line: 1, 407 Statement: 1, 408 Column: -1, 409 Scope: finch.SCOPE_ROW, 410 }, 411 }, 412 CopiedAt: map[string]finch.RunLevel{}, 413 }, 414 Meta: map[string]trx.Meta{ 415 file: {}, 416 }, 417 } 418 419 if diff := deep.Equal(got, expect); diff != nil { 420 t.Error(diff) 421 t.Logf("got: %#v", got) 422 } 423 } 424 425 func TestCalls(t *testing.T) { 426 type test struct { 427 inputs []string 428 expect []byte 429 } 430 callTests := []test{ 431 {[]string{"@d"}, []byte{0}}, 432 {[]string{"@d", "@d"}, []byte{0, 0}}, 433 {[]string{"@d()"}, []byte{1}}, 434 {[]string{"@d()", "@d"}, []byte{1, 0}}, 435 {[]string{"@d", "@x", "@d()"}, []byte{0, 0, 1}}, 436 {[]string{"@d()", "@d()", "@d()"}, []byte{1, 1, 1}}, 437 } 438 for _, c := range callTests { 439 got := trx.Calls(c.inputs) 440 if diff := deep.Equal(got, c.expect); diff != nil { 441 t.Logf("%s -> %v", c.inputs, got) 442 t.Error(diff) 443 } 444 } 445 } 446 447 func TestRowScope(t *testing.T) { 448 s := map[string]bool{ 449 "@d": true, 450 "@x": true, 451 } 452 type test struct { 453 keys map[string]bool 454 in string 455 expect string 456 } 457 callTests := []test{ 458 {s, "(@d)", "(@d())"}, 459 {s, "(@d())", "(@d())"}, 460 {s, "(@d(), @d)", "(@d(), @d)"}, 461 {s, "(@d, @d)", "(@d(), @d)"}, 462 {s, "(@d, @x, @d)", "(@d(), @x(), @d)"}, 463 {s, "(@d, @x(), @d)", "(@d(), @x(), @d)"}, 464 } 465 for _, c := range callTests { 466 got := trx.RowScope(c.keys, c.in) 467 if diff := deep.Equal(got, c.expect); diff != nil { 468 t.Error(diff) 469 t.Logf("%s -> %v", c.in, got) 470 } 471 } 472 }