client_test.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package snowflakereceiver
  4. import (
  5. "context"
  6. "database/sql"
  7. "database/sql/driver"
  8. "reflect"
  9. "regexp"
  10. "testing"
  11. "github.com/DATA-DOG/go-sqlmock"
  12. "github.com/stretchr/testify/assert"
  13. "go.opentelemetry.io/collector/component/componenttest"
  14. "go.opentelemetry.io/collector/receiver/receivertest"
  15. )
  16. func TestDefaultClientCreation(t *testing.T) {
  17. _, err := newDefaultClient(componenttest.NewNopTelemetrySettings(), Config{
  18. Username: "testuser",
  19. Password: "testPassword",
  20. Account: "testAccount",
  21. Schema: "testSchema",
  22. Warehouse: "testWarehouse",
  23. Database: "testDatabase",
  24. Role: "testRole",
  25. })
  26. assert.Equal(t, nil, err)
  27. }
  28. // test query wrapper
  29. func TestClientReadDB(t *testing.T) {
  30. db, mock, err := sqlmock.New()
  31. if err != nil {
  32. t.Fatal("an error was not expected when opening mock db", err)
  33. }
  34. defer db.Close()
  35. q := "SELECT * FROM mocktable"
  36. rows := mock.NewRows([]string{"row1", "row2"}).AddRow(1, 3)
  37. mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM mocktable`)).WillReturnRows(rows)
  38. client := snowflakeClient{
  39. client: db,
  40. logger: receivertest.NewNopCreateSettings().Logger,
  41. }
  42. ctx := context.Background()
  43. _, err = client.readDB(ctx, q)
  44. if err != nil {
  45. t.Errorf("Error during readDB: %s", err)
  46. }
  47. if err = mock.ExpectationsWereMet(); err != nil {
  48. t.Errorf("Unfulfilled expectations: %s", err)
  49. }
  50. }
  51. func TestMetricQueries(t *testing.T) {
  52. tests := []struct {
  53. desc string
  54. query string
  55. columns []string
  56. params []driver.Value
  57. expect any
  58. }{
  59. {
  60. desc: "FetchBillingMetrics",
  61. query: billingMetricsQuery,
  62. columns: []string{"service_type", "service_name", "virtualwarehouse_credit", "cloud_service", "totalcredit"},
  63. params: []driver.Value{"t", "n", 1.0, 2.0, 3.0},
  64. expect: billingMetric{
  65. serviceType: sql.NullString{
  66. String: "t",
  67. Valid: true,
  68. },
  69. serviceName: sql.NullString{
  70. String: "n",
  71. Valid: true,
  72. },
  73. totalCloudService: 2.0,
  74. totalCredits: 3.0,
  75. totalVirtualWarehouseCredits: 1.0,
  76. },
  77. },
  78. {
  79. desc: "FetchWarehouseBillingMetrics",
  80. query: warehouseBillingMetricsQuery,
  81. columns: []string{"wh_name", "virtual_wh", "cloud_service", "credit"},
  82. params: []driver.Value{"n", 1.0, 2.0, 3.0},
  83. expect: whBillingMetric{
  84. warehouseName: sql.NullString{
  85. String: "n",
  86. Valid: true,
  87. },
  88. totalCloudService: 2.0,
  89. totalCredit: 3.0,
  90. totalVirtualWarehouse: 1.0,
  91. },
  92. },
  93. {
  94. desc: "FetchLoginMetrics",
  95. query: loginMetricsQuery,
  96. columns: []string{"username", "error_message", "client_type", "is_success", "login_total"},
  97. params: []driver.Value{"t", "n", "m", "l", 1},
  98. expect: loginMetric{
  99. userName: sql.NullString{
  100. String: "t",
  101. Valid: true,
  102. },
  103. errorMessage: sql.NullString{
  104. String: "n",
  105. Valid: true,
  106. },
  107. reportedClientType: sql.NullString{
  108. String: "m",
  109. Valid: true,
  110. },
  111. isSuccess: sql.NullString{
  112. String: "l",
  113. Valid: true,
  114. },
  115. loginsTotal: 1,
  116. },
  117. },
  118. {
  119. desc: "FetchHighLevelQueryMetrics",
  120. query: highLevelQueryMetricsQuery,
  121. columns: []string{"wh_name", "query_executed", "queue_overload", "queue_provision", "query_blocked"},
  122. params: []driver.Value{"t", 0.0, 1.0, 2.0, 3.0},
  123. expect: hlQueryMetric{
  124. warehouseName: sql.NullString{
  125. String: "t",
  126. Valid: true,
  127. },
  128. avgQueryExecuted: 0.0,
  129. avgQueryBlocked: 3.0,
  130. avgQueryQueuedOverload: 1.0,
  131. avgQueryQueuedProvision: 2.0,
  132. },
  133. },
  134. {
  135. desc: "FetchDbMetrics",
  136. query: dbMetricsQuery,
  137. columns: []string{"schemaname", "execution_status", "error_message",
  138. "query_type", "wh_name", "db_name", "wh_size", "username",
  139. "count_queryid", "queued_overload", "queued_repair", "queued_provision",
  140. "total_elapsed", "execution_time", "comp_time", "bytes_scanned",
  141. "bytes_written", "bytes_deleted", "bytes_spilled_local", "bytes_spilled_remote",
  142. "percentage_cache", "partitions_scanned", "rows_unloaded", "rows_deleted",
  143. "rows_updated", "rows_inserted", "rows_produced"},
  144. params: []driver.Value{"a", "b", "c", "d", "e", "f", "g", "h", 1, 2.0, 3.0, 4.0, 5.0, 6.0,
  145. 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0},
  146. expect: dbMetric{
  147. attributes: dbMetricAttributes{
  148. userName: sql.NullString{
  149. String: "h",
  150. Valid: true,
  151. },
  152. schemaName: sql.NullString{
  153. String: "a",
  154. Valid: true,
  155. },
  156. executionStatus: sql.NullString{
  157. String: "b",
  158. Valid: true,
  159. },
  160. errorMessage: sql.NullString{
  161. String: "c",
  162. Valid: true,
  163. },
  164. queryType: sql.NullString{
  165. String: "d",
  166. Valid: true,
  167. },
  168. warehouseName: sql.NullString{
  169. String: "e",
  170. Valid: true,
  171. },
  172. warehouseSize: sql.NullString{
  173. String: "g",
  174. Valid: true,
  175. },
  176. databaseName: sql.NullString{
  177. String: "f",
  178. Valid: true,
  179. },
  180. },
  181. databaseQueryCount: 1,
  182. avgBytesScanned: 8.0,
  183. avgBytesDeleted: 10.0,
  184. avgBytesSpilledRemote: 12.0,
  185. avgBytesSpilledLocal: 11.0,
  186. avgBytesWritten: 9.0,
  187. avgCompilationTime: 7.0,
  188. avgDataScannedCache: 13.0,
  189. avgExecutionTime: 6.0,
  190. avgPartitionsScanned: 14.0,
  191. avgQueuedOverloadTime: 2.0,
  192. avgQueuedProvisioningTime: 4.0,
  193. avgQueuedRepairTime: 3.0,
  194. avgRowsInserted: 18.0,
  195. avgRowsDeleted: 16.0,
  196. avgRowsProduced: 19.0,
  197. avgRowsUnloaded: 15.0,
  198. avgRowsUpdated: 17.0,
  199. avgTotalElapsedTime: 5.0,
  200. },
  201. },
  202. {
  203. desc: "FetchSessionMetrics",
  204. query: sessionMetricsQuery,
  205. columns: []string{"username", "disctinct_id"},
  206. params: []driver.Value{"t", 3.0},
  207. expect: sessionMetric{
  208. userName: sql.NullString{
  209. String: "t",
  210. Valid: true,
  211. },
  212. distinctSessionID: 3.0,
  213. },
  214. },
  215. {
  216. desc: "FetchSnowpipeMetrics",
  217. query: snowpipeMetricsQuery,
  218. columns: []string{"pipe_name", "credits_used", "bytes_inserted", "files_inserted"},
  219. params: []driver.Value{"t", 1.0, 2.0, 3.0},
  220. expect: snowpipeMetric{
  221. pipeName: sql.NullString{
  222. String: "t",
  223. Valid: true,
  224. },
  225. creditsUsed: 1.0,
  226. bytesInserted: 2.0,
  227. filesInserted: 3.0,
  228. },
  229. },
  230. {
  231. desc: "FetchStorageMetrics",
  232. query: storageMetricsQuery,
  233. columns: []string{"storage_bytes", "stage_bytes", "failsafe_bytes"},
  234. params: []driver.Value{1, 2, 3},
  235. expect: storageMetric{
  236. storageBytes: 1,
  237. stageBytes: 2,
  238. failsafeBytes: 3,
  239. },
  240. },
  241. }
  242. for i := range tests {
  243. test := tests[i]
  244. t.Run(test.desc, func(t *testing.T) {
  245. db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
  246. if err != nil {
  247. t.Fatal("an error was not expected when opening mock db", err)
  248. }
  249. rows := mock.NewRows(test.columns).AddRow(test.params...)
  250. mock.ExpectQuery(test.query).WillReturnRows(rows)
  251. defer db.Close()
  252. client := snowflakeClient{
  253. client: db,
  254. logger: receivertest.NewNopCreateSettings().Logger,
  255. }
  256. ctx := context.Background()
  257. // iteratively call each client method with the correct db mock
  258. clientVal := reflect.ValueOf(&client)
  259. clientObj := reflect.Indirect(clientVal)
  260. returnVal := clientObj.MethodByName(test.desc).Call([]reflect.Value{reflect.ValueOf(ctx)})
  261. // GetMetric functions return a slice of type <metricType> but since we only have one
  262. // row we can safely just compare the first elem from the reflected slice
  263. metric := returnVal[0].Type().Elem().Elem()
  264. if err, ok := returnVal[1].Interface().(error); ok && err != nil {
  265. t.Errorf("DB error %v", err)
  266. }
  267. assert.Equal(t, reflect.TypeOf(test.expect), metric)
  268. })
  269. }
  270. }