Skip to content

Commit 424f25b

Browse files
jontsaiclaude
andcommitted
test: add comprehensive tests for catalog extra fields handling
Add test suite to verify that the Metadata class correctly handles extra fields from dbt, preventing job failures when dbt adds new fields. Tests cover: - Extra fields are accepted without validation errors - Extra fields are stored in __pydantic_extra__ - model_dump() includes extra fields - Specific case of invocation_started_at field - Various combinations of known and unknown fields 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent d7aa752 commit 424f25b

File tree

4 files changed

+128
-0
lines changed

4 files changed

+128
-0
lines changed

tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""Root pytest configuration."""
2+
import sys
3+
from pathlib import Path
4+
5+
# Add src directory to Python path for all tests
6+
src_path = Path(__file__).parent.parent / "src"
7+
if str(src_path) not in sys.path:
8+
sys.path.insert(0, str(src_path))

tests/vendor/__init__.py

Whitespace-only changes.

tests/vendor/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Pytest configuration for vendor tests."""
2+
import sys
3+
from pathlib import Path
4+
5+
# Add src directory to Python path
6+
src_path = Path(__file__).parent.parent.parent / "src"
7+
sys.path.insert(0, str(src_path))

tests/vendor/test_catalog_v1.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Tests for catalog v1 parser, specifically testing extra fields handling."""
2+
import pytest
3+
4+
from vendor.dbt_artifacts_parser.parsers.catalog.catalog_v1 import Metadata
5+
6+
7+
class TestMetadataExtraFields:
8+
"""Test that Metadata class accepts extra fields from dbt."""
9+
10+
def test_metadata_accepts_extra_fields(self):
11+
"""Test that metadata accepts fields not explicitly defined in the model."""
12+
# Test with a new field that dbt might add in the future
13+
data = {
14+
"dbt_schema_version": "https://schemas.getdbt.com/dbt/catalog/v1.json",
15+
"dbt_version": "1.9.0",
16+
"generated_at": "2025-11-05T10:00:00Z",
17+
"invocation_id": "test-invocation-123",
18+
"invocation_started_at": "2025-11-05T09:59:00Z", # New field
19+
"new_future_field": "some_value", # Another potential future field
20+
}
21+
22+
# This should not raise a validation error
23+
metadata = Metadata(**data)
24+
25+
# Verify that known fields are accessible normally
26+
assert metadata.dbt_schema_version == "https://schemas.getdbt.com/dbt/catalog/v1.json"
27+
assert metadata.dbt_version == "1.9.0"
28+
assert metadata.generated_at == "2025-11-05T10:00:00Z"
29+
assert metadata.invocation_id == "test-invocation-123"
30+
31+
def test_metadata_extra_fields_in_pydantic_extra(self):
32+
"""Test that extra fields are stored in __pydantic_extra__."""
33+
data = {
34+
"dbt_version": "1.9.0",
35+
"invocation_started_at": "2025-11-05T09:59:00Z",
36+
"new_field_1": "value1",
37+
"new_field_2": 123,
38+
}
39+
40+
metadata = Metadata(**data)
41+
42+
# Extra fields should be stored in __pydantic_extra__
43+
assert metadata.__pydantic_extra__ is not None
44+
assert "invocation_started_at" in metadata.__pydantic_extra__
45+
assert "new_field_1" in metadata.__pydantic_extra__
46+
assert "new_field_2" in metadata.__pydantic_extra__
47+
assert metadata.__pydantic_extra__["invocation_started_at"] == "2025-11-05T09:59:00Z"
48+
assert metadata.__pydantic_extra__["new_field_1"] == "value1"
49+
assert metadata.__pydantic_extra__["new_field_2"] == 123
50+
51+
def test_metadata_model_dump_includes_extra_fields(self):
52+
"""Test that model_dump() includes extra fields."""
53+
data = {
54+
"dbt_version": "1.9.0",
55+
"invocation_id": "test-123",
56+
"invocation_started_at": "2025-11-05T09:59:00Z",
57+
"future_field": "future_value",
58+
}
59+
60+
metadata = Metadata(**data)
61+
dumped = metadata.model_dump()
62+
63+
# All fields including extra should be in the dump
64+
assert dumped["dbt_version"] == "1.9.0"
65+
assert dumped["invocation_id"] == "test-123"
66+
assert dumped["invocation_started_at"] == "2025-11-05T09:59:00Z"
67+
assert dumped["future_field"] == "future_value"
68+
69+
def test_metadata_with_no_extra_fields(self):
70+
"""Test that metadata works normally when no extra fields are provided."""
71+
data = {
72+
"dbt_version": "1.9.0",
73+
"generated_at": "2025-11-05T10:00:00Z",
74+
}
75+
76+
metadata = Metadata(**data)
77+
78+
assert metadata.dbt_version == "1.9.0"
79+
assert metadata.generated_at == "2025-11-05T10:00:00Z"
80+
81+
def test_metadata_with_only_extra_fields(self):
82+
"""Test that metadata accepts data with only extra fields (all known fields are Optional)."""
83+
data = {
84+
"some_new_field": "value",
85+
"another_new_field": 42,
86+
}
87+
88+
# This should work since all defined fields are Optional
89+
metadata = Metadata(**data)
90+
91+
assert metadata.__pydantic_extra__["some_new_field"] == "value"
92+
assert metadata.__pydantic_extra__["another_new_field"] == 42
93+
94+
def test_invocation_started_at_as_extra_field(self):
95+
"""Test the specific case of invocation_started_at being handled as an extra field."""
96+
# This is the real-world scenario: dbt adds invocation_started_at
97+
data = {
98+
"dbt_schema_version": "https://schemas.getdbt.com/dbt/catalog/v1.json",
99+
"dbt_version": "1.9.0",
100+
"generated_at": "2025-11-05T10:00:00Z",
101+
"invocation_id": "abc-123-def-456",
102+
"invocation_started_at": "2025-11-05T09:55:30.123456Z",
103+
}
104+
105+
# Should not raise ValidationError
106+
metadata = Metadata(**data)
107+
108+
# The field should be accessible via __pydantic_extra__
109+
assert metadata.__pydantic_extra__["invocation_started_at"] == "2025-11-05T09:55:30.123456Z"
110+
111+
# And should be included in model_dump()
112+
dumped = metadata.model_dump()
113+
assert dumped["invocation_started_at"] == "2025-11-05T09:55:30.123456Z"

0 commit comments

Comments
 (0)