sql_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. package dbconnect
  2. import (
  3. "context"
  4. "database/sql"
  5. "fmt"
  6. "net/url"
  7. "strings"
  8. "testing"
  9. "time"
  10. "github.com/kshvakov/clickhouse"
  11. "github.com/lib/pq"
  12. "github.com/DATA-DOG/go-sqlmock"
  13. "github.com/stretchr/testify/assert"
  14. )
  15. func TestNewSQLClient(t *testing.T) {
  16. originURLs := []string{
  17. "postgres://localhost",
  18. "cockroachdb://localhost:1337",
  19. "postgresql://user:pass@127.0.0.1",
  20. "mysql://localhost",
  21. "clickhouse://127.0.0.1:9000/?debug",
  22. "sqlite3::memory:",
  23. "file:test.db?cache=shared",
  24. }
  25. for _, originURL := range originURLs {
  26. origin, _ := url.Parse(originURL)
  27. _, err := NewSQLClient(context.Background(), origin)
  28. assert.NoError(t, err, originURL)
  29. }
  30. originURLs = []string{
  31. "",
  32. "/",
  33. "http://localhost",
  34. "coolthing://user:pass@127.0.0.1",
  35. }
  36. for _, originURL := range originURLs {
  37. origin, _ := url.Parse(originURL)
  38. _, err := NewSQLClient(context.Background(), origin)
  39. assert.Error(t, err, originURL)
  40. }
  41. }
  42. func TestArgumentsSQL(t *testing.T) {
  43. args := []Arguments{
  44. Arguments{
  45. Positional: []interface{}{
  46. "val", 10, 3.14,
  47. },
  48. },
  49. Arguments{
  50. Named: map[string]interface{}{
  51. "key": time.Unix(0, 0),
  52. },
  53. },
  54. }
  55. var nameType sql.NamedArg
  56. for _, arg := range args {
  57. arg.sql("")
  58. for _, named := range arg.Positional {
  59. assert.IsType(t, nameType, named)
  60. }
  61. }
  62. }
  63. func TestSQLArg(t *testing.T) {
  64. tests := []struct {
  65. key interface{}
  66. val interface{}
  67. dialect string
  68. arg sql.NamedArg
  69. }{
  70. {"key", "val", "mssql", sql.Named("key", "val")},
  71. {0, 1, "sqlite3", sql.Named("0", 1)},
  72. {1, []string{"a", "b", "c"}, "postgres", sql.Named("1", pq.Array([]string{"a", "b", "c"}))},
  73. {"in", []uint{0, 1}, "clickhouse", sql.Named("in", clickhouse.Array([]uint{0, 1}))},
  74. {"", time.Unix(0, 0), "", sql.Named("", time.Unix(0, 0))},
  75. }
  76. for _, test := range tests {
  77. arg := sqlArg(test.key, test.val, test.dialect)
  78. assert.Equal(t, test.arg, arg, test.key)
  79. }
  80. }
  81. func TestSQLIsolation(t *testing.T) {
  82. tests := []struct {
  83. str string
  84. iso sql.IsolationLevel
  85. }{
  86. {"", sql.LevelDefault},
  87. {"DEFAULT", sql.LevelDefault},
  88. {"read_UNcommitted", sql.LevelReadUncommitted},
  89. {"serializable", sql.LevelSerializable},
  90. {"none", sql.IsolationLevel(-1)},
  91. {"SNAP shot", -2},
  92. {"blah", -2},
  93. }
  94. for _, test := range tests {
  95. iso, err := sqlIsolation(test.str)
  96. if test.iso < -1 {
  97. assert.Error(t, err, test.str)
  98. } else {
  99. assert.NoError(t, err)
  100. assert.Equal(t, test.iso, iso, test.str)
  101. }
  102. }
  103. }
  104. func TestSQLMode(t *testing.T) {
  105. modes := []string{
  106. "query",
  107. "exec",
  108. }
  109. for _, mode := range modes {
  110. actual, err := sqlMode(mode)
  111. assert.NoError(t, err)
  112. assert.Equal(t, strings.ToLower(mode), actual, mode)
  113. }
  114. modes = []string{
  115. "",
  116. "blah",
  117. }
  118. for _, mode := range modes {
  119. _, err := sqlMode(mode)
  120. assert.Error(t, err)
  121. }
  122. }
  123. func helperRows(mockRows *sqlmock.Rows) *sql.Rows {
  124. db, mock, _ := sqlmock.New()
  125. mock.ExpectQuery("SELECT").WillReturnRows(mockRows)
  126. rows, _ := db.Query("SELECT")
  127. return rows
  128. }
  129. func TestSQLRows(t *testing.T) {
  130. actual, err := sqlRows(helperRows(sqlmock.NewRows(
  131. []string{"name", "age", "dept"}).
  132. AddRow("alice", 19, "prod")))
  133. expected := []map[string]interface{}{map[string]interface{}{
  134. "name": "alice",
  135. "age": int64(19),
  136. "dept": "prod"}}
  137. assert.NoError(t, err)
  138. assert.ElementsMatch(t, expected, actual)
  139. }
  140. func TestSQLValue(t *testing.T) {
  141. tests := []struct {
  142. input interface{}
  143. output interface{}
  144. }{
  145. {"hello", "hello"},
  146. {1, 1},
  147. {false, false},
  148. {[]byte("random"), "random"},
  149. {[]byte("{\"json\":true}"), map[string]interface{}{"json": true}},
  150. {[]byte("[]"), []interface{}{}},
  151. }
  152. for _, test := range tests {
  153. assert.Equal(t, test.output, sqlValue(test.input, nil), test.input)
  154. }
  155. }
  156. func TestSQLResultFrom(t *testing.T) {
  157. res := sqlResultFrom(sqlmock.NewResult(1, 2))
  158. assert.Equal(t, sqlResult{1, 2}, res)
  159. res = sqlResultFrom(sqlmock.NewErrorResult(fmt.Errorf("")))
  160. assert.Equal(t, sqlResult{-1, -1}, res)
  161. }
  162. func helperSQLite3(t *testing.T) (context.Context, Client) {
  163. t.Helper()
  164. ctx := context.Background()
  165. url, _ := url.Parse("file::memory:?cache=shared")
  166. sqlite3, err := NewSQLClient(ctx, url)
  167. assert.NoError(t, err)
  168. return ctx, sqlite3
  169. }
  170. func TestPing(t *testing.T) {
  171. ctx, sqlite3 := helperSQLite3(t)
  172. err := sqlite3.Ping(ctx)
  173. assert.NoError(t, err)
  174. }
  175. func TestSubmit(t *testing.T) {
  176. ctx, sqlite3 := helperSQLite3(t)
  177. res, err := sqlite3.Submit(ctx, &Command{
  178. Statement: "CREATE TABLE t (a INTEGER, b FLOAT, c TEXT, d BLOB);",
  179. Mode: "exec",
  180. })
  181. assert.NoError(t, err)
  182. assert.Equal(t, sqlResult{0, 0}, res)
  183. res, err = sqlite3.Submit(ctx, &Command{
  184. Statement: "SELECT * FROM t;",
  185. Mode: "query",
  186. })
  187. assert.NoError(t, err)
  188. assert.Empty(t, res)
  189. res, err = sqlite3.Submit(ctx, &Command{
  190. Statement: "INSERT INTO t VALUES (?, ?, ?, ?);",
  191. Mode: "exec",
  192. Arguments: Arguments{
  193. Positional: []interface{}{
  194. 1,
  195. 3.14,
  196. "text",
  197. "blob",
  198. },
  199. },
  200. })
  201. assert.NoError(t, err)
  202. assert.Equal(t, sqlResult{1, 1}, res)
  203. res, err = sqlite3.Submit(ctx, &Command{
  204. Statement: "UPDATE t SET c = NULL;",
  205. Mode: "exec",
  206. })
  207. assert.NoError(t, err)
  208. assert.Equal(t, sqlResult{1, 1}, res)
  209. res, err = sqlite3.Submit(ctx, &Command{
  210. Statement: "SELECT * FROM t WHERE a = ?;",
  211. Mode: "query",
  212. Arguments: Arguments{
  213. Positional: []interface{}{1},
  214. },
  215. })
  216. assert.NoError(t, err)
  217. assert.Len(t, res, 1)
  218. resf, ok := res.([]map[string]interface{})
  219. assert.True(t, ok)
  220. assert.EqualValues(t, map[string]interface{}{
  221. "a": int64(1),
  222. "b": 3.14,
  223. "c": nil,
  224. "d": "blob",
  225. }, resf[0])
  226. res, err = sqlite3.Submit(ctx, &Command{
  227. Statement: "DROP TABLE t;",
  228. Mode: "exec",
  229. })
  230. assert.NoError(t, err)
  231. assert.Equal(t, sqlResult{1, 1}, res)
  232. }
  233. func TestSubmitTransaction(t *testing.T) {
  234. ctx, sqlite3 := helperSQLite3(t)
  235. res, err := sqlite3.Submit(ctx, &Command{
  236. Statement: "BEGIN;",
  237. Mode: "exec",
  238. })
  239. assert.Error(t, err)
  240. assert.Empty(t, res)
  241. res, err = sqlite3.Submit(ctx, &Command{
  242. Statement: "BEGIN; CREATE TABLE tt (a INT); COMMIT;",
  243. Mode: "exec",
  244. Isolation: "none",
  245. })
  246. assert.NoError(t, err)
  247. assert.Equal(t, sqlResult{0, 0}, res)
  248. rows, err := sqlite3.Submit(ctx, &Command{
  249. Statement: "SELECT * FROM tt;",
  250. Mode: "query",
  251. Isolation: "repeatable_read",
  252. })
  253. assert.NoError(t, err)
  254. assert.Empty(t, rows)
  255. }
  256. func TestSubmitTimeout(t *testing.T) {
  257. ctx, sqlite3 := helperSQLite3(t)
  258. res, err := sqlite3.Submit(ctx, &Command{
  259. Statement: "SELECT * FROM t;",
  260. Mode: "query",
  261. Timeout: 1 * time.Nanosecond,
  262. })
  263. assert.Error(t, err)
  264. assert.Empty(t, res)
  265. }
  266. func TestSubmitMode(t *testing.T) {
  267. ctx, sqlite3 := helperSQLite3(t)
  268. res, err := sqlite3.Submit(ctx, &Command{
  269. Statement: "SELECT * FROM t;",
  270. Mode: "notanoption",
  271. })
  272. assert.Error(t, err)
  273. assert.Empty(t, res)
  274. }
  275. func TestSubmitEmpty(t *testing.T) {
  276. ctx, sqlite3 := helperSQLite3(t)
  277. res, err := sqlite3.Submit(ctx, &Command{
  278. Statement: "; ; ; ;",
  279. Mode: "query",
  280. })
  281. assert.Error(t, err)
  282. assert.Empty(t, res)
  283. }