Skip to content

Commit 4558b00

Browse files
authored
Make rows key optional in test YAML (#937)
1 parent fdb21ea commit 4558b00

2 files changed

Lines changed: 236 additions & 10 deletions

File tree

sqlmesh/core/test/definition.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from sqlmesh.core.model import Model, PythonModel, SqlModel
1313
from sqlmesh.utils.errors import SQLMeshError
1414

15+
Row = t.Dict[str, t.Any]
16+
1517

1618
class TestError(SQLMeshError):
1719
"""Test error"""
@@ -55,7 +57,9 @@ def __init__(
5557

5658
def setUp(self) -> None:
5759
"""Load all input tables"""
58-
inputs = {name: table["rows"] for name, table in self.body.get("inputs", {}).items()}
60+
inputs = {
61+
name: self._get_rows(table) for name, table in self.body.get("inputs", {}).items()
62+
}
5963

6064
for table, rows in inputs.items():
6165
df = pd.DataFrame.from_records(rows) # noqa
@@ -128,6 +132,18 @@ def create_test(
128132
def __str__(self) -> str:
129133
return f"{self.test_name} ({self.path}:{self.body.lc.line})" # type: ignore
130134

135+
def _get_rows(self, table: list[Row] | dict[str, list[Row]]) -> list[Row]:
136+
"""Get a list of rows for input and output table data.
137+
138+
Input and output table data might be a list of rows or it might be a dictionary
139+
with a "rows" key.
140+
"""
141+
if isinstance(table, dict):
142+
if "rows" not in table:
143+
_raise_error("Incomplete test, missing row data for table", self.path)
144+
return table["rows"]
145+
return table
146+
131147

132148
class SqlModelTest(ModelTest):
133149
def __init__(
@@ -180,16 +196,16 @@ def test_ctes(self) -> None:
180196
cte_query = self.ctes[cte_name].this
181197
for alias, cte in self.ctes.items():
182198
cte_query = cte_query.with_(alias, cte.this)
183-
expected_df = pd.DataFrame.from_records(value["rows"])
199+
expected_df = pd.DataFrame.from_records(self._get_rows(value))
184200
actual_df = self.execute(cte_query)
185201
self.assert_equal(expected_df, actual_df)
186202

187203
def runTest(self) -> None:
188204
self.test_ctes()
189205

190206
# Test model query
191-
if "rows" in self.body["outputs"].get("query", {}):
192-
expected_df = pd.DataFrame.from_records(self.body["outputs"]["query"]["rows"])
207+
if "query" in self.body["outputs"]:
208+
expected_df = pd.DataFrame.from_records(self._get_rows(self.body["outputs"]["query"]))
193209
actual_df = self.execute(self.query)
194210
self.assert_equal(expected_df, actual_df)
195211

@@ -224,8 +240,8 @@ def __init__(
224240
)
225241

226242
def runTest(self) -> None:
227-
if "rows" in self.body["outputs"].get("query", {}):
228-
expected_df = pd.DataFrame.from_records(self.body["outputs"]["query"]["rows"])
243+
if "query" in self.body["outputs"]:
244+
expected_df = pd.DataFrame.from_records(self._get_rows(self.body["outputs"]["query"]))
229245
actual_df = next(
230246
self.model.render(
231247
context=self.context,

tests/core/test_test.py

Lines changed: 214 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,121 @@ def test_ctes(sushi_context: Context) -> None:
4040
model: sushi.foo
4141
inputs:
4242
raw:
43-
rows:
43+
- id: 1
44+
outputs:
45+
ctes:
46+
source:
4447
- id: 1
48+
renamed:
49+
- fid: 1
50+
query:
51+
- fid: 1
52+
vars:
53+
start: 2022-01-01
54+
end: 2022-01-01
55+
latest: 2022-01-01
56+
"""
57+
)
58+
result = SqlModelTest(
59+
body=body["test_foo"],
60+
test_name="test_foo",
61+
model=model,
62+
models=sushi_context._models,
63+
engine_adapter=sushi_context._test_engine_adapter,
64+
path=None,
65+
).run()
66+
assert result and result.wasSuccessful()
67+
68+
69+
def test_ctes_only(sushi_context: Context) -> None:
70+
model = t.cast(
71+
SqlModel,
72+
sushi_context.upsert_model(
73+
load_model(
74+
parse(
75+
"""
76+
MODEL (
77+
name sushi.foo,
78+
kind FULL,
79+
);
80+
81+
WITH source AS (
82+
SELECT id FROM raw
83+
),
84+
renamed AS (
85+
SELECT id as fid FROM source
86+
)
87+
SELECT fid FROM renamed;
88+
"""
89+
)
90+
)
91+
),
92+
)
93+
94+
body = load_yaml(
95+
"""
96+
test_foo:
97+
model: sushi.foo
98+
inputs:
99+
raw:
100+
- id: 1
45101
outputs:
46102
ctes:
47103
source:
48-
rows:
49104
- id: 1
50105
renamed:
51-
rows:
52106
- fid: 1
107+
vars:
108+
start: 2022-01-01
109+
end: 2022-01-01
110+
latest: 2022-01-01
111+
"""
112+
)
113+
result = SqlModelTest(
114+
body=body["test_foo"],
115+
test_name="test_foo",
116+
model=model,
117+
models=sushi_context._models,
118+
engine_adapter=sushi_context._test_engine_adapter,
119+
path=None,
120+
).run()
121+
assert result and result.wasSuccessful()
122+
123+
124+
def test_query_only(sushi_context: Context) -> None:
125+
model = t.cast(
126+
SqlModel,
127+
sushi_context.upsert_model(
128+
load_model(
129+
parse(
130+
"""
131+
MODEL (
132+
name sushi.foo,
133+
kind FULL,
134+
);
135+
136+
WITH source AS (
137+
SELECT id FROM raw
138+
),
139+
renamed AS (
140+
SELECT id as fid FROM source
141+
)
142+
SELECT fid FROM renamed;
143+
"""
144+
)
145+
)
146+
),
147+
)
148+
149+
body = load_yaml(
150+
"""
151+
test_foo:
152+
model: sushi.foo
153+
inputs:
154+
raw:
155+
- id: 1
156+
outputs:
53157
query:
54-
rows:
55158
- fid: 1
56159
vars:
57160
start: 2022-01-01
@@ -68,3 +171,110 @@ def test_ctes(sushi_context: Context) -> None:
68171
path=None,
69172
).run()
70173
assert result and result.wasSuccessful()
174+
175+
176+
def test_with_rows(sushi_context: Context) -> None:
177+
model = t.cast(
178+
SqlModel,
179+
sushi_context.upsert_model(
180+
load_model(
181+
parse(
182+
"""
183+
MODEL (
184+
name sushi.foo,
185+
kind FULL,
186+
);
187+
188+
WITH source AS (
189+
SELECT id FROM raw
190+
)
191+
SELECT id FROM source;
192+
"""
193+
)
194+
)
195+
),
196+
)
197+
198+
body = load_yaml(
199+
"""
200+
test_foo:
201+
model: sushi.foo
202+
inputs:
203+
raw:
204+
rows:
205+
- id: 1
206+
outputs:
207+
ctes:
208+
source:
209+
rows:
210+
- id: 1
211+
query:
212+
rows:
213+
- id: 1
214+
vars:
215+
start: 2022-01-01
216+
end: 2022-01-01
217+
latest: 2022-01-01
218+
"""
219+
)
220+
result = SqlModelTest(
221+
body=body["test_foo"],
222+
test_name="test_foo",
223+
model=model,
224+
models=sushi_context._models,
225+
engine_adapter=sushi_context._test_engine_adapter,
226+
path=None,
227+
).run()
228+
assert result and result.wasSuccessful()
229+
230+
231+
def test_without_rows(sushi_context: Context) -> None:
232+
model = t.cast(
233+
SqlModel,
234+
sushi_context.upsert_model(
235+
load_model(
236+
parse(
237+
"""
238+
MODEL (
239+
name sushi.foo,
240+
kind FULL,
241+
);
242+
243+
WITH source AS (
244+
SELECT id FROM raw
245+
)
246+
SELECT id FROM source;
247+
"""
248+
)
249+
)
250+
),
251+
)
252+
253+
body = load_yaml(
254+
"""
255+
test_foo:
256+
model: sushi.foo
257+
inputs:
258+
raw:
259+
- id: 1
260+
outputs:
261+
ctes:
262+
source:
263+
- id: 1
264+
query:
265+
- id: 1
266+
vars:
267+
start: 2022-01-01
268+
end: 2022-01-01
269+
latest: 2022-01-01
270+
"""
271+
)
272+
result = SqlModelTest(
273+
body=body["test_foo"],
274+
test_name="test_foo",
275+
model=model,
276+
models=sushi_context._models,
277+
engine_adapter=sushi_context._test_engine_adapter,
278+
path=None,
279+
).run()
280+
assert result and result.wasSuccessful()

0 commit comments

Comments
 (0)