recce-nightly 1.3.0.20250507__py3-none-any.whl → 1.4.0.20250515__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of recce-nightly might be problematic. Click here for more details.

Files changed (93) hide show
  1. recce/VERSION +1 -1
  2. recce/__init__.py +22 -22
  3. recce/adapter/base.py +11 -14
  4. recce/adapter/dbt_adapter/__init__.py +355 -316
  5. recce/adapter/dbt_adapter/dbt_version.py +3 -0
  6. recce/adapter/sqlmesh_adapter.py +24 -35
  7. recce/apis/check_api.py +39 -28
  8. recce/apis/check_func.py +33 -27
  9. recce/apis/run_api.py +25 -19
  10. recce/apis/run_func.py +29 -23
  11. recce/artifact.py +44 -49
  12. recce/cli.py +484 -285
  13. recce/config.py +42 -33
  14. recce/core.py +52 -44
  15. recce/data/404.html +1 -1
  16. recce/data/_next/static/chunks/{368-7587b306577df275.js → 778-aef312bffb4c0312.js} +15 -15
  17. recce/data/_next/static/chunks/8d700b6a.ed11a130057c7a47.js +1 -0
  18. recce/data/_next/static/chunks/app/layout-c713a2829d3279e4.js +1 -0
  19. recce/data/_next/static/chunks/app/page-7086764277331fcb.js +1 -0
  20. recce/data/_next/static/chunks/{cd9f8d63-cf0d5a7b0f7a92e8.js → cd9f8d63-e020f408095ed77c.js} +3 -3
  21. recce/data/_next/static/chunks/webpack-b787cb1a4f2293de.js +1 -0
  22. recce/data/_next/static/css/88b8abc134cfd59a.css +3 -0
  23. recce/data/index.html +2 -2
  24. recce/data/index.txt +2 -2
  25. recce/diff.py +6 -12
  26. recce/event/__init__.py +74 -72
  27. recce/event/collector.py +27 -20
  28. recce/event/track.py +39 -27
  29. recce/exceptions.py +1 -1
  30. recce/git.py +7 -7
  31. recce/github.py +57 -53
  32. recce/models/__init__.py +1 -1
  33. recce/models/check.py +6 -7
  34. recce/models/run.py +1 -0
  35. recce/models/types.py +27 -27
  36. recce/pull_request.py +26 -24
  37. recce/run.py +148 -111
  38. recce/server.py +103 -89
  39. recce/state.py +209 -177
  40. recce/summary.py +168 -143
  41. recce/tasks/__init__.py +3 -3
  42. recce/tasks/core.py +11 -13
  43. recce/tasks/dataframe.py +19 -17
  44. recce/tasks/histogram.py +69 -34
  45. recce/tasks/lineage.py +2 -2
  46. recce/tasks/profile.py +147 -86
  47. recce/tasks/query.py +139 -87
  48. recce/tasks/rowcount.py +33 -30
  49. recce/tasks/schema.py +14 -14
  50. recce/tasks/top_k.py +35 -35
  51. recce/tasks/valuediff.py +216 -152
  52. recce/util/breaking.py +77 -84
  53. recce/util/cll.py +55 -51
  54. recce/util/io.py +19 -17
  55. recce/util/logger.py +1 -1
  56. recce/util/recce_cloud.py +70 -72
  57. recce/util/singleton.py +4 -4
  58. recce/yaml/__init__.py +7 -10
  59. {recce_nightly-1.3.0.20250507.dist-info → recce_nightly-1.4.0.20250515.dist-info}/METADATA +5 -2
  60. recce_nightly-1.4.0.20250515.dist-info/RECORD +143 -0
  61. {recce_nightly-1.3.0.20250507.dist-info → recce_nightly-1.4.0.20250515.dist-info}/WHEEL +1 -1
  62. tests/adapter/dbt_adapter/conftest.py +1 -0
  63. tests/adapter/dbt_adapter/dbt_test_helper.py +28 -18
  64. tests/adapter/dbt_adapter/test_dbt_adapter.py +0 -15
  65. tests/adapter/dbt_adapter/test_dbt_cll.py +39 -32
  66. tests/adapter/dbt_adapter/test_selector.py +22 -21
  67. tests/tasks/test_histogram.py +58 -66
  68. tests/tasks/test_lineage.py +36 -23
  69. tests/tasks/test_preset_checks.py +45 -31
  70. tests/tasks/test_profile.py +340 -15
  71. tests/tasks/test_query.py +40 -40
  72. tests/tasks/test_row_count.py +65 -46
  73. tests/tasks/test_schema.py +65 -42
  74. tests/tasks/test_top_k.py +22 -18
  75. tests/tasks/test_valuediff.py +43 -32
  76. tests/test_cli.py +71 -58
  77. tests/test_config.py +7 -9
  78. tests/test_core.py +5 -3
  79. tests/test_dbt.py +7 -7
  80. tests/test_pull_request.py +1 -1
  81. tests/test_server.py +19 -13
  82. tests/test_state.py +40 -27
  83. tests/test_summary.py +18 -14
  84. recce/data/_next/static/chunks/8d700b6a-f0b1f6b9e0d97ce2.js +0 -1
  85. recce/data/_next/static/chunks/app/layout-9102e22cb73f74d6.js +0 -1
  86. recce/data/_next/static/chunks/app/page-92f13c8fad9fae3d.js +0 -1
  87. recce/data/_next/static/chunks/webpack-567d72f0bc0820d5.js +0 -1
  88. recce_nightly-1.3.0.20250507.dist-info/RECORD +0 -142
  89. /recce/data/_next/static/{K5iKlCYhdcpq8Ea6ck9J_ → q0Xsc9Sd6PDuo1lshYpLu}/_buildManifest.js +0 -0
  90. /recce/data/_next/static/{K5iKlCYhdcpq8Ea6ck9J_ → q0Xsc9Sd6PDuo1lshYpLu}/_ssgManifest.js +0 -0
  91. {recce_nightly-1.3.0.20250507.dist-info → recce_nightly-1.4.0.20250515.dist-info}/entry_points.txt +0 -0
  92. {recce_nightly-1.3.0.20250507.dist-info → recce_nightly-1.4.0.20250515.dist-info}/licenses/LICENSE +0 -0
  93. {recce_nightly-1.3.0.20250507.dist-info → recce_nightly-1.4.0.20250515.dist-info}/top_level.txt +0 -0
@@ -4,47 +4,61 @@ import pytest
4
4
  def test_default_validator():
5
5
  from recce.tasks.core import CheckValidator
6
6
 
7
- CheckValidator().validate({
8
- "name": "test",
9
- "type": "row_count_diff",
10
- "params": {},
11
- })
7
+ CheckValidator().validate(
8
+ {
9
+ "name": "test",
10
+ "type": "row_count_diff",
11
+ "params": {},
12
+ }
13
+ )
12
14
 
13
15
  with pytest.raises(ValueError):
14
- CheckValidator().validate({
15
- "type": "row_count_diff",
16
- })
16
+ CheckValidator().validate(
17
+ {
18
+ "type": "row_count_diff",
19
+ }
20
+ )
17
21
 
18
22
 
19
23
  def test_query_diff_validator():
20
24
  from recce.tasks.query import QueryDiffCheckValidator
21
- QueryDiffCheckValidator().validate({
22
- "name": "test",
23
- "type": "query_diff",
24
- "params": {
25
- "sql_template": "select * from {{ model }}"
26
- },
27
- })
28
- with pytest.raises(ValueError):
29
- QueryDiffCheckValidator().validate({
25
+
26
+ QueryDiffCheckValidator().validate(
27
+ {
30
28
  "name": "test",
31
29
  "type": "query_diff",
32
- })
30
+ "params": {"sql_template": "select * from {{ model }}"},
31
+ }
32
+ )
33
+ with pytest.raises(ValueError):
34
+ QueryDiffCheckValidator().validate(
35
+ {
36
+ "name": "test",
37
+ "type": "query_diff",
38
+ }
39
+ )
33
40
 
34
41
 
35
42
  def test_lineage_diff_validator():
36
43
  from recce.tasks.lineage import LineageDiffCheckValidator
37
- LineageDiffCheckValidator().validate({
38
- "name": "test",
39
- "type": "lineage_diff",
40
- "params": {},
41
- })
42
- LineageDiffCheckValidator().validate({
43
- "name": "test",
44
- "type": "lineage_diff",
45
- "view_options": {},
46
- })
47
- with pytest.raises(ValueError):
48
- LineageDiffCheckValidator().validate({
44
+
45
+ LineageDiffCheckValidator().validate(
46
+ {
47
+ "name": "test",
49
48
  "type": "lineage_diff",
50
- })
49
+ "params": {},
50
+ }
51
+ )
52
+ LineageDiffCheckValidator().validate(
53
+ {
54
+ "name": "test",
55
+ "type": "lineage_diff",
56
+ "view_options": {},
57
+ }
58
+ )
59
+ with pytest.raises(ValueError):
60
+ LineageDiffCheckValidator().validate(
61
+ {
62
+ "type": "lineage_diff",
63
+ }
64
+ )
@@ -1,6 +1,10 @@
1
1
  import pytest
2
+ from agate import data_types
3
+ from jinja2 import Template
4
+ from sqlglot import parse_one
2
5
 
3
6
  from recce.tasks import ProfileDiffTask, ProfileTask
7
+ from recce.tasks.profile import PROFILE_COLUMN_JINJA_TEMPLATE
4
8
 
5
9
  csv_data_curr = """
6
10
  customer_id,name,age
@@ -19,7 +23,7 @@ csv_data_base = """
19
23
 
20
24
  def test_profile(dbt_test_helper):
21
25
  dbt_test_helper.create_model("customers", None, csv_data_curr)
22
- params = dict(model='customers')
26
+ params = dict(model="customers")
23
27
  task = ProfileTask(params)
24
28
  run_result = task.execute()
25
29
 
@@ -28,7 +32,7 @@ def test_profile(dbt_test_helper):
28
32
 
29
33
  def test_profile_with_selected_columns(dbt_test_helper):
30
34
  dbt_test_helper.create_model("customers", None, csv_data_curr)
31
- params = dict(model='customers', columns=['name', 'age'])
35
+ params = dict(model="customers", columns=["name", "age"])
32
36
  task = ProfileTask(params)
33
37
  run_result = task.execute()
34
38
  assert len(run_result.current.data) == 2
@@ -36,7 +40,7 @@ def test_profile_with_selected_columns(dbt_test_helper):
36
40
 
37
41
  def test_profile_diff(dbt_test_helper):
38
42
  dbt_test_helper.create_model("customers", csv_data_base, csv_data_curr)
39
- params = dict(model='customers')
43
+ params = dict(model="customers")
40
44
  task = ProfileDiffTask(params)
41
45
  run_result = task.execute()
42
46
 
@@ -46,7 +50,7 @@ def test_profile_diff(dbt_test_helper):
46
50
 
47
51
  def test_profile_diff_with_selected_columns(dbt_test_helper):
48
52
  dbt_test_helper.create_model("customers", csv_data_base, csv_data_curr)
49
- params = dict(model='customers', columns=['name', 'age'])
53
+ params = dict(model="customers", columns=["name", "age"])
50
54
  task = ProfileDiffTask(params)
51
55
  run_result = task.execute()
52
56
  assert len(run_result.current.data) == 2
@@ -57,17 +61,338 @@ def test_validator():
57
61
  from recce.tasks.profile import ProfileCheckValidator
58
62
 
59
63
  def validate(params: dict = {}, view_options: dict = {}):
60
- ProfileCheckValidator().validate({
61
- 'name': 'test',
62
- 'type': 'profile_diff',
63
- 'params': params,
64
- 'view_options': view_options,
65
- })
64
+ ProfileCheckValidator().validate(
65
+ {
66
+ "name": "test",
67
+ "type": "profile_diff",
68
+ "params": params,
69
+ "view_options": view_options,
70
+ }
71
+ )
66
72
 
67
- validate({
68
- 'model': 'customers',
69
- })
73
+ validate(
74
+ {
75
+ "model": "customers",
76
+ }
77
+ )
70
78
 
71
79
  with pytest.raises(ValueError):
72
- validate({
73
- })
80
+ validate({})
81
+
82
+
83
+ def test_profile_column_jinja_template():
84
+
85
+ class DummyAdapter:
86
+ def __init__(self, database_type="duckdb"):
87
+ self.database_type = database_type
88
+
89
+ def quote(self, col):
90
+ quote_marks = {
91
+ "athena": '"',
92
+ "bigquery": "`",
93
+ "databricks": "`",
94
+ "duckdb": '"',
95
+ "postgres": '"',
96
+ "redshift": '"',
97
+ "snowflake": '"',
98
+ "sqlite": '"',
99
+ "sqlserver": '"',
100
+ "trino": '"',
101
+ }
102
+ quote_mark = quote_marks.get(self.database_type, '"')
103
+ return f"{quote_mark}{col}{quote_mark}"
104
+
105
+ class DummyDbt:
106
+ def __init__(self, database_type="Postgres"):
107
+ self.database_type = database_type
108
+
109
+ def type_bigint(self):
110
+ bigint_types = {
111
+ "athena": "BIGINT",
112
+ "bigquery": "INT64",
113
+ "databricks": "BIGINT",
114
+ "duckdb": "BIGINT",
115
+ "postgres": "BIGINT",
116
+ "redshift": "BIGINT",
117
+ "snowflake": "NUMBER",
118
+ "sqlite": "INTEGER",
119
+ "sqlserver": "BIGINT",
120
+ "trino": "BIGINT",
121
+ }
122
+ return bigint_types.get(self.database_type, "bigint")
123
+
124
+ def type_numeric(self):
125
+ numeric_types = {
126
+ "athena": "DECIMAL",
127
+ "bigquery": "NUMERIC",
128
+ "databricks": "DECIMAL",
129
+ "duckdb": "DECIMAL",
130
+ "postgres": "NUMERIC",
131
+ "redshift": "DECIMAL",
132
+ "snowflake": "NUMBER",
133
+ "sqlite": "NUMERIC",
134
+ "sqlserver": "DECIMAL",
135
+ "trino": "DECIMAL",
136
+ }
137
+ return numeric_types.get(self.database_type, "numeric")
138
+
139
+ def type_string(self):
140
+ string_types = {
141
+ "athena": "VARCHAR",
142
+ "bigquery": "STRING",
143
+ "databricks": "STRING",
144
+ "duckdb": "VARCHAR",
145
+ "postgres": "TEXT",
146
+ "redshift": "VARCHAR",
147
+ "snowflake": "VARCHAR",
148
+ "sqlite": "TEXT",
149
+ "sqlserver": "VARCHAR",
150
+ "trino": "VARCHAR",
151
+ }
152
+ return string_types.get(self.database_type, "varchar")
153
+
154
+ test_db_column_types = {
155
+ "athena": [
156
+ "boolean",
157
+ "int",
158
+ "double",
159
+ "decimal",
160
+ "char",
161
+ "string",
162
+ "binary",
163
+ "date",
164
+ "array",
165
+ "struct",
166
+ "map",
167
+ ],
168
+ "bigquery": [
169
+ "array",
170
+ "bool",
171
+ "bytes",
172
+ "date",
173
+ "datetime",
174
+ "interval",
175
+ "json",
176
+ "int64",
177
+ "bignumeric",
178
+ "range",
179
+ "string",
180
+ "struct",
181
+ "time",
182
+ "timestamp",
183
+ ],
184
+ "databricks": [
185
+ "bigint",
186
+ "binary",
187
+ "boolean",
188
+ "date",
189
+ "decimal",
190
+ "double",
191
+ "float",
192
+ "int",
193
+ "interval",
194
+ "void",
195
+ "string",
196
+ "timestamp",
197
+ "array",
198
+ "map",
199
+ "struct",
200
+ "variant",
201
+ "object",
202
+ ],
203
+ "duckdb": [
204
+ "boolean",
205
+ "integer",
206
+ "bigint",
207
+ "real",
208
+ "double",
209
+ "decimal",
210
+ "varchar",
211
+ "date",
212
+ "time",
213
+ "timestamp",
214
+ "blob",
215
+ "array",
216
+ "list",
217
+ "struct",
218
+ "map",
219
+ "union",
220
+ "uuid",
221
+ "json",
222
+ ],
223
+ "postgres": [
224
+ "bigint",
225
+ "bigserial",
226
+ "bit",
227
+ "boolean",
228
+ "box",
229
+ "bytea",
230
+ "character",
231
+ "cidr",
232
+ "circle",
233
+ "date",
234
+ "double precision",
235
+ "inet",
236
+ "integer",
237
+ "interval",
238
+ "json",
239
+ "jsonb",
240
+ "line",
241
+ "lseg",
242
+ "macaddr",
243
+ "money",
244
+ "numeric",
245
+ "path",
246
+ "pg_lsn",
247
+ "point",
248
+ "polygon",
249
+ "real",
250
+ "smallint",
251
+ "smallserial",
252
+ "serial",
253
+ "text",
254
+ "time",
255
+ "timestamp",
256
+ "tsquery",
257
+ "tsvector",
258
+ "txid_snapshot",
259
+ "uuid",
260
+ "xml",
261
+ "array",
262
+ ],
263
+ "redshift": [
264
+ "smallint",
265
+ "integer",
266
+ "bigint",
267
+ "decimal",
268
+ "real",
269
+ "double precision",
270
+ "boolean",
271
+ "char",
272
+ "varchar",
273
+ "date",
274
+ "timestamp",
275
+ "timestamptz",
276
+ "super",
277
+ "time",
278
+ "timetz",
279
+ "varbyte",
280
+ "geometry",
281
+ "geography",
282
+ "hllsketch",
283
+ ],
284
+ "snowflake": [
285
+ "number",
286
+ "decimal",
287
+ "numeric",
288
+ "int",
289
+ "integer",
290
+ "bigint",
291
+ "smallint",
292
+ "tinyint",
293
+ "byteint",
294
+ "float",
295
+ "float4",
296
+ "float8",
297
+ "double",
298
+ "double precision",
299
+ "real",
300
+ "varchar",
301
+ "char",
302
+ "character",
303
+ "string",
304
+ "text",
305
+ "binary",
306
+ "varbinary",
307
+ "boolean",
308
+ "date",
309
+ "datetime",
310
+ "time",
311
+ "timestamp",
312
+ "timestamp_ltz",
313
+ "timestamp_ntz",
314
+ "timestamp_tz",
315
+ "variant",
316
+ "object",
317
+ "array",
318
+ "geography",
319
+ "geometry",
320
+ ],
321
+ "sqlite": ["integer", "real", "text", "blob", "numeric", "boolean", "date", "datetime", "json"],
322
+ "trino": [
323
+ "boolean",
324
+ "tinyint",
325
+ "smallint",
326
+ "integer",
327
+ "bigint",
328
+ "real",
329
+ "double",
330
+ "decimal",
331
+ "varchar",
332
+ "char",
333
+ "varbinary",
334
+ "json",
335
+ "date",
336
+ "time",
337
+ "timestamp",
338
+ "timestamp with time zone",
339
+ "interval year to month",
340
+ "interval day to second",
341
+ "array",
342
+ "map",
343
+ "row",
344
+ "ipaddress",
345
+ "uuid",
346
+ "hyperloglog",
347
+ "p4hyperloglog",
348
+ "qdigest",
349
+ ],
350
+ "sqlserver": [
351
+ "bigint",
352
+ "int",
353
+ "smallint",
354
+ "tinyint",
355
+ "bit",
356
+ "decimal",
357
+ "numeric",
358
+ "money",
359
+ "smallmoney",
360
+ "float",
361
+ "real",
362
+ "date",
363
+ "datetime",
364
+ "datetime2",
365
+ "smalldatetime",
366
+ "time",
367
+ "char",
368
+ "varchar",
369
+ "text",
370
+ "nchar",
371
+ "nvarchar",
372
+ "ntext",
373
+ "binary",
374
+ "varbinary",
375
+ "image",
376
+ "timestamp",
377
+ "uniqueidentifier",
378
+ "xml",
379
+ "cursor",
380
+ "table",
381
+ "sql_variant",
382
+ ],
383
+ }
384
+
385
+ for db_type, column_types in test_db_column_types.items():
386
+ for column_type in column_types:
387
+ context = {
388
+ "column_type": column_type,
389
+ "column_name": "profile_column",
390
+ "db_type": db_type,
391
+ "relation": "test_table",
392
+ "adapter": DummyAdapter(),
393
+ "dbt": DummyDbt(),
394
+ }
395
+
396
+ sql = Template(PROFILE_COLUMN_JINJA_TEMPLATE).render(context)
397
+ dialect = db_type if db_type != "sqlserver" else "tsql"
398
+ parse_one(sql, read=dialect)
tests/tasks/test_query.py CHANGED
@@ -73,7 +73,7 @@ def test_query_diff_in_warehouse(dbt_test_helper):
73
73
  """
74
74
 
75
75
  dbt_test_helper.create_model("customers", csv_data_base, csv_data_curr)
76
- params = dict(sql_template=f'select * from {{{{ ref("customers") }}}}', primary_keys=['customer_id'])
76
+ params = dict(sql_template=f'select * from {{{{ ref("customers") }}}}', primary_keys=["customer_id"])
77
77
  task = QueryDiffTask(params)
78
78
  run_result = task.execute()
79
79
  assert len(run_result.diff.data) == 2
@@ -81,7 +81,7 @@ def test_query_diff_in_warehouse(dbt_test_helper):
81
81
  params = dict(
82
82
  base_sql_template=f'select * from {{{{ ref("customers") }}}} where customer_id == 1',
83
83
  sql_template=f'select * from {{{{ ref("customers") }}}}',
84
- primary_keys=['customer_id'],
84
+ primary_keys=["customer_id"],
85
85
  )
86
86
  task = QueryDiffTask(params)
87
87
  run_result = task.execute()
@@ -92,50 +92,50 @@ def test_validator():
92
92
  from recce.tasks.query import QueryCheckValidator, QueryDiffCheckValidator
93
93
 
94
94
  def validate(params: dict = {}, view_options: dict = {}):
95
- QueryCheckValidator().validate({
96
- 'name': 'test',
97
- 'type': 'query',
98
- 'params': params,
99
- 'view_options': view_options,
100
- })
95
+ QueryCheckValidator().validate(
96
+ {
97
+ "name": "test",
98
+ "type": "query",
99
+ "params": params,
100
+ "view_options": view_options,
101
+ }
102
+ )
101
103
 
102
104
  def validate_diff(params: dict = {}, view_options: dict = {}):
103
- QueryDiffCheckValidator().validate({
104
- 'name': 'test',
105
- 'type': 'query_diff',
106
- 'params': params,
107
- 'view_options': view_options,
108
- })
105
+ QueryDiffCheckValidator().validate(
106
+ {
107
+ "name": "test",
108
+ "type": "query_diff",
109
+ "params": params,
110
+ "view_options": view_options,
111
+ }
112
+ )
109
113
 
110
114
  # query
111
- validate({
112
- 'sql_template': 'select * from abc'
113
- })
115
+ validate({"sql_template": "select * from abc"})
114
116
 
115
117
  # diff in client
116
- validate_diff({
117
- 'sql_template': 'select * from abc'
118
- })
119
- validate_diff({
120
- 'sql_template': 'select * from abc',
121
- 'base_sql_template': 'select * from abc',
122
- })
118
+ validate_diff({"sql_template": "select * from abc"})
119
+ validate_diff(
120
+ {
121
+ "sql_template": "select * from abc",
122
+ "base_sql_template": "select * from abc",
123
+ }
124
+ )
123
125
 
124
126
  # diff in warehouse
125
- validate_diff({
126
- 'primary_keys': ['customer_id'],
127
- 'sql_template': 'select * from abc',
128
- })
129
- validate_diff({
130
- 'sql_template': 'select * from abc',
131
- 'base_sql_template': 'select * from abc',
132
- 'primary_keys': ['customer_id']
133
- })
134
- validate_diff({
135
- 'sql_template': 'select * from abc',
136
- 'base_sql_template': 'select * from abc',
137
- 'primary_keys': ['customer_id']
138
- })
127
+ validate_diff(
128
+ {
129
+ "primary_keys": ["customer_id"],
130
+ "sql_template": "select * from abc",
131
+ }
132
+ )
133
+ validate_diff(
134
+ {"sql_template": "select * from abc", "base_sql_template": "select * from abc", "primary_keys": ["customer_id"]}
135
+ )
136
+ validate_diff(
137
+ {"sql_template": "select * from abc", "base_sql_template": "select * from abc", "primary_keys": ["customer_id"]}
138
+ )
139
139
 
140
140
  # invalid
141
141
  with pytest.raises(ValueError):
@@ -145,7 +145,7 @@ def test_validator():
145
145
  validate_diff()
146
146
 
147
147
  with pytest.raises(ValueError):
148
- validate_diff({'sql_template': 123, 'primary_keys': 'xyz'})
148
+ validate_diff({"sql_template": 123, "primary_keys": "xyz"})
149
149
 
150
150
  with pytest.raises(ValueError):
151
- validate_diff({'sql_template': 's', 'primary_keys': 'xyz'})
151
+ validate_diff({"sql_template": "s", "primary_keys": "xyz"})