|
| 1 | +"""Tests for run_results v6 parser, specifically the resilient run `Status` enum. |
| 2 | +
|
| 3 | +Regression coverage for dbt 2.0 emitting ``status="reused"`` (and future unknown |
| 4 | +statuses), which previously raised a ``ValidationError`` and caused the entire |
| 5 | +run_results.json to be silently dropped during ingestion. |
| 6 | +""" |
| 7 | +from vendor.dbt_artifacts_parser.parser import parse_run_results |
| 8 | +from vendor.dbt_artifacts_parser.parsers.run_results.run_results_v6 import Result |
| 9 | +from vendor.dbt_artifacts_parser.parsers.run_results.run_results_v6 import Status |
| 10 | + |
| 11 | +V6_SCHEMA = "https://schemas.getdbt.com/dbt/run-results/v6.json" |
| 12 | + |
| 13 | + |
| 14 | +def _result(status: str, unique_id: str) -> dict: |
| 15 | + return { |
| 16 | + "status": status, |
| 17 | + "timing": [], |
| 18 | + "thread_id": "Thread-1", |
| 19 | + "execution_time": 0.1, |
| 20 | + "adapter_response": {}, |
| 21 | + "unique_id": unique_id, |
| 22 | + } |
| 23 | + |
| 24 | + |
| 25 | +def _run_results(*statuses: str) -> dict: |
| 26 | + return { |
| 27 | + "metadata": { |
| 28 | + "dbt_schema_version": V6_SCHEMA, |
| 29 | + "dbt_version": "2.0.0", |
| 30 | + "invocation_id": "test-invocation-123", |
| 31 | + }, |
| 32 | + "elapsed_time": 1.5, |
| 33 | + "args": {}, |
| 34 | + "results": [_result(s, f"model.proj.m{i}") for i, s in enumerate(statuses)], |
| 35 | + } |
| 36 | + |
| 37 | + |
| 38 | +class TestRunResultStatus: |
| 39 | + """The run `Status` enum must accept new/unknown dbt statuses without failing.""" |
| 40 | + |
| 41 | + def test_reused_status_parses(self): |
| 42 | + """dbt 2.0 emits `reused` for unchanged models; it must not fail validation.""" |
| 43 | + result = Result(**_result("reused", "model.proj.a")) |
| 44 | + assert result.status.value == "reused" |
| 45 | + assert result.status is Status.reused |
| 46 | + |
| 47 | + def test_known_statuses_preserve_value(self): |
| 48 | + """Known run statuses still resolve with the correct `.value`.""" |
| 49 | + for status in ("success", "error", "skipped", "partial success"): |
| 50 | + assert Result(**_result(status, "model.proj.a")).status.value == status |
| 51 | + |
| 52 | + def test_unknown_future_status_parses(self): |
| 53 | + """Forward-compat: a status dbt has not shipped yet still parses via `_missing_`.""" |
| 54 | + result = Result(**_result("some_future_status", "model.proj.a")) |
| 55 | + assert result.status.value == "some_future_status" |
| 56 | + |
| 57 | + def test_test_and_freshness_statuses_keep_their_value(self): |
| 58 | + """Test/freshness statuses must keep their `.value` (resolved via Status1/Status2).""" |
| 59 | + for status in ("pass", "fail", "warn", "runtime error"): |
| 60 | + assert Result(**_result(status, "test.proj.t")).status.value == status |
| 61 | + |
| 62 | + |
| 63 | +class TestParseRunResultsEntryPoint: |
| 64 | + """The public `parse_run_results` must parse a full file containing `reused`.""" |
| 65 | + |
| 66 | + def test_file_with_reused_result_parses_fully(self): |
| 67 | + run_results = parse_run_results(_run_results("success", "reused", "skipped", "pass", "error", "no-op")) |
| 68 | + # Previously this whole file was dropped because of the single `reused` row. |
| 69 | + assert len(run_results.results) == 6 |
| 70 | + assert {r.status.value for r in run_results.results} == { |
| 71 | + "success", |
| 72 | + "reused", |
| 73 | + "skipped", |
| 74 | + "pass", |
| 75 | + "error", |
| 76 | + "no-op", |
| 77 | + } |
0 commit comments