sql_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. package dbconnect_tests
  2. import (
  3. "context"
  4. "log"
  5. "net/url"
  6. "os"
  7. "testing"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/cloudflare/cloudflared/dbconnect"
  10. )
  11. func TestIntegrationPostgreSQL(t *testing.T) {
  12. ctx, pq := helperNewSQLClient(t, "POSTGRESQL_URL")
  13. err := pq.Ping(ctx)
  14. assert.NoError(t, err)
  15. _, err = pq.Submit(ctx, &dbconnect.Command{
  16. Statement: "CREATE TABLE t (a TEXT, b UUID, c JSON, d INET[], e SERIAL);",
  17. Mode: "exec",
  18. })
  19. assert.NoError(t, err)
  20. _, err = pq.Submit(ctx, &dbconnect.Command{
  21. Statement: "INSERT INTO t VALUES ($1, $2, $3, $4);",
  22. Mode: "exec",
  23. Arguments: dbconnect.Arguments{
  24. Positional: []interface{}{
  25. "text",
  26. "6b8d686d-bd8e-43bc-b09a-cfcbbe702c10",
  27. "{\"bool\":true,\"array\":[\"a\", 1, 3.14],\"embed\":{\"num\":21}}",
  28. []string{"1.1.1.1", "1.0.0.1"},
  29. },
  30. },
  31. })
  32. assert.NoError(t, err)
  33. _, err = pq.Submit(ctx, &dbconnect.Command{
  34. Statement: "UPDATE t SET b = NULL;",
  35. Mode: "exec",
  36. })
  37. assert.NoError(t, err)
  38. res, err := pq.Submit(ctx, &dbconnect.Command{
  39. Statement: "SELECT * FROM t;",
  40. Mode: "query",
  41. })
  42. assert.NoError(t, err)
  43. assert.IsType(t, make([]map[string]interface{}, 0), res)
  44. actual := res.([]map[string]interface{})[0]
  45. expected := map[string]interface{}{
  46. "a": "text",
  47. "b": nil,
  48. "c": map[string]interface{}{
  49. "bool": true,
  50. "array": []interface{}{"a", float64(1), 3.14},
  51. "embed": map[string]interface{}{"num": float64(21)},
  52. },
  53. "d": "{1.1.1.1,1.0.0.1}",
  54. "e": int64(1),
  55. }
  56. assert.EqualValues(t, expected, actual)
  57. _, err = pq.Submit(ctx, &dbconnect.Command{
  58. Statement: "DROP TABLE t;",
  59. Mode: "exec",
  60. })
  61. assert.NoError(t, err)
  62. }
  63. func TestIntegrationMySQL(t *testing.T) {
  64. ctx, my := helperNewSQLClient(t, "MYSQL_URL")
  65. err := my.Ping(ctx)
  66. assert.NoError(t, err)
  67. _, err = my.Submit(ctx, &dbconnect.Command{
  68. Statement: "CREATE TABLE t (a CHAR, b TINYINT, c FLOAT, d JSON, e YEAR);",
  69. Mode: "exec",
  70. })
  71. assert.NoError(t, err)
  72. _, err = my.Submit(ctx, &dbconnect.Command{
  73. Statement: "INSERT INTO t VALUES (?, ?, ?, ?, ?);",
  74. Mode: "exec",
  75. Arguments: dbconnect.Arguments{
  76. Positional: []interface{}{
  77. "a",
  78. 10,
  79. 3.14,
  80. "{\"bool\":true}",
  81. 2000,
  82. },
  83. },
  84. })
  85. assert.NoError(t, err)
  86. _, err = my.Submit(ctx, &dbconnect.Command{
  87. Statement: "ALTER TABLE t ADD COLUMN f GEOMETRY;",
  88. Mode: "exec",
  89. })
  90. assert.NoError(t, err)
  91. res, err := my.Submit(ctx, &dbconnect.Command{
  92. Statement: "SELECT * FROM t;",
  93. Mode: "query",
  94. })
  95. assert.NoError(t, err)
  96. assert.IsType(t, make([]map[string]interface{}, 0), res)
  97. actual := res.([]map[string]interface{})[0]
  98. expected := map[string]interface{}{
  99. "a": "a",
  100. "b": float64(10),
  101. "c": 3.14,
  102. "d": map[string]interface{}{"bool": true},
  103. "e": float64(2000),
  104. "f": nil,
  105. }
  106. assert.EqualValues(t, expected, actual)
  107. _, err = my.Submit(ctx, &dbconnect.Command{
  108. Statement: "DROP TABLE t;",
  109. Mode: "exec",
  110. })
  111. assert.NoError(t, err)
  112. }
  113. func TestIntegrationMSSQL(t *testing.T) {
  114. ctx, ms := helperNewSQLClient(t, "MSSQL_URL")
  115. err := ms.Ping(ctx)
  116. assert.NoError(t, err)
  117. _, err = ms.Submit(ctx, &dbconnect.Command{
  118. Statement: "CREATE TABLE t (a BIT, b DECIMAL, c MONEY, d TEXT);",
  119. Mode: "exec"})
  120. assert.NoError(t, err)
  121. _, err = ms.Submit(ctx, &dbconnect.Command{
  122. Statement: "INSERT INTO t VALUES (?, ?, ?, ?);",
  123. Mode: "exec",
  124. Arguments: dbconnect.Arguments{
  125. Positional: []interface{}{
  126. 0,
  127. 3,
  128. "$0.99",
  129. "text",
  130. },
  131. },
  132. })
  133. assert.NoError(t, err)
  134. _, err = ms.Submit(ctx, &dbconnect.Command{
  135. Statement: "UPDATE t SET d = NULL;",
  136. Mode: "exec",
  137. })
  138. assert.NoError(t, err)
  139. res, err := ms.Submit(ctx, &dbconnect.Command{
  140. Statement: "SELECT * FROM t;",
  141. Mode: "query",
  142. })
  143. assert.NoError(t, err)
  144. assert.IsType(t, make([]map[string]interface{}, 0), res)
  145. actual := res.([]map[string]interface{})[0]
  146. expected := map[string]interface{}{
  147. "a": false,
  148. "b": float64(3),
  149. "c": float64(0.99),
  150. "d": nil,
  151. }
  152. assert.EqualValues(t, expected, actual)
  153. _, err = ms.Submit(ctx, &dbconnect.Command{
  154. Statement: "DROP TABLE t;",
  155. Mode: "exec",
  156. })
  157. assert.NoError(t, err)
  158. }
  159. func TestIntegrationClickhouse(t *testing.T) {
  160. ctx, ch := helperNewSQLClient(t, "CLICKHOUSE_URL")
  161. err := ch.Ping(ctx)
  162. assert.NoError(t, err)
  163. _, err = ch.Submit(ctx, &dbconnect.Command{
  164. Statement: "CREATE TABLE t (a UUID, b String, c Float64, d UInt32, e Int16, f Array(Enum8('a'=1, 'b'=2, 'c'=3))) engine=Memory;",
  165. Mode: "exec",
  166. })
  167. assert.NoError(t, err)
  168. _, err = ch.Submit(ctx, &dbconnect.Command{
  169. Statement: "INSERT INTO t VALUES (?, ?, ?, ?, ?, ?);",
  170. Mode: "exec",
  171. Arguments: dbconnect.Arguments{
  172. Positional: []interface{}{
  173. "ec65f626-6f50-4c86-9628-6314ef1edacd",
  174. "",
  175. 3.14,
  176. 314,
  177. -144,
  178. []string{"a", "b", "c"},
  179. },
  180. },
  181. })
  182. assert.NoError(t, err)
  183. res, err := ch.Submit(ctx, &dbconnect.Command{
  184. Statement: "SELECT * FROM t;",
  185. Mode: "query",
  186. })
  187. assert.NoError(t, err)
  188. assert.IsType(t, make([]map[string]interface{}, 0), res)
  189. actual := res.([]map[string]interface{})[0]
  190. expected := map[string]interface{}{
  191. "a": "ec65f626-6f50-4c86-9628-6314ef1edacd",
  192. "b": "",
  193. "c": float64(3.14),
  194. "d": uint32(314),
  195. "e": int16(-144),
  196. "f": []string{"a", "b", "c"},
  197. }
  198. assert.EqualValues(t, expected, actual)
  199. _, err = ch.Submit(ctx, &dbconnect.Command{
  200. Statement: "DROP TABLE t;",
  201. Mode: "exec",
  202. })
  203. assert.NoError(t, err)
  204. }
  205. func helperNewSQLClient(t *testing.T, env string) (context.Context, dbconnect.Client) {
  206. _, ok := os.LookupEnv("DBCONNECT_INTEGRATION_TEST")
  207. if ok {
  208. t.Helper()
  209. } else {
  210. t.SkipNow()
  211. }
  212. val, ok := os.LookupEnv(env)
  213. if !ok {
  214. log.Fatalf("must provide database url as environment variable: %s", env)
  215. }
  216. parsed, err := url.Parse(val)
  217. if err != nil {
  218. log.Fatalf("cannot provide invalid database url: %s=%s", env, val)
  219. }
  220. ctx := context.Background()
  221. client, err := dbconnect.NewSQLClient(ctx, parsed)
  222. if err != nil {
  223. log.Fatalf("could not start test client: %s", err)
  224. }
  225. return ctx, client
  226. }