|
10 | 10 |
|
11 | 11 |
|
12 | 12 | @pytest.mark.slow
|
13 |
| -def test_data_diff(sushi_context_fixed_date): |
| 13 | +def test_data_diff(sushi_context_fixed_date, capsys, caplog): |
14 | 14 | model = sushi_context_fixed_date.models['"memory"."sushi"."customer_revenue_by_day"']
|
15 | 15 |
|
16 | 16 | model.query.select(exp.cast("'1'", "VARCHAR").as_("modified_col"), "1 AS y", copy=False)
|
@@ -74,6 +74,11 @@ def test_data_diff(sushi_context_fixed_date):
|
74 | 74 | model_or_snapshot="sushi.customer_revenue_by_day",
|
75 | 75 | )
|
76 | 76 |
|
| 77 | + # verify queries were actually logged to the log file, this helps immensely with debugging |
| 78 | + console_output = capsys.readouterr() |
| 79 | + assert "__sqlmesh_join_key" not in console_output # they should not go to the console |
| 80 | + assert "__sqlmesh_join_key" in caplog.text |
| 81 | + |
77 | 82 | schema_diff = diff.schema_diff()
|
78 | 83 | assert schema_diff.added == [("z", exp.DataType.build("int"))]
|
79 | 84 | assert schema_diff.modified == {
|
@@ -272,6 +277,12 @@ def test_generated_sql(sushi_context_fixed_date: Context, mocker: MockerFixture)
|
272 | 277 | sample_query_sql = 'SELECT "s_exists", "t_exists", "row_joined", "row_full_match", "s__key", "s__value", "s____sqlmesh_join_key", "t__key", "t__value", "t____sqlmesh_join_key" FROM "memory"."sqlmesh_temp_test"."__temp_diff_abcdefgh" WHERE "key_matches" = 0 OR "value_matches" = 0 ORDER BY "s__key" NULLS FIRST, "t__key" NULLS FIRST LIMIT 20'
|
273 | 278 | drop_sql = 'DROP TABLE IF EXISTS "memory"."sqlmesh_temp_test"."__temp_diff_abcdefgh"'
|
274 | 279 |
|
| 280 | + # make with_log_level() return the current instance of engine_adapter so we can still spy on _execute |
| 281 | + mocker.patch.object( |
| 282 | + engine_adapter, "with_log_level", new_callable=lambda: lambda _: engine_adapter |
| 283 | + ) |
| 284 | + assert engine_adapter.with_log_level(1) == engine_adapter |
| 285 | + |
275 | 286 | spy_execute = mocker.spy(engine_adapter, "_execute")
|
276 | 287 | mocker.patch("sqlmesh.core.engine_adapter.base.random_id", return_value="abcdefgh")
|
277 | 288 |
|
@@ -302,3 +313,53 @@ def test_generated_sql(sushi_context_fixed_date: Context, mocker: MockerFixture)
|
302 | 313 |
|
303 | 314 | query_sql_where = 'CREATE TABLE IF NOT EXISTS "memory"."sqlmesh_temp"."__temp_diff_abcdefgh" AS WITH "__source" AS (SELECT "key", "value", "key" AS "__sqlmesh_join_key" FROM "table_diff_source" WHERE "key" = 2), "__target" AS (SELECT "key", "value", "key" AS "__sqlmesh_join_key" FROM "table_diff_target" WHERE "key" = 2), "__stats" AS (SELECT "s"."key" AS "s__key", "s"."value" AS "s__value", "s"."__sqlmesh_join_key" AS "s____sqlmesh_join_key", "t"."key" AS "t__key", "t"."value" AS "t__value", "t"."__sqlmesh_join_key" AS "t____sqlmesh_join_key", CASE WHEN NOT "s"."key" IS NULL THEN 1 ELSE 0 END AS "s_exists", CASE WHEN NOT "t"."key" IS NULL THEN 1 ELSE 0 END AS "t_exists", CASE WHEN "s"."__sqlmesh_join_key" = "t"."__sqlmesh_join_key" AND (NOT "s"."key" IS NULL AND NOT "t"."key" IS NULL) THEN 1 ELSE 0 END AS "row_joined", CASE WHEN "s"."key" IS NULL AND "t"."key" IS NULL THEN 1 ELSE 0 END AS "null_grain", CASE WHEN "s"."key" = "t"."key" THEN 1 WHEN ("s"."key" IS NULL) AND ("t"."key" IS NULL) THEN 1 WHEN ("s"."key" IS NULL) OR ("t"."key" IS NULL) THEN 0 ELSE 0 END AS "key_matches", CASE WHEN ROUND("s"."value", 3) = ROUND("t"."value", 3) THEN 1 WHEN ("s"."value" IS NULL) AND ("t"."value" IS NULL) THEN 1 WHEN ("s"."value" IS NULL) OR ("t"."value" IS NULL) THEN 0 ELSE 0 END AS "value_matches" FROM "__source" AS "s" FULL JOIN "__target" AS "t" ON "s"."__sqlmesh_join_key" = "t"."__sqlmesh_join_key") SELECT *, CASE WHEN "key_matches" = 1 AND "value_matches" = 1 THEN 1 ELSE 0 END AS "row_full_match" FROM "__stats"'
|
304 | 315 | spy_execute.assert_any_call(query_sql_where)
|
| 316 | + |
| 317 | + |
| 318 | +@pytest.mark.slow |
| 319 | +def test_tables_and_grain_inferred_from_model(sushi_context_fixed_date: Context): |
| 320 | + (sushi_context_fixed_date.path / "models" / "waiter_revenue_by_day.sql").write_text(""" |
| 321 | + MODEL ( |
| 322 | + name sushi.waiter_revenue_by_day, |
| 323 | + kind incremental_by_time_range ( |
| 324 | + time_column event_date, |
| 325 | + batch_size 10, |
| 326 | + ), |
| 327 | + owner jen, |
| 328 | + cron '@daily', |
| 329 | + audits ( |
| 330 | + NUMBER_OF_ROWS(threshold := 0) |
| 331 | + ), |
| 332 | + grain (waiter_id, event_date) |
| 333 | + ); |
| 334 | +
|
| 335 | + SELECT |
| 336 | + o.waiter_id::INT + 1 AS waiter_id, /* Waiter id */ |
| 337 | + SUM(oi.quantity * i.price)::DOUBLE AS revenue, /* Revenue from orders taken by this waiter */ |
| 338 | + o.event_date::DATE AS event_date /* Date */ |
| 339 | + FROM sushi.orders AS o |
| 340 | + LEFT JOIN sushi.order_items AS oi |
| 341 | + ON o.id = oi.order_id AND o.event_date = oi.event_date |
| 342 | + LEFT JOIN sushi.items AS i |
| 343 | + ON oi.item_id = i.id AND oi.event_date = i.event_date |
| 344 | + WHERE |
| 345 | + o.event_date BETWEEN @start_date AND @end_date |
| 346 | + GROUP BY |
| 347 | + o.waiter_id, |
| 348 | + o.event_date |
| 349 | +""") |
| 350 | + # this creates a dev preview of "sushi.waiter_revenue_by_day" |
| 351 | + sushi_context_fixed_date.refresh() |
| 352 | + sushi_context_fixed_date.auto_categorize_changes = CategorizerConfig( |
| 353 | + sql=AutoCategorizationMode.FULL |
| 354 | + ) |
| 355 | + sushi_context_fixed_date.plan(environment="unit_test", auto_apply=True, include_unmodified=True) |
| 356 | + |
| 357 | + table_diff = sushi_context_fixed_date.table_diff( |
| 358 | + source="unit_test", target="prod", model_or_snapshot="sushi.waiter_revenue_by_day" |
| 359 | + ) |
| 360 | + |
| 361 | + assert table_diff.source == "memory.sushi__unit_test.waiter_revenue_by_day" |
| 362 | + assert table_diff.target == "memory.sushi.waiter_revenue_by_day" |
| 363 | + |
| 364 | + _, _, col_names = table_diff.key_columns |
| 365 | + assert col_names == ["waiter_id", "event_date"] |
0 commit comments