Skip to content

Commit 680611e

Browse files
axellpadillaAxell Padilla
authored andcommitted
Adding tests for new feature to use config for indexes
1 parent f6bab93 commit 680611e

File tree

2 files changed

+380
-69
lines changed

2 files changed

+380
-69
lines changed
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
import re
2+
3+
import pytest
4+
from dbt.tests.util import run_dbt, run_dbt_and_capture
5+
6+
base_validation = """
7+
with base_query AS (
8+
select i.[name] as index_name,
9+
substring(column_names, 1, len(column_names)-1) as [columns],
10+
case when i.[type] = 1 then 'Clustered index'
11+
when i.[type] = 2 then 'Nonclustered unique index'
12+
when i.[type] = 3 then 'XML index'
13+
when i.[type] = 4 then 'Spatial index'
14+
when i.[type] = 5 then 'Clustered columnstore index'
15+
when i.[type] = 6 then 'Nonclustered columnstore index'
16+
when i.[type] = 7 then 'Nonclustered hash index'
17+
end as index_type,
18+
case when i.is_unique = 1 then 'Unique'
19+
else 'Not unique' end as [unique],
20+
schema_name(t.schema_id) + '.' + t.[name] as table_view,
21+
case when t.[type] = 'U' then 'Table'
22+
when t.[type] = 'V' then 'View'
23+
end as [object_type],
24+
s.name as schema_name
25+
from sys.objects t
26+
inner join sys.schemas s
27+
on
28+
t.schema_id = s.schema_id
29+
inner join sys.indexes i
30+
on t.object_id = i.object_id
31+
cross apply (select col.[name] + ', '
32+
from sys.index_columns ic
33+
inner join sys.columns col
34+
on ic.object_id = col.object_id
35+
and ic.column_id = col.column_id
36+
where ic.object_id = t.object_id
37+
and ic.index_id = i.index_id
38+
order by key_ordinal
39+
for xml path ('') ) D (column_names)
40+
where t.is_ms_shipped <> 1
41+
and index_id > 0
42+
)
43+
"""
44+
45+
index_count = (
46+
base_validation
47+
+ """
48+
select
49+
index_type,
50+
count(*) index_count
51+
from
52+
base_query
53+
WHERE
54+
schema_name='{schema_name}'
55+
group by index_type
56+
"""
57+
)
58+
59+
indexes_def = (
60+
base_validation
61+
+ """
62+
SELECT
63+
index_name,
64+
[columns],
65+
index_type,
66+
[unique],
67+
table_view,
68+
[object_type],
69+
schema_name
70+
FROM
71+
base_query
72+
WHERE
73+
schema_name='{schema_name}'
74+
AND
75+
table_view='{schema_name}.{table_name}'
76+
77+
"""
78+
)
79+
80+
# Altered from: https://github.com/dbt-labs/dbt-postgres
81+
82+
models__incremental_sql = """
83+
{{
84+
config(
85+
materialized = "incremental",
86+
as_columnstore = False,
87+
indexes=[
88+
{'columns': ['column_a'], 'type': 'nonclustered'},
89+
{'columns': ['column_a', 'column_b'], 'unique': True},
90+
]
91+
)
92+
}}
93+
94+
select *
95+
from (
96+
select 1 as column_a, 2 as column_b
97+
) t
98+
99+
{% if is_incremental() %}
100+
where column_a > (select max(column_a) from {{this}})
101+
{% endif %}
102+
103+
"""
104+
105+
models__columnstore_sql = """
106+
{{
107+
config(
108+
materialized = "incremental",
109+
as_columnstore = False,
110+
indexes=[
111+
{'columns': ['column_a'], 'type': 'columnstore'},
112+
]
113+
)
114+
}}
115+
116+
select *
117+
from (
118+
select 1 as column_a, 2 as column_b
119+
) t
120+
121+
{% if is_incremental() %}
122+
where column_a > (select max(column_a) from {{this}})
123+
{% endif %}
124+
125+
"""
126+
127+
128+
models__table_sql = """
129+
{{
130+
config(
131+
materialized = "table",
132+
as_columnstore = False,
133+
indexes=[
134+
{'columns': ['column_a']},
135+
{'columns': ['column_b']},
136+
{'columns': ['column_a', 'column_b']},
137+
{'columns': ['column_b', 'column_a'], 'type': 'clustered', 'unique': True},
138+
{'columns': ['column_a'], 'type': 'nonclustered'}
139+
]
140+
)
141+
}}
142+
143+
select 1 as column_a, 2 as column_b
144+
145+
"""
146+
147+
models_invalid__invalid_columns_type_sql = """
148+
{{
149+
config(
150+
materialized = "table",
151+
indexes=[
152+
{'columns': 'column_a, column_b'},
153+
]
154+
)
155+
}}
156+
157+
select 1 as column_a, 2 as column_b
158+
159+
"""
160+
161+
models_invalid__invalid_type_sql = """
162+
{{
163+
config(
164+
materialized = "table",
165+
indexes=[
166+
{'columns': ['column_a'], 'type': 'non_existent_type'},
167+
]
168+
)
169+
}}
170+
171+
select 1 as column_a, 2 as column_b
172+
173+
"""
174+
175+
models_invalid__invalid_unique_config_sql = """
176+
{{
177+
config(
178+
materialized = "table",
179+
indexes=[
180+
{'columns': ['column_a'], 'unique': 'yes'},
181+
]
182+
)
183+
}}
184+
185+
select 1 as column_a, 2 as column_b
186+
187+
"""
188+
189+
models_invalid__missing_columns_sql = """
190+
{{
191+
config(
192+
materialized = "table",
193+
indexes=[
194+
{'unique': True},
195+
]
196+
)
197+
}}
198+
199+
select 1 as column_a, 2 as column_b
200+
201+
"""
202+
203+
snapshots__colors_sql = """
204+
{% snapshot colors %}
205+
206+
{{
207+
config(
208+
target_database=database,
209+
target_schema=schema,
210+
as_columnstore=False,
211+
unique_key='id',
212+
strategy='check',
213+
check_cols=['color'],
214+
indexes=[
215+
{'columns': ['id'], 'type': 'nonclustered'},
216+
{'columns': ['id', 'color'], 'unique': True},
217+
]
218+
)
219+
}}
220+
221+
{% if var('version') == 1 %}
222+
223+
select 1 as id, 'red' as color union all
224+
select 2 as id, 'green' as color
225+
226+
{% else %}
227+
228+
select 1 as id, 'blue' as color union all
229+
select 2 as id, 'green' as color
230+
231+
{% endif %}
232+
233+
{% endsnapshot %}
234+
235+
"""
236+
237+
seeds__seed_csv = """country_code,country_name
238+
US,United States
239+
CA,Canada
240+
GB,United Kingdom
241+
"""
242+
243+
244+
class TestSQLServerIndex:
245+
@pytest.fixture(scope="class")
246+
def models(self):
247+
return {
248+
"table.sql": models__table_sql,
249+
"incremental.sql": models__incremental_sql,
250+
"columnstore.sql": models__columnstore_sql,
251+
}
252+
253+
@pytest.fixture(scope="class")
254+
def seeds(self):
255+
return {"seed.csv": seeds__seed_csv}
256+
257+
@pytest.fixture(scope="class")
258+
def snapshots(self):
259+
return {"colors.sql": snapshots__colors_sql}
260+
261+
@pytest.fixture(scope="class")
262+
def project_config_update(self):
263+
return {
264+
"config-version": 2,
265+
"seeds": {
266+
"quote_columns": False,
267+
"indexes": [
268+
{"columns": ["country_code"], "unique": False, "type": "nonclustered"},
269+
{"columns": ["country_code", "country_name"], "unique": True},
270+
],
271+
},
272+
"vars": {
273+
"version": 1,
274+
},
275+
}
276+
277+
def test_table(self, project, unique_schema):
278+
results = run_dbt(["run", "--models", "table"])
279+
assert len(results) == 1
280+
281+
indexes = self.get_indexes("table", project, unique_schema)
282+
expected = [
283+
{"columns": "column_a", "unique": False, "type": "nonclustered"},
284+
{"columns": "column_b", "unique": False, "type": "nonclustered"},
285+
{"columns": "column_a, column_b", "unique": False, "type": "nonclustered"},
286+
{"columns": "column_b, column_a", "unique": True, "type": "clustered"},
287+
{"columns": "column_a", "unique": False, "type": "nonclustered"},
288+
]
289+
assert len(indexes) == len(expected)
290+
291+
def test_incremental(self, project, unique_schema):
292+
for additional_argument in [[], [], ["--full-refresh"]]:
293+
results = run_dbt(["run", "--models", "incremental"] + additional_argument)
294+
assert len(results) == 1
295+
296+
indexes = self.get_indexes("incremental", project, unique_schema)
297+
expected = [
298+
{"columns": "column_a", "unique": False, "type": "nonclustered"},
299+
{"columns": "column_a, column_b", "unique": True, "type": "nonclustered"},
300+
]
301+
assert len(indexes) == len(expected)
302+
303+
def test_columnstore(self, project, unique_schema):
304+
for additional_argument in [[], [], ["--full-refresh"]]:
305+
results = run_dbt(["run", "--models", "columnstore"] + additional_argument)
306+
assert len(results) == 1
307+
308+
indexes = self.get_indexes("columnstore", project, unique_schema)
309+
expected = [
310+
{"columns": "column_a", "unique": False, "type": "columnstore"},
311+
]
312+
assert len(indexes) == len(expected)
313+
314+
def test_seed(self, project, unique_schema):
315+
for additional_argument in [[], [], ["--full-refresh"]]:
316+
results = run_dbt(["seed"] + additional_argument)
317+
assert len(results) == 1
318+
319+
indexes = self.get_indexes("seed", project, unique_schema)
320+
expected = [
321+
{"columns": "country_code", "unique": False, "type": "nonclustered"},
322+
{"columns": "country_code, country_name", "unique": True, "type": "clustered"},
323+
]
324+
assert len(indexes) == len(expected)
325+
326+
def test_snapshot(self, project, unique_schema):
327+
for version in [1, 2]:
328+
results = run_dbt(["snapshot", "--vars", f"version: {version}"])
329+
assert len(results) == 1
330+
331+
indexes = self.get_indexes("colors", project, unique_schema)
332+
expected = [
333+
{"columns": "id", "unique": False, "type": "nonclustered"},
334+
{"columns": "id, color", "unique": True, "type": "clustered"},
335+
]
336+
assert len(indexes) == len(expected)
337+
338+
def get_indexes(self, table_name, project, unique_schema):
339+
sql = indexes_def.format(schema_name=unique_schema, table_name=table_name)
340+
results = project.run_sql(sql, fetch="all")
341+
return [self.index_definition_dict(row) for row in results]
342+
343+
def index_definition_dict(self, index_definition):
344+
is_unique = index_definition[3] == "Unique"
345+
return {
346+
"columns": index_definition[1],
347+
"unique": is_unique,
348+
"type": index_definition[2],
349+
}
350+
351+
def assertCountEqual(self, a, b):
352+
assert len(a) == len(b)
353+
354+
355+
class TestSQLServerInvalidIndex:
356+
@pytest.fixture(scope="class")
357+
def models(self):
358+
return {
359+
"invalid_unique_config.sql": models_invalid__invalid_unique_config_sql,
360+
"invalid_type.sql": models_invalid__invalid_type_sql,
361+
"invalid_columns_type.sql": models_invalid__invalid_columns_type_sql,
362+
"missing_columns.sql": models_invalid__missing_columns_sql,
363+
}
364+
365+
def test_invalid_index_configs(self, project):
366+
results, output = run_dbt_and_capture(expect_pass=False)
367+
assert len(results) == 4
368+
assert re.search(r"columns.*is not of type 'array'", output)
369+
assert re.search(r"unique.*is not of type 'boolean'", output)
370+
assert re.search(r"'columns' is a required property", output)
371+
assert re.search(r"'non_existent_type'.*is not one of", output)

0 commit comments

Comments
 (0)