123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- package dbconnect
- import (
- "context"
- "database/sql"
- "fmt"
- "net/url"
- "strings"
- "testing"
- "time"
- "github.com/kshvakov/clickhouse"
- "github.com/lib/pq"
- "github.com/DATA-DOG/go-sqlmock"
- "github.com/stretchr/testify/assert"
- )
- func TestNewSQLClient(t *testing.T) {
- originURLs := []string{
- "postgres://localhost",
- "cockroachdb://localhost:1337",
- "postgresql://user:pass@127.0.0.1",
- "mysql://localhost",
- "clickhouse://127.0.0.1:9000/?debug",
- "sqlite3::memory:",
- "file:test.db?cache=shared",
- }
- for _, originURL := range originURLs {
- origin, _ := url.Parse(originURL)
- _, err := NewSQLClient(context.Background(), origin)
- assert.NoError(t, err, originURL)
- }
- originURLs = []string{
- "",
- "/",
- "http://localhost",
- "coolthing://user:pass@127.0.0.1",
- }
- for _, originURL := range originURLs {
- origin, _ := url.Parse(originURL)
- _, err := NewSQLClient(context.Background(), origin)
- assert.Error(t, err, originURL)
- }
- }
- func TestArgumentsSQL(t *testing.T) {
- args := []Arguments{
- Arguments{
- Positional: []interface{}{
- "val", 10, 3.14,
- },
- },
- Arguments{
- Named: map[string]interface{}{
- "key": time.Unix(0, 0),
- },
- },
- }
- var nameType sql.NamedArg
- for _, arg := range args {
- arg.sql("")
- for _, named := range arg.Positional {
- assert.IsType(t, nameType, named)
- }
- }
- }
- func TestSQLArg(t *testing.T) {
- tests := []struct {
- key interface{}
- val interface{}
- dialect string
- arg sql.NamedArg
- }{
- {"key", "val", "mssql", sql.Named("key", "val")},
- {0, 1, "sqlite3", sql.Named("0", 1)},
- {1, []string{"a", "b", "c"}, "postgres", sql.Named("1", pq.Array([]string{"a", "b", "c"}))},
- {"in", []uint{0, 1}, "clickhouse", sql.Named("in", clickhouse.Array([]uint{0, 1}))},
- {"", time.Unix(0, 0), "", sql.Named("", time.Unix(0, 0))},
- }
- for _, test := range tests {
- arg := sqlArg(test.key, test.val, test.dialect)
- assert.Equal(t, test.arg, arg, test.key)
- }
- }
- func TestSQLIsolation(t *testing.T) {
- tests := []struct {
- str string
- iso sql.IsolationLevel
- }{
- {"", sql.LevelDefault},
- {"DEFAULT", sql.LevelDefault},
- {"read_UNcommitted", sql.LevelReadUncommitted},
- {"serializable", sql.LevelSerializable},
- {"none", sql.IsolationLevel(-1)},
- {"SNAP shot", -2},
- {"blah", -2},
- }
- for _, test := range tests {
- iso, err := sqlIsolation(test.str)
- if test.iso < -1 {
- assert.Error(t, err, test.str)
- } else {
- assert.NoError(t, err)
- assert.Equal(t, test.iso, iso, test.str)
- }
- }
- }
- func TestSQLMode(t *testing.T) {
- modes := []string{
- "query",
- "exec",
- }
- for _, mode := range modes {
- actual, err := sqlMode(mode)
- assert.NoError(t, err)
- assert.Equal(t, strings.ToLower(mode), actual, mode)
- }
- modes = []string{
- "",
- "blah",
- }
- for _, mode := range modes {
- _, err := sqlMode(mode)
- assert.Error(t, err)
- }
- }
- func helperRows(mockRows *sqlmock.Rows) *sql.Rows {
- db, mock, _ := sqlmock.New()
- mock.ExpectQuery("SELECT").WillReturnRows(mockRows)
- rows, _ := db.Query("SELECT")
- return rows
- }
- func TestSQLRows(t *testing.T) {
- actual, err := sqlRows(helperRows(sqlmock.NewRows(
- []string{"name", "age", "dept"}).
- AddRow("alice", 19, "prod")))
- expected := []map[string]interface{}{map[string]interface{}{
- "name": "alice",
- "age": int64(19),
- "dept": "prod"}}
- assert.NoError(t, err)
- assert.ElementsMatch(t, expected, actual)
- }
- func TestSQLValue(t *testing.T) {
- tests := []struct {
- input interface{}
- output interface{}
- }{
- {"hello", "hello"},
- {1, 1},
- {false, false},
- {[]byte("random"), "random"},
- {[]byte("{\"json\":true}"), map[string]interface{}{"json": true}},
- {[]byte("[]"), []interface{}{}},
- }
- for _, test := range tests {
- assert.Equal(t, test.output, sqlValue(test.input, nil), test.input)
- }
- }
- func TestSQLResultFrom(t *testing.T) {
- res := sqlResultFrom(sqlmock.NewResult(1, 2))
- assert.Equal(t, sqlResult{1, 2}, res)
- res = sqlResultFrom(sqlmock.NewErrorResult(fmt.Errorf("")))
- assert.Equal(t, sqlResult{-1, -1}, res)
- }
- func helperSQLite3(t *testing.T) (context.Context, Client) {
- t.Helper()
- ctx := context.Background()
- url, _ := url.Parse("file::memory:?cache=shared")
- sqlite3, err := NewSQLClient(ctx, url)
- assert.NoError(t, err)
- return ctx, sqlite3
- }
- func TestPing(t *testing.T) {
- ctx, sqlite3 := helperSQLite3(t)
- err := sqlite3.Ping(ctx)
- assert.NoError(t, err)
- }
- func TestSubmit(t *testing.T) {
- ctx, sqlite3 := helperSQLite3(t)
- res, err := sqlite3.Submit(ctx, &Command{
- Statement: "CREATE TABLE t (a INTEGER, b FLOAT, c TEXT, d BLOB);",
- Mode: "exec",
- })
- assert.NoError(t, err)
- assert.Equal(t, sqlResult{0, 0}, res)
- res, err = sqlite3.Submit(ctx, &Command{
- Statement: "SELECT * FROM t;",
- Mode: "query",
- })
- assert.NoError(t, err)
- assert.Empty(t, res)
- res, err = sqlite3.Submit(ctx, &Command{
- Statement: "INSERT INTO t VALUES (?, ?, ?, ?);",
- Mode: "exec",
- Arguments: Arguments{
- Positional: []interface{}{
- 1,
- 3.14,
- "text",
- "blob",
- },
- },
- })
- assert.NoError(t, err)
- assert.Equal(t, sqlResult{1, 1}, res)
- res, err = sqlite3.Submit(ctx, &Command{
- Statement: "UPDATE t SET c = NULL;",
- Mode: "exec",
- })
- assert.NoError(t, err)
- assert.Equal(t, sqlResult{1, 1}, res)
- res, err = sqlite3.Submit(ctx, &Command{
- Statement: "SELECT * FROM t WHERE a = ?;",
- Mode: "query",
- Arguments: Arguments{
- Positional: []interface{}{1},
- },
- })
- assert.NoError(t, err)
- assert.Len(t, res, 1)
- resf, ok := res.([]map[string]interface{})
- assert.True(t, ok)
- assert.EqualValues(t, map[string]interface{}{
- "a": int64(1),
- "b": 3.14,
- "c": nil,
- "d": "blob",
- }, resf[0])
- res, err = sqlite3.Submit(ctx, &Command{
- Statement: "DROP TABLE t;",
- Mode: "exec",
- })
- assert.NoError(t, err)
- assert.Equal(t, sqlResult{1, 1}, res)
- }
- func TestSubmitTransaction(t *testing.T) {
- ctx, sqlite3 := helperSQLite3(t)
- res, err := sqlite3.Submit(ctx, &Command{
- Statement: "BEGIN;",
- Mode: "exec",
- })
- assert.Error(t, err)
- assert.Empty(t, res)
- res, err = sqlite3.Submit(ctx, &Command{
- Statement: "BEGIN; CREATE TABLE tt (a INT); COMMIT;",
- Mode: "exec",
- Isolation: "none",
- })
- assert.NoError(t, err)
- assert.Equal(t, sqlResult{0, 0}, res)
- rows, err := sqlite3.Submit(ctx, &Command{
- Statement: "SELECT * FROM tt;",
- Mode: "query",
- Isolation: "repeatable_read",
- })
- assert.NoError(t, err)
- assert.Empty(t, rows)
- }
- func TestSubmitTimeout(t *testing.T) {
- ctx, sqlite3 := helperSQLite3(t)
- res, err := sqlite3.Submit(ctx, &Command{
- Statement: "SELECT * FROM t;",
- Mode: "query",
- Timeout: 1 * time.Nanosecond,
- })
- assert.Error(t, err)
- assert.Empty(t, res)
- }
- func TestSubmitMode(t *testing.T) {
- ctx, sqlite3 := helperSQLite3(t)
- res, err := sqlite3.Submit(ctx, &Command{
- Statement: "SELECT * FROM t;",
- Mode: "notanoption",
- })
- assert.Error(t, err)
- assert.Empty(t, res)
- }
- func TestSubmitEmpty(t *testing.T) {
- ctx, sqlite3 := helperSQLite3(t)
- res, err := sqlite3.Submit(ctx, &Command{
- Statement: "; ; ; ;",
- Mode: "query",
- })
- assert.Error(t, err)
- assert.Empty(t, res)
- }
|