github.com/Jeffail/benthos/v3@v3.65.0/lib/processor/sql_test.go (about) 1 package processor 2 3 import ( 4 "database/sql" 5 "flag" 6 "fmt" 7 "reflect" 8 "regexp" 9 "strings" 10 "testing" 11 12 "github.com/Jeffail/benthos/v3/lib/log" 13 "github.com/Jeffail/benthos/v3/lib/message" 14 "github.com/Jeffail/benthos/v3/lib/metrics" 15 "github.com/ory/dockertest/v3" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestSQLIntegration(t *testing.T) { 21 if m := flag.Lookup("test.run").Value.String(); m == "" || regexp.MustCompile(strings.Split(m, "/")[0]).FindString(t.Name()) == "" { 22 t.Skip("Skipping as execution was not requested explicitly using go test -run ^TestIntegration$") 23 } 24 25 if testing.Short() { 26 t.Skip("Skipping integration test in short mode") 27 } 28 29 t.Run("TestSQLClickhouseIntegration", SQLClickhouseIntegration) 30 t.Run("TestSQLPostgresIntegration", SQLPostgresIntegration) 31 t.Run("TestSQLMySQLIntegration", SQLMySQLIntegration) 32 t.Run("TestSQLMSSQLIntegration", SQLMSSQLIntegration) 33 } 34 35 func SQLClickhouseIntegration(t *testing.T) { 36 t.Parallel() 37 38 pool, err := dockertest.NewPool("") 39 if err != nil { 40 t.Skipf("Could not connect to docker: %s", err) 41 } 42 43 resource, err := pool.RunWithOptions(&dockertest.RunOptions{ 44 Repository: "yandex/clickhouse-server", 45 ExposedPorts: []string{"9000/tcp"}, 46 }) 47 require.NoError(t, err) 48 49 t.Cleanup(func() { 50 if err = pool.Purge(resource); err != nil { 51 t.Logf("Failed to clean up docker resource: %v", err) 52 } 53 }) 54 55 dsn := fmt.Sprintf("tcp://localhost:%v/", resource.GetPort("9000/tcp")) 56 if err = pool.Retry(func() error { 57 db, dberr := sql.Open("clickhouse", dsn) 58 if dberr != nil { 59 return dberr 60 } 61 if dberr = db.Ping(); err != nil { 62 return dberr 63 } 64 if _, dberr = db.Exec(`create table footable ( 65 foo String, 66 bar Int64, 67 baz String 68 ) engine=Memory;`); dberr != nil { 69 return dberr 70 } 71 return nil 72 }); err != nil { 73 t.Fatalf("Could not connect to docker resource: %s", err) 74 } 75 76 t.Run("testSQLClickhouse", func(t *testing.T) { 77 testSQLClickhouse(t, dsn) 78 }) 79 } 80 81 func testSQLClickhouse(t *testing.T, dsn string) { 82 conf := NewConfig() 83 conf.Type = TypeSQL 84 conf.SQL.Driver = "clickhouse" 85 conf.SQL.DataSourceName = dsn 86 conf.SQL.Query = "INSERT INTO footable (foo, bar, baz) VALUES (?, ?, ?);" 87 conf.SQL.ArgsMapping = `root = [ this.foo, this.bar.floor(), this.baz ]` 88 89 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 90 require.NoError(t, err) 91 92 parts := [][]byte{ 93 []byte(`{"foo":"foo1","bar":11,"baz":"baz1"}`), 94 []byte(`{"foo":"foo2","bar":12,"baz":"baz2"}`), 95 } 96 97 resMsgs, response := s.ProcessMessage(message.New(parts)) 98 require.Nil(t, response) 99 require.Len(t, resMsgs, 1) 100 assert.Equal(t, parts, message.GetAllBytes(resMsgs[0])) 101 require.Empty(t, GetFail(resMsgs[0].Get(0))) 102 require.Empty(t, GetFail(resMsgs[0].Get(1))) 103 104 conf.SQL.Query = "SELECT * FROM footable WHERE foo = ?;" 105 conf.SQL.ArgsMapping = `[ this.foo ]` 106 conf.SQL.ResultCodec = "json_array" 107 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 108 require.NoError(t, err) 109 110 parts = [][]byte{ 111 []byte(`{"foo":"foo1"}`), 112 []byte(`{"foo":"foo2"}`), 113 } 114 115 resMsgs, response = s.ProcessMessage(message.New(parts)) 116 require.Nil(t, response) 117 require.Len(t, resMsgs, 1) 118 119 expParts := [][]byte{ 120 []byte(`[{"bar":11,"baz":"baz1","foo":"foo1"}]`), 121 []byte(`[{"bar":12,"baz":"baz2","foo":"foo2"}]`), 122 } 123 assert.Equal(t, expParts, message.GetAllBytes(resMsgs[0])) 124 } 125 126 func SQLPostgresIntegration(t *testing.T) { 127 t.Parallel() 128 129 pool, err := dockertest.NewPool("") 130 if err != nil { 131 t.Skipf("Could not connect to docker: %s", err) 132 } 133 134 resource, err := pool.RunWithOptions(&dockertest.RunOptions{ 135 Repository: "postgres", 136 ExposedPorts: []string{"5432/tcp"}, 137 Env: []string{ 138 "POSTGRES_USER=testuser", 139 "POSTGRES_PASSWORD=testpass", 140 "POSTGRES_DB=testdb", 141 }, 142 }) 143 if err != nil { 144 t.Fatalf("Could not start resource: %s", err) 145 } 146 147 t.Cleanup(func() { 148 if err = pool.Purge(resource); err != nil { 149 t.Logf("Failed to clean up docker resource: %v", err) 150 } 151 }) 152 153 dsn := fmt.Sprintf("postgres://testuser:testpass@localhost:%v/testdb?sslmode=disable", resource.GetPort("5432/tcp")) 154 if err = pool.Retry(func() error { 155 db, dberr := sql.Open("postgres", dsn) 156 if dberr != nil { 157 return dberr 158 } 159 if dberr = db.Ping(); err != nil { 160 return dberr 161 } 162 if _, dberr = db.Exec(`create table footable ( 163 foo varchar(50) not null, 164 bar integer not null, 165 baz varchar(50) not null, 166 primary key (foo) 167 );`); dberr != nil { 168 return dberr 169 } 170 return nil 171 }); err != nil { 172 t.Fatalf("Could not connect to docker resource: %s", err) 173 } 174 175 t.Run("testSQLPostgresArgsMapping", func(t *testing.T) { 176 testSQLPostgresArgsMapping(t, dsn) 177 }) 178 t.Run("testSQLPostgresArgs", func(t *testing.T) { 179 testSQLPostgresArgs(t, dsn) 180 }) 181 t.Run("testSQLPostgresDeprecated", func(t *testing.T) { 182 testSQLPostgresDeprecated(t, dsn) 183 }) 184 } 185 186 func testSQLPostgresArgsMapping(t *testing.T, dsn string) { 187 conf := NewConfig() 188 conf.Type = TypeSQL 189 conf.SQL.Driver = "postgres" 190 conf.SQL.DataSourceName = dsn 191 conf.SQL.Query = "INSERT INTO footable (foo, bar, baz) VALUES ($1, $2, $3);" 192 conf.SQL.ArgsMapping = `[ this.foo, this.bar, this.baz ]` 193 194 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 195 require.NoError(t, err) 196 197 parts := [][]byte{ 198 []byte(`{"foo":"foo1","bar":11,"baz":"baz1"}`), 199 []byte(`{"foo":"foo2","bar":12,"baz":"baz2"}`), 200 } 201 202 resMsgs, response := s.ProcessMessage(message.New(parts)) 203 require.Nil(t, response) 204 require.Len(t, resMsgs, 1) 205 assert.Equal(t, parts, message.GetAllBytes(resMsgs[0])) 206 207 conf.SQL.Query = "SELECT * FROM footable WHERE foo = $1;" 208 conf.SQL.ArgsMapping = `[ this.foo ]` 209 conf.SQL.ResultCodec = "json_array" 210 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 211 require.NoError(t, err) 212 213 parts = [][]byte{ 214 []byte(`{"foo":"foo1"}`), 215 []byte(`{"foo":"foo2"}`), 216 } 217 218 resMsgs, response = s.ProcessMessage(message.New(parts)) 219 require.Nil(t, response) 220 require.Len(t, resMsgs, 1) 221 222 expParts := [][]byte{ 223 []byte(`[{"bar":11,"baz":"baz1","foo":"foo1"}]`), 224 []byte(`[{"bar":12,"baz":"baz2","foo":"foo2"}]`), 225 } 226 assert.Equal(t, expParts, message.GetAllBytes(resMsgs[0])) 227 } 228 229 func testSQLPostgresArgs(t *testing.T, dsn string) { 230 conf := NewConfig() 231 conf.Type = TypeSQL 232 conf.SQL.Driver = "postgres" 233 conf.SQL.DataSourceName = dsn 234 conf.SQL.Query = "INSERT INTO footable (foo, bar, baz) VALUES ($1, $2, $3);" 235 conf.SQL.Args = []string{ 236 "${! json(\"foo\") }", 237 "${! json(\"bar\") }", 238 "${! json(\"baz\") }", 239 } 240 241 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 242 require.NoError(t, err) 243 244 parts := [][]byte{ 245 []byte(`{"foo":"foo1","bar":11,"baz":"baz1"}`), 246 []byte(`{"foo":"foo2","bar":12,"baz":"baz2"}`), 247 } 248 249 resMsgs, response := s.ProcessMessage(message.New(parts)) 250 require.Nil(t, response) 251 require.Len(t, resMsgs, 1) 252 assert.Equal(t, parts, message.GetAllBytes(resMsgs[0])) 253 254 conf.SQL.Query = "SELECT * FROM footable WHERE foo = $1;" 255 conf.SQL.Args = []string{ 256 "${! json(\"foo\") }", 257 } 258 conf.SQL.ResultCodec = "json_array" 259 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 260 require.NoError(t, err) 261 262 parts = [][]byte{ 263 []byte(`{"foo":"foo1"}`), 264 []byte(`{"foo":"foo2"}`), 265 } 266 267 resMsgs, response = s.ProcessMessage(message.New(parts)) 268 require.Nil(t, response) 269 require.Len(t, resMsgs, 1) 270 271 expParts := [][]byte{ 272 []byte(`[{"bar":11,"baz":"baz1","foo":"foo1"}]`), 273 []byte(`[{"bar":12,"baz":"baz2","foo":"foo2"}]`), 274 } 275 assert.Equal(t, expParts, message.GetAllBytes(resMsgs[0])) 276 } 277 278 func testSQLPostgresDeprecated(t *testing.T, dsn string) { 279 conf := NewConfig() 280 conf.Type = TypeSQL 281 conf.SQL.Driver = "postgres" 282 conf.SQL.DSN = dsn 283 conf.SQL.Query = "INSERT INTO footable (foo, bar, baz) VALUES ($1, $2, $3);" 284 conf.SQL.Args = []string{ 285 "${! json(\"foo\").from(1) }", 286 "${! json(\"bar\").from(1) }", 287 "${! json(\"baz\").from(1) }", 288 } 289 290 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 291 if err != nil { 292 t.Fatal(err) 293 } 294 295 parts := [][]byte{ 296 []byte(`{"foo":"foo3","bar":13,"baz":"baz3"}`), 297 []byte(`{"foo":"foo4","bar":14,"baz":"baz4"}`), 298 } 299 300 resMsgs, response := s.ProcessMessage(message.New(parts)) 301 if response != nil { 302 if response.Error() != nil { 303 t.Fatal(response.Error()) 304 } 305 t.Fatal("Expected nil response") 306 } 307 if len(resMsgs) != 1 { 308 t.Fatalf("Wrong resulting msgs: %v != %v", len(resMsgs), 1) 309 } 310 if act, exp := message.GetAllBytes(resMsgs[0]), parts; !reflect.DeepEqual(exp, act) { 311 t.Fatalf("Wrong result: %s != %s", act, exp) 312 } 313 314 conf.SQL.Query = "SELECT * FROM footable WHERE foo = $1;" 315 conf.SQL.Args = []string{ 316 "${! json(\"foo\").from(1) }", 317 } 318 conf.SQL.ResultCodec = "json_array" 319 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 320 if err != nil { 321 t.Fatal(err) 322 } 323 324 resMsgs, response = s.ProcessMessage(message.New(parts)) 325 if response != nil { 326 if response.Error() != nil { 327 t.Fatal(response.Error()) 328 } 329 t.Fatal("Expected nil response") 330 } 331 if len(resMsgs) != 1 { 332 t.Fatalf("Wrong resulting msgs: %v != %v", len(resMsgs), 1) 333 } 334 expParts := [][]byte{ 335 []byte(`[{"bar":14,"baz":"baz4","foo":"foo4"}]`), 336 } 337 if act, exp := message.GetAllBytes(resMsgs[0]), expParts; !reflect.DeepEqual(exp, act) { 338 t.Fatalf("Wrong result: %s != %s", act, exp) 339 } 340 } 341 342 func SQLMySQLIntegration(t *testing.T) { 343 t.Parallel() 344 345 pool, err := dockertest.NewPool("") 346 if err != nil { 347 t.Skipf("Could not connect to docker: %s", err) 348 } 349 350 resource, err := pool.RunWithOptions(&dockertest.RunOptions{ 351 Repository: "mysql", 352 ExposedPorts: []string{"3306/tcp"}, 353 Env: []string{ 354 "MYSQL_USER=testuser", 355 "MYSQL_PASSWORD=testpass", 356 "MYSQL_DATABASE=testdb", 357 "MYSQL_RANDOM_ROOT_PASSWORD=yes", 358 }, 359 }) 360 if err != nil { 361 t.Fatalf("Could not start resource: %s", err) 362 } 363 364 t.Cleanup(func() { 365 if err = pool.Purge(resource); err != nil { 366 t.Logf("Failed to clean up docker resource: %v", err) 367 } 368 }) 369 370 var db *sql.DB 371 t.Cleanup(func() { 372 if db != nil { 373 db.Close() 374 } 375 }) 376 377 dsn := fmt.Sprintf("testuser:testpass@tcp(localhost:%v)/testdb", resource.GetPort("3306/tcp")) 378 if err = pool.Retry(func() error { 379 var dberr error 380 if db, dberr = sql.Open("mysql", dsn); dberr != nil { 381 return dberr 382 } 383 if dberr = db.Ping(); dberr != nil { 384 db.Close() 385 db = nil 386 return dberr 387 } 388 return nil 389 }); err != nil { 390 t.Fatalf("Could not connect to docker resource: %s", err) 391 } 392 393 t.Run("testSQLMySQLArgsMapping", func(t *testing.T) { 394 testSQLMySQLArgsMapping(t, db, dsn) 395 }) 396 t.Run("testSQLMySQLDynamicQueries", func(t *testing.T) { 397 testSQLMySQLDynamicQueries(t, db, dsn) 398 }) 399 t.Run("testSQLMySQLArgs", func(t *testing.T) { 400 testSQLMySQLArgs(t, db, dsn) 401 }) 402 t.Run("testSQLMySQLDeprecated", func(t *testing.T) { 403 testSQLMySQLDeprecated(t, db, dsn) 404 }) 405 } 406 407 func testSQLMySQLArgsMapping(t *testing.T, db *sql.DB, dsn string) { 408 _, err := db.Exec(`create table footable ( 409 foo varchar(50) not null, 410 bar integer not null, 411 baz varchar(50) not null, 412 primary key (foo) 413 );`) 414 require.NoError(t, err) 415 416 conf := NewConfig() 417 conf.Type = TypeSQL 418 conf.SQL.Driver = "mysql" 419 conf.SQL.DataSourceName = dsn 420 conf.SQL.Query = "INSERT INTO footable (foo, bar, baz) VALUES (?, ?, ?);" 421 conf.SQL.ArgsMapping = `[ this.foo, this.bar, this.baz ]` 422 423 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 424 require.NoError(t, err) 425 426 parts := [][]byte{ 427 []byte(`{"foo":"foo1","bar":11,"baz":"baz1"}`), 428 []byte(`{"foo":"foo2","bar":12,"baz":"baz2"}`), 429 } 430 431 resMsgs, response := s.ProcessMessage(message.New(parts)) 432 require.Nil(t, response) 433 require.Len(t, resMsgs, 1) 434 assert.Equal(t, parts, message.GetAllBytes(resMsgs[0])) 435 436 conf.SQL.Query = "SELECT * FROM footable WHERE foo = ?;" 437 conf.SQL.ArgsMapping = `[ this.foo ]` 438 conf.SQL.ResultCodec = "json_array" 439 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 440 require.NoError(t, err) 441 442 parts = [][]byte{ 443 []byte(`{"foo":"foo1"}`), 444 []byte(`{"foo":"foo2"}`), 445 } 446 447 resMsgs, response = s.ProcessMessage(message.New(parts)) 448 require.Nil(t, response) 449 require.Len(t, resMsgs, 1) 450 451 expParts := [][]byte{ 452 []byte(`[{"bar":11,"baz":"baz1","foo":"foo1"}]`), 453 []byte(`[{"bar":12,"baz":"baz2","foo":"foo2"}]`), 454 } 455 assert.Equal(t, expParts, message.GetAllBytes(resMsgs[0])) 456 } 457 458 func testSQLMySQLDynamicQueries(t *testing.T, db *sql.DB, dsn string) { 459 _, err := db.Exec(`create table bartable ( 460 foo varchar(50) not null, 461 bar integer not null, 462 baz varchar(50) not null, 463 primary key (foo) 464 );`) 465 require.NoError(t, err) 466 467 conf := NewConfig() 468 conf.Type = TypeSQL 469 conf.SQL.Driver = "mysql" 470 conf.SQL.DataSourceName = dsn 471 conf.SQL.Query = `${! json("query") }` 472 conf.SQL.UnsafeDynamicQuery = true 473 conf.SQL.ArgsMapping = `[ this.foo, this.bar, this.baz ]` 474 475 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 476 require.NoError(t, err) 477 478 parts := [][]byte{ 479 []byte(`{"query":"INSERT INTO bartable (foo, bar, baz) VALUES (?, ?, ?);","foo":"foo1","bar":11,"baz":"baz1"}`), 480 []byte(`{"query":"INSERT INTO bartable (foo, bar, baz) VALUES (?, ?, ?);","foo":"foo2","bar":12,"baz":"baz2"}`), 481 } 482 483 resMsgs, response := s.ProcessMessage(message.New(parts)) 484 require.Nil(t, response) 485 require.Len(t, resMsgs, 1) 486 assert.Equal(t, parts, message.GetAllBytes(resMsgs[0])) 487 488 conf.SQL.Query = `${! json("query") }` 489 conf.SQL.ArgsMapping = `[ this.foo ]` 490 conf.SQL.UnsafeDynamicQuery = true 491 conf.SQL.ResultCodec = "json_array" 492 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 493 require.NoError(t, err) 494 495 parts = [][]byte{ 496 []byte(`{"query":"SELECT * FROM bartable WHERE foo = ?;","foo":"foo1"}`), 497 []byte(`{"query":"SELECT * FROM bartable WHERE foo = ?;","foo":"foo2"}`), 498 } 499 500 resMsgs, response = s.ProcessMessage(message.New(parts)) 501 require.Nil(t, response) 502 require.Len(t, resMsgs, 1) 503 504 expParts := [][]byte{ 505 []byte(`[{"bar":11,"baz":"baz1","foo":"foo1"}]`), 506 []byte(`[{"bar":12,"baz":"baz2","foo":"foo2"}]`), 507 } 508 assert.Equal(t, expParts, message.GetAllBytes(resMsgs[0])) 509 } 510 511 func testSQLMySQLArgs(t *testing.T, db *sql.DB, dsn string) { 512 _, err := db.Exec(`create table baztable ( 513 foo varchar(50) not null, 514 bar integer not null, 515 baz varchar(50) not null, 516 primary key (foo) 517 );`) 518 require.NoError(t, err) 519 520 conf := NewConfig() 521 conf.Type = TypeSQL 522 conf.SQL.Driver = "mysql" 523 conf.SQL.DataSourceName = dsn 524 conf.SQL.Query = "INSERT INTO baztable (foo, bar, baz) VALUES (?, ?, ?);" 525 conf.SQL.Args = []string{ 526 "${! json(\"foo\") }", 527 "${! json(\"bar\") }", 528 "${! json(\"baz\") }", 529 } 530 531 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 532 require.NoError(t, err) 533 534 parts := [][]byte{ 535 []byte(`{"foo":"foo1","bar":11,"baz":"baz1"}`), 536 []byte(`{"foo":"foo2","bar":12,"baz":"baz2"}`), 537 } 538 539 resMsgs, response := s.ProcessMessage(message.New(parts)) 540 require.Nil(t, response) 541 require.Len(t, resMsgs, 1) 542 assert.Equal(t, parts, message.GetAllBytes(resMsgs[0])) 543 544 conf.SQL.Query = "SELECT * FROM baztable WHERE foo = ?;" 545 conf.SQL.Args = []string{ 546 "${! json(\"foo\") }", 547 } 548 conf.SQL.ResultCodec = "json_array" 549 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 550 require.NoError(t, err) 551 552 parts = [][]byte{ 553 []byte(`{"foo":"foo1"}`), 554 []byte(`{"foo":"foo2"}`), 555 } 556 557 resMsgs, response = s.ProcessMessage(message.New(parts)) 558 require.Nil(t, response) 559 require.Len(t, resMsgs, 1) 560 561 expParts := [][]byte{ 562 []byte(`[{"bar":11,"baz":"baz1","foo":"foo1"}]`), 563 []byte(`[{"bar":12,"baz":"baz2","foo":"foo2"}]`), 564 } 565 assert.Equal(t, expParts, message.GetAllBytes(resMsgs[0])) 566 } 567 568 func testSQLMySQLDeprecated(t *testing.T, db *sql.DB, dsn string) { 569 _, err := db.Exec(`create table buztable ( 570 foo varchar(50) not null, 571 bar integer not null, 572 baz varchar(50) not null, 573 primary key (foo) 574 );`) 575 require.NoError(t, err) 576 577 conf := NewConfig() 578 conf.Type = TypeSQL 579 conf.SQL.Driver = "mysql" 580 conf.SQL.DSN = dsn 581 conf.SQL.Query = "INSERT INTO buztable (foo, bar, baz) VALUES (?, ?, ?);" 582 conf.SQL.Args = []string{ 583 "${! json(\"foo\").from(1) }", 584 "${! json(\"bar\").from(1) }", 585 "${! json(\"baz\").from(1) }", 586 } 587 588 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 589 if err != nil { 590 t.Fatal(err) 591 } 592 593 parts := [][]byte{ 594 []byte(`{"foo":"foo3","bar":13,"baz":"baz3"}`), 595 []byte(`{"foo":"foo4","bar":14,"baz":"baz4"}`), 596 } 597 598 resMsgs, response := s.ProcessMessage(message.New(parts)) 599 if response != nil { 600 if response.Error() != nil { 601 t.Fatal(response.Error()) 602 } 603 t.Fatal("Expected nil response") 604 } 605 if len(resMsgs) != 1 { 606 t.Fatalf("Wrong resulting msgs: %v != %v", len(resMsgs), 1) 607 } 608 if act, exp := message.GetAllBytes(resMsgs[0]), parts; !reflect.DeepEqual(exp, act) { 609 t.Fatalf("Wrong result: %s != %s", act, exp) 610 } 611 612 conf.SQL.Query = "SELECT * FROM buztable WHERE foo = ?;" 613 conf.SQL.Args = []string{ 614 "${! json(\"foo\").from(1) }", 615 } 616 conf.SQL.ResultCodec = "json_array" 617 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 618 if err != nil { 619 t.Fatal(err) 620 } 621 622 resMsgs, response = s.ProcessMessage(message.New(parts)) 623 if response != nil { 624 if response.Error() != nil { 625 t.Fatal(response.Error()) 626 } 627 t.Fatal("Expected nil response") 628 } 629 if len(resMsgs) != 1 { 630 t.Fatalf("Wrong resulting msgs: %v != %v", len(resMsgs), 1) 631 } 632 expParts := [][]byte{ 633 []byte(`[{"bar":14,"baz":"baz4","foo":"foo4"}]`), 634 } 635 if act, exp := message.GetAllBytes(resMsgs[0]), expParts; !reflect.DeepEqual(exp, act) { 636 t.Fatalf("Wrong result: %s != %s", act, exp) 637 } 638 } 639 640 func SQLMSSQLIntegration(t *testing.T) { 641 t.Parallel() 642 643 pool, err := dockertest.NewPool("") 644 if err != nil { 645 t.Skipf("Could not connect to docker: %s", err) 646 } 647 648 testPassword := "ins4n3lyStrongP4ssword" 649 resource, err := pool.RunWithOptions(&dockertest.RunOptions{ 650 Repository: "mcr.microsoft.com/mssql/server", 651 ExposedPorts: []string{"1433/tcp"}, 652 Env: []string{ 653 "ACCEPT_EULA=Y", 654 "SA_PASSWORD=" + testPassword, 655 }, 656 }) 657 if err != nil { 658 t.Fatalf("Could not start resource: %s", err) 659 } 660 661 t.Cleanup(func() { 662 if err = pool.Purge(resource); err != nil { 663 t.Logf("Failed to clean up docker resource: %v", err) 664 } 665 }) 666 dsn := fmt.Sprintf("sqlserver://sa:"+testPassword+"@localhost:%v?database=master", resource.GetPort("1433/tcp")) 667 if err = pool.Retry(func() error { 668 db, dberr := sql.Open("mssql", dsn) 669 if dberr != nil { 670 return dberr 671 } 672 if dberr = db.Ping(); err != nil { 673 return dberr 674 } 675 if _, dberr = db.Exec(`create table footable ( 676 foo varchar(50) not null, 677 bar integer not null, 678 baz varchar(50) not null, 679 primary key (foo) 680 );`); dberr != nil { 681 return dberr 682 } 683 return nil 684 }); err != nil { 685 t.Fatalf("Could not connect to docker resource: %s", err) 686 } 687 688 t.Run("testSQLMSSQLArgsMapping", func(t *testing.T) { 689 testSQLMSSQLArgsMapping(t, dsn) 690 }) 691 t.Run("testSQLMSSQLArgs", func(t *testing.T) { 692 testSQLMSSQLArgs(t, dsn) 693 }) 694 t.Run("testSQLMSSQLDeprecated", func(t *testing.T) { 695 testSQLMSSQLDeprecated(t, dsn) 696 }) 697 } 698 699 func testSQLMSSQLArgsMapping(t *testing.T, dsn string) { 700 conf := NewConfig() 701 conf.Type = TypeSQL 702 conf.SQL.Driver = "mssql" 703 conf.SQL.DataSourceName = dsn 704 conf.SQL.Query = "INSERT INTO footable (foo, bar, baz) VALUES ($1, $2, $3);" 705 conf.SQL.ArgsMapping = `[ this.foo, this.bar, this.baz ]` 706 707 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 708 require.NoError(t, err) 709 710 parts := [][]byte{ 711 []byte(`{"foo":"foo1","bar":11,"baz":"baz1"}`), 712 []byte(`{"foo":"foo2","bar":12,"baz":"baz2"}`), 713 } 714 715 resMsgs, response := s.ProcessMessage(message.New(parts)) 716 require.Nil(t, response) 717 require.Len(t, resMsgs, 1) 718 assert.Equal(t, parts, message.GetAllBytes(resMsgs[0])) 719 720 conf.SQL.Query = "SELECT * FROM footable WHERE foo = $1;" 721 conf.SQL.ArgsMapping = `[ this.foo ]` 722 conf.SQL.ResultCodec = "json_array" 723 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 724 require.NoError(t, err) 725 726 parts = [][]byte{ 727 []byte(`{"foo":"foo1"}`), 728 []byte(`{"foo":"foo2"}`), 729 } 730 731 resMsgs, response = s.ProcessMessage(message.New(parts)) 732 require.Nil(t, response) 733 require.Len(t, resMsgs, 1) 734 735 expParts := [][]byte{ 736 []byte(`[{"bar":11,"baz":"baz1","foo":"foo1"}]`), 737 []byte(`[{"bar":12,"baz":"baz2","foo":"foo2"}]`), 738 } 739 assert.Equal(t, expParts, message.GetAllBytes(resMsgs[0])) 740 } 741 742 func testSQLMSSQLArgs(t *testing.T, dsn string) { 743 conf := NewConfig() 744 conf.Type = TypeSQL 745 conf.SQL.Driver = "mssql" 746 conf.SQL.DataSourceName = dsn 747 conf.SQL.Query = "INSERT INTO footable (foo, bar, baz) VALUES ($1, $2, $3);" 748 conf.SQL.Args = []string{ 749 "${! json(\"foo\") }", 750 "${! json(\"bar\") }", 751 "${! json(\"baz\") }", 752 } 753 754 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 755 require.NoError(t, err) 756 757 parts := [][]byte{ 758 []byte(`{"foo":"foo1","bar":11,"baz":"baz1"}`), 759 []byte(`{"foo":"foo2","bar":12,"baz":"baz2"}`), 760 } 761 762 resMsgs, response := s.ProcessMessage(message.New(parts)) 763 require.Nil(t, response) 764 require.Len(t, resMsgs, 1) 765 assert.Equal(t, parts, message.GetAllBytes(resMsgs[0])) 766 767 conf.SQL.Query = "SELECT * FROM footable WHERE foo = $1;" 768 conf.SQL.Args = []string{ 769 "${! json(\"foo\") }", 770 } 771 conf.SQL.ResultCodec = "json_array" 772 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 773 require.NoError(t, err) 774 775 parts = [][]byte{ 776 []byte(`{"foo":"foo1"}`), 777 []byte(`{"foo":"foo2"}`), 778 } 779 780 resMsgs, response = s.ProcessMessage(message.New(parts)) 781 require.Nil(t, response) 782 require.Len(t, resMsgs, 1) 783 784 expParts := [][]byte{ 785 []byte(`[{"bar":11,"baz":"baz1","foo":"foo1"}]`), 786 []byte(`[{"bar":12,"baz":"baz2","foo":"foo2"}]`), 787 } 788 assert.Equal(t, expParts, message.GetAllBytes(resMsgs[0])) 789 } 790 791 func testSQLMSSQLDeprecated(t *testing.T, dsn string) { 792 conf := NewConfig() 793 conf.Type = TypeSQL 794 conf.SQL.Driver = "mssql" 795 conf.SQL.DSN = dsn 796 conf.SQL.Query = "INSERT INTO footable (foo, bar, baz) VALUES ($1, $2, $3);" 797 conf.SQL.Args = []string{ 798 "${! json(\"foo\").from(1) }", 799 "${! json(\"bar\").from(1) }", 800 "${! json(\"baz\").from(1) }", 801 } 802 803 s, err := NewSQL(conf, nil, log.Noop(), metrics.Noop()) 804 if err != nil { 805 t.Fatal(err) 806 } 807 808 parts := [][]byte{ 809 []byte(`{"foo":"foo3","bar":13,"baz":"baz3"}`), 810 []byte(`{"foo":"foo4","bar":14,"baz":"baz4"}`), 811 } 812 813 resMsgs, response := s.ProcessMessage(message.New(parts)) 814 if response != nil { 815 if response.Error() != nil { 816 t.Fatal(response.Error()) 817 } 818 t.Fatal("Expected nil response") 819 } 820 if len(resMsgs) != 1 { 821 t.Fatalf("Wrong resulting msgs: %v != %v", len(resMsgs), 1) 822 } 823 if act, exp := message.GetAllBytes(resMsgs[0]), parts; !reflect.DeepEqual(exp, act) { 824 t.Fatalf("Wrong result: %s != %s", act, exp) 825 } 826 827 conf.SQL.Query = "SELECT * FROM footable WHERE foo = $1;" 828 conf.SQL.Args = []string{ 829 "${! json(\"foo\").from(1) }", 830 } 831 conf.SQL.ResultCodec = "json_array" 832 s, err = NewSQL(conf, nil, log.Noop(), metrics.Noop()) 833 if err != nil { 834 t.Fatal(err) 835 } 836 837 resMsgs, response = s.ProcessMessage(message.New(parts)) 838 if response != nil { 839 if response.Error() != nil { 840 t.Fatal(response.Error()) 841 } 842 t.Fatal("Expected nil response") 843 } 844 if len(resMsgs) != 1 { 845 t.Fatalf("Wrong resulting msgs: %v != %v", len(resMsgs), 1) 846 } 847 expParts := [][]byte{ 848 []byte(`[{"bar":14,"baz":"baz4","foo":"foo4"}]`), 849 } 850 if act, exp := message.GetAllBytes(resMsgs[0]), expParts; !reflect.DeepEqual(exp, act) { 851 t.Fatalf("Wrong result: %s != %s", act, exp) 852 } 853 }