|
5 | 5 | from tempfile import TemporaryDirectory |
6 | 6 | from unittest.mock import PropertyMock, call, patch |
7 | 7 |
|
| 8 | +import freezegun |
8 | 9 | import pytest |
9 | 10 | from pytest_mock.plugin import MockerFixture |
10 | 11 | from sqlglot import parse_one |
11 | 12 | from sqlglot.errors import SchemaError |
12 | 13 |
|
13 | 14 | import sqlmesh.core.constants |
| 15 | +import sqlmesh.core.dialect as d |
14 | 16 | from sqlmesh.core.config import ( |
15 | 17 | Config, |
16 | 18 | DuckDBConnectionConfig, |
|
24 | 26 | from sqlmesh.core.environment import Environment |
25 | 27 | from sqlmesh.core.model import load_sql_based_model |
26 | 28 | from sqlmesh.core.plan import BuiltInPlanEvaluator, PlanBuilder |
27 | | -from sqlmesh.utils.date import make_inclusive_end, now, to_date, yesterday_ds |
| 29 | +from sqlmesh.utils.date import ( |
| 30 | + make_inclusive_end, |
| 31 | + now, |
| 32 | + to_date, |
| 33 | + to_timestamp, |
| 34 | + yesterday_ds, |
| 35 | +) |
28 | 36 | from sqlmesh.utils.errors import ConfigError |
29 | 37 | from tests.utils.test_filesystem import create_temp_file |
30 | 38 |
|
@@ -482,6 +490,42 @@ def test_plan_default_end(sushi_context_pre_scheduling: Context): |
482 | 490 | assert forward_only_dev_plan.start == plan_end |
483 | 491 |
|
484 | 492 |
|
| 493 | +@pytest.mark.slow |
| 494 | +def test_plan_start_ahead_of_end(copy_to_temp_path): |
| 495 | + path = copy_to_temp_path("examples/sushi") |
| 496 | + with freezegun.freeze_time("2024-01-02 00:00:00"): |
| 497 | + context = Context(paths=path, config="local_config") |
| 498 | + context.plan("prod", no_prompts=True, auto_apply=True) |
| 499 | + assert context.state_sync.max_interval_end_for_environment("prod") == to_timestamp( |
| 500 | + "2024-01-02" |
| 501 | + ) |
| 502 | + context.close() |
| 503 | + with freezegun.freeze_time("2024-01-03 00:00:00"): |
| 504 | + context = Context(paths=path, config="local_config") |
| 505 | + expression = d.parse( |
| 506 | + """ |
| 507 | + MODEL( |
| 508 | + name sushi.hourly, |
| 509 | + KIND FULL, |
| 510 | + cron '@hourly', |
| 511 | + start '2024-01-02 12:00:00', |
| 512 | + ); |
| 513 | +
|
| 514 | + SELECT 1""" |
| 515 | + ) |
| 516 | + model = load_sql_based_model(expression, default_catalog=context.default_catalog) |
| 517 | + context.upsert_model(model) |
| 518 | + context.plan("prod", no_prompts=True, auto_apply=True) |
| 519 | + # Since the new start is ahead of the latest end loaded for prod, the table is deployed as empty |
| 520 | + # This isn't considered a gap since prod has not loaded these intervals yet |
| 521 | + # As a results the max interval end is unchanged and the table is empty |
| 522 | + assert context.state_sync.max_interval_end_for_environment("prod") == to_timestamp( |
| 523 | + "2024-01-02" |
| 524 | + ) |
| 525 | + assert context.engine_adapter.fetchone("SELECT COUNT(*) FROM sushi.hourly")[0] == 0 |
| 526 | + context.close() |
| 527 | + |
| 528 | + |
485 | 529 | @pytest.mark.slow |
486 | 530 | def test_schema_error_no_default(sushi_context_pre_scheduling) -> None: |
487 | 531 | context = sushi_context_pre_scheduling |
|
0 commit comments