recce-nightly 1.10.0.20250625__py3-none-any.whl → 1.10.0.20250629__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.
- recce/VERSION +1 -1
- recce/adapter/dbt_adapter/__init__.py +231 -195
- recce/data/404.html +2 -2
- recce/data/_next/static/chunks/{42-cd3c06533f5fd47c.js → 41-f30276c289169376.js} +1 -1
- recce/data/_next/static/chunks/92-68460b15fe448f33.js +1 -0
- recce/data/_next/static/chunks/app/{layout-177a410a97e0d018.js → layout-292f035bb0d2a98e.js} +1 -1
- recce/data/_next/static/chunks/app/page-598f8acc82179d01.js +1 -0
- recce/data/_next/static/css/{1b121dc4d36aeb4d.css → a2b12b4ba4227f0a.css} +1 -1
- recce/data/index.html +2 -2
- recce/data/index.txt +3 -3
- recce/server.py +6 -5
- recce/util/lineage.py +5 -0
- {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.10.0.20250629.dist-info}/METADATA +1 -1
- {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.10.0.20250629.dist-info}/RECORD +22 -22
- tests/adapter/dbt_adapter/test_dbt_cll.py +361 -79
- tests/test_core.py +1 -3
- recce/data/_next/static/chunks/92-607cd1af83c41f43.js +0 -1
- recce/data/_next/static/chunks/app/page-da6e046a8235dbfc.js +0 -1
- /recce/data/_next/static/{abCX3x3UoIdRLEDWxx4xd → Mrb9CZ3toH6Q8xrzNzCrg}/_buildManifest.js +0 -0
- /recce/data/_next/static/{abCX3x3UoIdRLEDWxx4xd → Mrb9CZ3toH6Q8xrzNzCrg}/_ssgManifest.js +0 -0
- {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.10.0.20250629.dist-info}/WHEEL +0 -0
- {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.10.0.20250629.dist-info}/entry_points.txt +0 -0
- {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.10.0.20250629.dist-info}/licenses/LICENSE +0 -0
- {recce_nightly-1.10.0.20250625.dist-info → recce_nightly-1.10.0.20250629.dist-info}/top_level.txt +0 -0
|
@@ -1,34 +1,75 @@
|
|
|
1
1
|
from recce.adapter.dbt_adapter import DbtAdapter
|
|
2
2
|
from recce.models.types import CllData
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
from recce.util.lineage import build_column_key
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def assert_parent_map(result: CllData, node_or_column_id, parents):
|
|
7
|
+
a_parents = result.parent_map.get(node_or_column_id) or set()
|
|
8
|
+
|
|
9
|
+
assert len(a_parents) == len(parents), "parents length mismatch"
|
|
10
|
+
for parent in parents:
|
|
11
|
+
if isinstance(parent, str):
|
|
12
|
+
node_id = parent
|
|
13
|
+
assert node_id in a_parents, f"Node {node_id} not found in parent list"
|
|
14
|
+
elif len(parent) == 1:
|
|
15
|
+
(node_id,) = parent
|
|
16
|
+
assert node_id in a_parents, f"Column {parent} not found in parent list"
|
|
17
|
+
elif len(parent) == 2:
|
|
18
|
+
node, column = parent
|
|
19
|
+
column_id = build_column_key(node, column)
|
|
20
|
+
assert column_id in a_parents, f"Column {column_id} not found in parent list for {node_or_column_id}"
|
|
21
|
+
else:
|
|
22
|
+
raise ValueError(f"Invalid parent format: {parent}. Expected node_id or (node_id, column_name).")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def assert_column(
|
|
26
|
+
result: CllData,
|
|
27
|
+
node_id,
|
|
28
|
+
column_name,
|
|
29
|
+
transformation_type=None,
|
|
30
|
+
change_status=None,
|
|
31
|
+
parents=None,
|
|
32
|
+
):
|
|
33
|
+
column_id = build_column_key(node_id, column_name)
|
|
7
34
|
entry = result.columns.get(column_id)
|
|
8
35
|
assert entry is not None, f"Column {column_id} not found in result"
|
|
9
36
|
assert (
|
|
10
37
|
entry.transformation_type == transformation_type
|
|
11
38
|
), f"Column {column_name} type mismatch: expected {transformation_type}, got {entry.transformation_type}"
|
|
12
|
-
|
|
39
|
+
assert_parent_map(result, column_id, parents)
|
|
40
|
+
assert (
|
|
41
|
+
entry.change_status == change_status
|
|
42
|
+
), f"Column {column_name} change status mismatch: expected {change_status}, got {entry.change_status}"
|
|
13
43
|
|
|
14
|
-
assert len(parents) == len(depends_on), "depends_on length mismatch"
|
|
15
|
-
for i in range(len(depends_on)):
|
|
16
|
-
node, column = depends_on[i]
|
|
17
|
-
parent_column_id = f"{node}_{column}"
|
|
18
44
|
|
|
19
|
-
|
|
45
|
+
def assert_model(
|
|
46
|
+
result: CllData,
|
|
47
|
+
node_id,
|
|
48
|
+
change_category=None,
|
|
49
|
+
parents=None,
|
|
50
|
+
):
|
|
51
|
+
entry = result.nodes.get(node_id)
|
|
52
|
+
assert entry is not None, f"Node {node_id} not found in result"
|
|
53
|
+
assert_parent_map(result, node_id, parents)
|
|
20
54
|
|
|
55
|
+
assert (
|
|
56
|
+
entry.change_category == change_category
|
|
57
|
+
), f"Node {node_id} change category mismatch: expected {change_category}, got {entry.change_category}"
|
|
21
58
|
|
|
22
|
-
def assert_model(result: CllData, node_name, depends_on):
|
|
23
|
-
assert result.nodes.get(node_name) is not None, f"Node {node_name} not found in result"
|
|
24
|
-
parent_map = result.parent_map.get(node_name)
|
|
25
|
-
assert parent_map is not None, f"Parent map {node_name} not found in result"
|
|
26
|
-
# assert len(parent_map) == len(depends_on), "depends_on length mismatch"
|
|
27
|
-
for i in range(len(depends_on)):
|
|
28
|
-
node, column = depends_on[i]
|
|
29
|
-
column_id = f"{node}_{column}"
|
|
30
59
|
|
|
31
|
-
|
|
60
|
+
def assert_cll_contain_nodes(cll_data: CllData, nodes):
|
|
61
|
+
assert len(nodes) == len(cll_data.nodes), "Model count mismatch"
|
|
62
|
+
for node in nodes:
|
|
63
|
+
assert node in cll_data.nodes, f"Model {node} not found in lineage"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def assert_cll_contain_columns(cll_data: CllData, columns):
|
|
67
|
+
assert len(columns) == len(cll_data.columns), "Column count mismatch"
|
|
68
|
+
for column in columns:
|
|
69
|
+
column_key = f"{column[0]}_{column[1]}"
|
|
70
|
+
assert column_key in cll_data.columns, f"Column {column} not found in lineage"
|
|
71
|
+
assert column[0] == cll_data.columns[column_key].table_id, f"Column {column[0]} node mismatch"
|
|
72
|
+
assert column[1] == cll_data.columns[column_key].name, f"Column {column[1]} name mismatch"
|
|
32
73
|
|
|
33
74
|
|
|
34
75
|
def test_cll_basic(dbt_test_helper):
|
|
@@ -51,13 +92,12 @@ def test_cll_basic(dbt_test_helper):
|
|
|
51
92
|
)
|
|
52
93
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
53
94
|
|
|
54
|
-
result = adapter.get_cll("model.
|
|
55
|
-
assert_model(result, "model.model2", [("model.model1", "c")])
|
|
56
|
-
assert_column(result, "model.model2", "c", "passthrough", [("model.model1", "c")])
|
|
95
|
+
result = adapter.get_cll("model.model1", "c")
|
|
96
|
+
assert_model(result, "model.model2", parents=[("model.model1", "c")])
|
|
97
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
57
98
|
|
|
58
|
-
result =
|
|
59
|
-
|
|
60
|
-
assert_column(result, "model.model3", "c", "passthrough", [("model.model1", "c")])
|
|
99
|
+
assert_model(result, "model.model3", parents=[("model.model1", "c")])
|
|
100
|
+
assert_column(result, "model.model3", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
61
101
|
|
|
62
102
|
|
|
63
103
|
def test_cll_table_alisa(dbt_test_helper):
|
|
@@ -75,8 +115,8 @@ def test_cll_table_alisa(dbt_test_helper):
|
|
|
75
115
|
depends_on=["model.model1"],
|
|
76
116
|
)
|
|
77
117
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
78
|
-
result = adapter.get_cll("model.model1",
|
|
79
|
-
assert_column(result, "model.model2", "c", "passthrough", [("model.model1", "c")])
|
|
118
|
+
result = adapter.get_cll("model.model1", "c")
|
|
119
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
80
120
|
|
|
81
121
|
|
|
82
122
|
def test_seed(dbt_test_helper):
|
|
@@ -103,11 +143,17 @@ def test_seed(dbt_test_helper):
|
|
|
103
143
|
)
|
|
104
144
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
105
145
|
|
|
106
|
-
result = adapter.get_cll("model.model1"
|
|
107
|
-
assert_model(result, "seed.seed1", [])
|
|
108
|
-
assert_column(result, "seed.seed1", "customer_id", "source", [])
|
|
109
|
-
assert_model(result, "model.model1", [("seed.seed1", "age")])
|
|
110
|
-
assert_column(
|
|
146
|
+
result = adapter.get_cll("model.model1")
|
|
147
|
+
assert_model(result, "seed.seed1", parents=[])
|
|
148
|
+
assert_column(result, "seed.seed1", "customer_id", transformation_type="source", parents=[])
|
|
149
|
+
assert_model(result, "model.model1", parents=["seed.seed1", ("seed.seed1", "age")])
|
|
150
|
+
assert_column(
|
|
151
|
+
result,
|
|
152
|
+
"model.model1",
|
|
153
|
+
"customer_id",
|
|
154
|
+
transformation_type="passthrough",
|
|
155
|
+
parents=[("seed.seed1", "customer_id")],
|
|
156
|
+
)
|
|
111
157
|
|
|
112
158
|
|
|
113
159
|
def test_python_model(dbt_test_helper):
|
|
@@ -134,8 +180,9 @@ def test_python_model(dbt_test_helper):
|
|
|
134
180
|
assert not adapter.is_python_model("model1")
|
|
135
181
|
assert adapter.is_python_model("model2")
|
|
136
182
|
|
|
137
|
-
result = adapter.get_cll("
|
|
138
|
-
|
|
183
|
+
result = adapter.get_cll("model2")
|
|
184
|
+
assert_model(result, "model2", parents=["model1"])
|
|
185
|
+
assert_column(result, "model2", "customer_id", transformation_type="unknown", parents=[])
|
|
139
186
|
|
|
140
187
|
|
|
141
188
|
def test_source(dbt_test_helper):
|
|
@@ -161,43 +208,33 @@ def test_source(dbt_test_helper):
|
|
|
161
208
|
depends_on=["source.source1.table1"],
|
|
162
209
|
)
|
|
163
210
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
164
|
-
result = adapter.get_cll("
|
|
165
|
-
assert_column(result, "source.source1.table1", "customer_id", "source", [])
|
|
166
|
-
|
|
167
|
-
|
|
211
|
+
result = adapter.get_cll("model.model1")
|
|
212
|
+
assert_column(result, "source.source1.table1", "customer_id", transformation_type="source", parents=[])
|
|
213
|
+
assert_column(
|
|
214
|
+
result,
|
|
215
|
+
"model.model1",
|
|
216
|
+
"customer_id",
|
|
217
|
+
transformation_type="passthrough",
|
|
218
|
+
parents=[("source.source1.table1", "customer_id")],
|
|
219
|
+
)
|
|
168
220
|
|
|
169
221
|
|
|
170
222
|
def test_parse_error(dbt_test_helper):
|
|
171
223
|
dbt_test_helper.create_model("model1", curr_sql="select 1 as c", curr_columns={"c": "int"})
|
|
172
224
|
dbt_test_helper.create_model("model2", curr_sql="this is not a valid sql", curr_columns={"c": "int"})
|
|
173
225
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
174
|
-
result = adapter.get_cll("model2"
|
|
175
|
-
assert_column(result, "model2", "c", "unknown", [])
|
|
226
|
+
result = adapter.get_cll("model2")
|
|
227
|
+
assert_column(result, "model2", "c", transformation_type="unknown", parents=[])
|
|
176
228
|
|
|
177
229
|
|
|
178
230
|
def test_model_without_catalog(dbt_test_helper):
|
|
179
231
|
dbt_test_helper.create_model("model1", curr_sql="select 1 as c")
|
|
180
232
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
181
|
-
result = adapter.get_cll("model1"
|
|
233
|
+
result = adapter.get_cll("model1")
|
|
182
234
|
assert not result.nodes["model1"].columns
|
|
183
235
|
|
|
184
236
|
|
|
185
|
-
def
|
|
186
|
-
assert len(nodes) == len(cll_data.nodes), "Model count mismatch"
|
|
187
|
-
for node in nodes:
|
|
188
|
-
assert node in cll_data.nodes, f"Model {node} not found in lineage"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def assert_lineage_column(cll_data: CllData, columns):
|
|
192
|
-
assert len(columns) == len(cll_data.columns), "Column count mismatch"
|
|
193
|
-
for column in columns:
|
|
194
|
-
column_key = f"{column[0]}_{column[1]}"
|
|
195
|
-
assert column_key in cll_data.columns, f"Column {column} not found in lineage"
|
|
196
|
-
assert column[0] == cll_data.columns[column_key].table_id, f"Column {column[0]} node mismatch"
|
|
197
|
-
assert column[1] == cll_data.columns[column_key].name, f"Column {column[1]} name mismatch"
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def test_cll_column_filter(dbt_test_helper):
|
|
237
|
+
def test_column_level_lineage(dbt_test_helper):
|
|
201
238
|
dbt_test_helper.create_model(
|
|
202
239
|
"model1", unique_id="model.model1", curr_sql="select 1 as c", curr_columns={"c": "int"}
|
|
203
240
|
)
|
|
@@ -226,19 +263,256 @@ def test_cll_column_filter(dbt_test_helper):
|
|
|
226
263
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
227
264
|
|
|
228
265
|
result = adapter.get_cll("model.model2", "c")
|
|
229
|
-
|
|
230
|
-
|
|
266
|
+
assert_cll_contain_nodes(result, [])
|
|
267
|
+
assert_cll_contain_columns(result, [("model.model1", "c"), ("model.model2", "c"), ("model.model3", "c")])
|
|
268
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
231
269
|
|
|
232
270
|
result = adapter.get_cll("model.model2", "y")
|
|
233
|
-
|
|
234
|
-
|
|
271
|
+
assert_cll_contain_nodes(result, ["model.model3"])
|
|
272
|
+
assert_cll_contain_columns(result, [("model.model2", "y"), ("model.model4", "y")])
|
|
273
|
+
assert_column(result, "model.model2", "y", transformation_type="source", parents=[])
|
|
235
274
|
|
|
236
275
|
result = adapter.get_cll("model.model3", "c")
|
|
237
|
-
|
|
238
|
-
|
|
276
|
+
assert_cll_contain_nodes(result, [])
|
|
277
|
+
assert_cll_contain_columns(result, [("model.model1", "c"), ("model.model2", "c"), ("model.model3", "c")])
|
|
278
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[("model.model1", "c")])
|
|
279
|
+
|
|
280
|
+
result = adapter.get_cll("model.model2", "c", no_upstream=True, no_downstream=True)
|
|
281
|
+
assert_cll_contain_nodes(result, [])
|
|
282
|
+
assert_cll_contain_columns(result, [("model.model2", "c")])
|
|
283
|
+
assert_column(result, "model.model2", "c", transformation_type="passthrough", parents=[])
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def test_impact_radius_no_change_analysis_no_cll(dbt_test_helper):
|
|
287
|
+
dbt_test_helper.create_model(
|
|
288
|
+
"model1",
|
|
289
|
+
unique_id="model.model1",
|
|
290
|
+
curr_sql="select 1 as c",
|
|
291
|
+
base_sql="select 1 as c --- non-breaking",
|
|
292
|
+
curr_columns={"c": "int"},
|
|
293
|
+
base_columns={"c": "int"},
|
|
294
|
+
)
|
|
295
|
+
dbt_test_helper.create_model(
|
|
296
|
+
"model2",
|
|
297
|
+
unique_id="model.model2",
|
|
298
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
299
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }} where c > 0 --- breaking',
|
|
300
|
+
curr_columns={"c": "int", "y": "int"},
|
|
301
|
+
base_columns={"c": "int", "y": "int"},
|
|
302
|
+
depends_on=["model.model1"],
|
|
303
|
+
)
|
|
304
|
+
dbt_test_helper.create_model(
|
|
305
|
+
"model3",
|
|
306
|
+
unique_id="model.model3",
|
|
307
|
+
curr_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
308
|
+
base_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
309
|
+
curr_columns={"c": "int"},
|
|
310
|
+
base_columns={"c": "int"},
|
|
311
|
+
depends_on=["model.model2"],
|
|
312
|
+
)
|
|
313
|
+
dbt_test_helper.create_model(
|
|
314
|
+
"model4",
|
|
315
|
+
unique_id="model.model4",
|
|
316
|
+
curr_sql='select y + 1 as year from {{ ref("model2") }} --- partial breaking',
|
|
317
|
+
base_sql='select y as year from {{ ref("model2") }}',
|
|
318
|
+
curr_columns={"year": "int"},
|
|
319
|
+
base_columns={"year": "int"},
|
|
320
|
+
depends_on=["model.model2"],
|
|
321
|
+
)
|
|
322
|
+
dbt_test_helper.create_model(
|
|
323
|
+
"model5",
|
|
324
|
+
unique_id="model.model5",
|
|
325
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
326
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
327
|
+
curr_columns={"c": "int", "y": "int"},
|
|
328
|
+
base_columns={"c": "int", "y": "int"},
|
|
329
|
+
depends_on=["model.model1"],
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
333
|
+
|
|
334
|
+
result = adapter.get_cll(no_cll=True)
|
|
335
|
+
assert_cll_contain_nodes(result, ["model.model1", "model.model2", "model.model3", "model.model4", "model.model5"])
|
|
336
|
+
assert_model(result, "model.model1", parents=[])
|
|
337
|
+
assert_model(result, "model.model2", parents=["model.model1"])
|
|
338
|
+
assert_model(result, "model.model3", parents=["model.model2"])
|
|
339
|
+
assert_model(result, "model.model4", parents=["model.model2"])
|
|
340
|
+
assert_model(result, "model.model5", parents=["model.model1"])
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def test_impact_radius_with_change_analysis_no_cll(dbt_test_helper):
|
|
344
|
+
dbt_test_helper.create_model(
|
|
345
|
+
"model1",
|
|
346
|
+
unique_id="model.model1",
|
|
347
|
+
curr_sql="select 1 as c",
|
|
348
|
+
base_sql="select 1 as c --- non-breaking",
|
|
349
|
+
curr_columns={"c": "int"},
|
|
350
|
+
base_columns={"c": "int"},
|
|
351
|
+
)
|
|
352
|
+
dbt_test_helper.create_model(
|
|
353
|
+
"model2",
|
|
354
|
+
unique_id="model.model2",
|
|
355
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
356
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }} where c > 0 --- breaking',
|
|
357
|
+
curr_columns={"c": "int", "y": "int"},
|
|
358
|
+
base_columns={"c": "int", "y": "int"},
|
|
359
|
+
depends_on=["model.model1"],
|
|
360
|
+
)
|
|
361
|
+
dbt_test_helper.create_model(
|
|
362
|
+
"model3",
|
|
363
|
+
unique_id="model.model3",
|
|
364
|
+
curr_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
365
|
+
base_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
366
|
+
curr_columns={"c": "int"},
|
|
367
|
+
base_columns={"c": "int"},
|
|
368
|
+
depends_on=["model.model2"],
|
|
369
|
+
)
|
|
370
|
+
dbt_test_helper.create_model(
|
|
371
|
+
"model4",
|
|
372
|
+
unique_id="model.model4",
|
|
373
|
+
curr_sql='select y + 1 as year from {{ ref("model2") }} --- partial breaking',
|
|
374
|
+
base_sql='select y as year from {{ ref("model2") }}',
|
|
375
|
+
curr_columns={"year": "int"},
|
|
376
|
+
base_columns={"year": "int"},
|
|
377
|
+
depends_on=["model.model2"],
|
|
378
|
+
)
|
|
379
|
+
dbt_test_helper.create_model(
|
|
380
|
+
"model5",
|
|
381
|
+
unique_id="model.model5",
|
|
382
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
383
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
384
|
+
curr_columns={"c": "int", "y": "int"},
|
|
385
|
+
base_columns={"c": "int", "y": "int"},
|
|
386
|
+
depends_on=["model.model1"],
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
390
|
+
|
|
391
|
+
# breaking
|
|
392
|
+
result = adapter.get_cll(change_analysis=True, no_cll=True, no_upstream=True)
|
|
393
|
+
assert_cll_contain_nodes(result, ["model.model1", "model.model2", "model.model3", "model.model4"])
|
|
394
|
+
assert_model(result, "model.model1", parents=[], change_category="non_breaking")
|
|
395
|
+
assert_model(result, "model.model2", parents=[], change_category="breaking")
|
|
396
|
+
assert_model(result, "model.model3", parents=["model.model2"], change_category=None)
|
|
397
|
+
assert_model(result, "model.model4", parents=["model.model2"], change_category="partial_breaking")
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def test_impact_radius_with_change_analysis_no_cll_2(dbt_test_helper):
|
|
401
|
+
dbt_test_helper.create_model(
|
|
402
|
+
"model1",
|
|
403
|
+
unique_id="model.model1",
|
|
404
|
+
curr_sql="select 1 as c",
|
|
405
|
+
base_sql="select 2 as c",
|
|
406
|
+
curr_columns={"c": "int"},
|
|
407
|
+
base_columns={"c": "int"},
|
|
408
|
+
)
|
|
409
|
+
dbt_test_helper.create_model(
|
|
410
|
+
"model2",
|
|
411
|
+
unique_id="model.model2",
|
|
412
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
413
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }} where c > 0 --- breaking',
|
|
414
|
+
curr_columns={"c": "int", "y": "int"},
|
|
415
|
+
base_columns={"c": "int", "y": "int"},
|
|
416
|
+
depends_on=["model.model1"],
|
|
417
|
+
)
|
|
418
|
+
dbt_test_helper.create_model(
|
|
419
|
+
"model3",
|
|
420
|
+
unique_id="model.model3",
|
|
421
|
+
curr_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
422
|
+
base_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
423
|
+
curr_columns={"c": "int"},
|
|
424
|
+
base_columns={"c": "int"},
|
|
425
|
+
depends_on=["model.model2"],
|
|
426
|
+
)
|
|
427
|
+
dbt_test_helper.create_model(
|
|
428
|
+
"model4",
|
|
429
|
+
unique_id="model.model4",
|
|
430
|
+
curr_sql='select y + 1 as year from {{ ref("model2") }} --- partial breaking',
|
|
431
|
+
base_sql='select y as year from {{ ref("model2") }}',
|
|
432
|
+
curr_columns={"year": "int"},
|
|
433
|
+
base_columns={"year": "int"},
|
|
434
|
+
depends_on=["model.model2"],
|
|
435
|
+
)
|
|
436
|
+
dbt_test_helper.create_model(
|
|
437
|
+
"model5",
|
|
438
|
+
unique_id="model.model5",
|
|
439
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
440
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
441
|
+
curr_columns={"c": "int", "y": "int"},
|
|
442
|
+
base_columns={"c": "int", "y": "int"},
|
|
443
|
+
depends_on=["model.model1"],
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
447
|
+
|
|
448
|
+
# breaking
|
|
449
|
+
result = adapter.get_cll(change_analysis=True, no_cll=True, no_upstream=True)
|
|
450
|
+
assert_cll_contain_nodes(result, ["model.model1", "model.model2", "model.model3", "model.model4", "model.model5"])
|
|
451
|
+
assert_model(result, "model.model1", parents=[], change_category="partial_breaking")
|
|
452
|
+
assert_model(result, "model.model2", parents=["model.model1"], change_category="breaking")
|
|
453
|
+
assert_model(result, "model.model3", parents=["model.model2"], change_category=None)
|
|
454
|
+
assert_model(result, "model.model4", parents=["model.model2"], change_category="partial_breaking")
|
|
455
|
+
assert_model(result, "model.model5", parents=["model.model1"])
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def test_impact_radius_with_change_analysis_with_cll(dbt_test_helper):
|
|
459
|
+
dbt_test_helper.create_model(
|
|
460
|
+
"model1",
|
|
461
|
+
unique_id="model.model1",
|
|
462
|
+
curr_sql="select 1 as c",
|
|
463
|
+
base_sql="select 1 as c --- non-breaking",
|
|
464
|
+
curr_columns={"c": "int"},
|
|
465
|
+
base_columns={"c": "int"},
|
|
466
|
+
)
|
|
467
|
+
dbt_test_helper.create_model(
|
|
468
|
+
"model2",
|
|
469
|
+
unique_id="model.model2",
|
|
470
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
471
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }} where c > 0 --- breaking',
|
|
472
|
+
curr_columns={"c": "int", "y": "int"},
|
|
473
|
+
base_columns={"c": "int", "y": "int"},
|
|
474
|
+
depends_on=["model.model1"],
|
|
475
|
+
)
|
|
476
|
+
dbt_test_helper.create_model(
|
|
477
|
+
"model3",
|
|
478
|
+
unique_id="model.model3",
|
|
479
|
+
curr_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
480
|
+
base_sql='select c from {{ ref("model2") }} where y < 2025',
|
|
481
|
+
curr_columns={"c": "int"},
|
|
482
|
+
base_columns={"c": "int"},
|
|
483
|
+
depends_on=["model.model2"],
|
|
484
|
+
)
|
|
485
|
+
dbt_test_helper.create_model(
|
|
486
|
+
"model4",
|
|
487
|
+
unique_id="model.model4",
|
|
488
|
+
curr_sql='select y + 1 as year from {{ ref("model2") }} --- partial breaking',
|
|
489
|
+
base_sql='select y as year from {{ ref("model2") }}',
|
|
490
|
+
curr_columns={"year": "int"},
|
|
491
|
+
base_columns={"year": "int"},
|
|
492
|
+
depends_on=["model.model2"],
|
|
493
|
+
)
|
|
494
|
+
dbt_test_helper.create_model(
|
|
495
|
+
"model5",
|
|
496
|
+
unique_id="model.model5",
|
|
497
|
+
curr_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
498
|
+
base_sql='select c, 2025 as y from {{ ref("model1") }}',
|
|
499
|
+
curr_columns={"c": "int", "y": "int"},
|
|
500
|
+
base_columns={"c": "int", "y": "int"},
|
|
501
|
+
depends_on=["model.model1"],
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
505
|
+
|
|
506
|
+
result = adapter.get_cll(change_analysis=True, no_upstream=True)
|
|
507
|
+
assert_cll_contain_nodes(result, ["model.model1", "model.model2", "model.model3", "model.model4"])
|
|
508
|
+
assert_cll_contain_columns(result, [("model.model4", "year")])
|
|
509
|
+
assert_model(result, "model.model1", parents=[], change_category="non_breaking")
|
|
510
|
+
assert_model(result, "model.model2", parents=[], change_category="breaking")
|
|
511
|
+
assert_model(result, "model.model3", parents=["model.model2"], change_category=None)
|
|
512
|
+
assert_model(result, "model.model4", parents=["model.model2"], change_category="partial_breaking")
|
|
239
513
|
|
|
240
514
|
|
|
241
|
-
def
|
|
515
|
+
def test_impact_radius_by_node_no_cll(dbt_test_helper):
|
|
242
516
|
# non-breaking
|
|
243
517
|
dbt_test_helper.create_model(
|
|
244
518
|
"model1",
|
|
@@ -280,15 +554,15 @@ def test_impact_radius_nodes(dbt_test_helper):
|
|
|
280
554
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
281
555
|
|
|
282
556
|
# breaking
|
|
283
|
-
result = adapter.
|
|
284
|
-
|
|
557
|
+
result = adapter.get_cll(node_id="model.model2", change_analysis=True, no_cll=True, no_upstream=True)
|
|
558
|
+
assert_cll_contain_nodes(result, ["model.model2", "model.model3", "model.model4"])
|
|
285
559
|
|
|
286
560
|
# non-breaking
|
|
287
|
-
result = adapter.
|
|
288
|
-
|
|
561
|
+
result = adapter.get_cll(node_id="model.model1", change_analysis=True, no_cll=True, no_upstream=True)
|
|
562
|
+
assert_cll_contain_nodes(result, ["model.model1"])
|
|
289
563
|
|
|
290
564
|
|
|
291
|
-
def
|
|
565
|
+
def test_impact_radius_by_node_with_cll(dbt_test_helper):
|
|
292
566
|
# added column
|
|
293
567
|
dbt_test_helper.create_model(
|
|
294
568
|
"model1",
|
|
@@ -329,16 +603,20 @@ def test_impact_radius_columns(dbt_test_helper):
|
|
|
329
603
|
|
|
330
604
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
331
605
|
|
|
332
|
-
result = adapter.
|
|
333
|
-
|
|
334
|
-
|
|
606
|
+
result = adapter.get_cll(node_id="model.model2", change_analysis=True, no_upstream=True)
|
|
607
|
+
assert_model(result, "model.model2", parents=[], change_category="partial_breaking")
|
|
608
|
+
assert_column(result, "model.model2", "y", transformation_type="source", parents=[], change_status="modified")
|
|
609
|
+
assert_cll_contain_nodes(result, ["model.model2", "model.model3"])
|
|
610
|
+
assert_cll_contain_columns(result, [("model.model2", "y"), ("model.model4", "y")])
|
|
335
611
|
|
|
336
|
-
result = adapter.
|
|
337
|
-
|
|
338
|
-
|
|
612
|
+
result = adapter.get_cll(node_id="model.model1", change_analysis=True, no_upstream=True)
|
|
613
|
+
assert_cll_contain_nodes(result, ["model.model1"])
|
|
614
|
+
assert_cll_contain_columns(result, [("model.model1", "d")])
|
|
615
|
+
assert_model(result, "model.model1", parents=[], change_category="non_breaking")
|
|
616
|
+
assert_column(result, "model.model1", "d", transformation_type="source", parents=[], change_status="added")
|
|
339
617
|
|
|
340
618
|
|
|
341
|
-
def
|
|
619
|
+
def test_impact_radius_by_node_with_cll_2(dbt_test_helper):
|
|
342
620
|
# added column
|
|
343
621
|
dbt_test_helper.create_model(
|
|
344
622
|
"model1",
|
|
@@ -379,6 +657,10 @@ def test_impact_radius(dbt_test_helper):
|
|
|
379
657
|
|
|
380
658
|
adapter: DbtAdapter = dbt_test_helper.context.adapter
|
|
381
659
|
|
|
382
|
-
result = adapter.
|
|
383
|
-
|
|
384
|
-
|
|
660
|
+
result = adapter.get_cll(node_id="model.model2", change_analysis=True, no_upstream=True)
|
|
661
|
+
assert_model(result, "model.model2", parents=[], change_category="breaking")
|
|
662
|
+
assert_column(result, "model.model2", "y", transformation_type="source", parents=[], change_status="modified")
|
|
663
|
+
assert_model(result, "model.model3", parents=["model.model2", ("model.model2", "y")])
|
|
664
|
+
assert_column(result, "model.model4", "y", transformation_type="passthrough", parents=[("model.model2", "y")])
|
|
665
|
+
assert_cll_contain_nodes(result, ["model.model2", "model.model3", "model.model4"])
|
|
666
|
+
assert_cll_contain_columns(result, [("model.model2", "d"), ("model.model2", "y"), ("model.model4", "y")])
|
tests/test_core.py
CHANGED
|
@@ -24,6 +24,4 @@ def test_lineage_diff(dbt_test_helper):
|
|
|
24
24
|
nodediff = result.diff.get("model1")
|
|
25
25
|
assert nodediff is None
|
|
26
26
|
nodediff2 = result.diff.get("model2")
|
|
27
|
-
assert
|
|
28
|
-
nodediff2 is not None and nodediff2.change_status == "modified" and nodediff2.change.category == "non_breaking"
|
|
29
|
-
)
|
|
27
|
+
assert nodediff2 is not None and nodediff2.change_status == "modified"
|