Skip to content

Commit 59d44eb

Browse files
authored
feat: dbt adapter allow invalid source ref for tests (#5259)
1 parent d53a58e commit 59d44eb

File tree

5 files changed

+33
-15
lines changed

5 files changed

+33
-15
lines changed

sqlmesh/dbt/basemodel.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ def tests_ref_source_dependencies(self) -> Dependencies:
247247

248248
def remove_tests_with_invalid_refs(self, context: DbtContext) -> None:
249249
"""
250-
Removes tests that reference models that do not exist in the context in order to match dbt behavior.
250+
Removes tests that reference models or sources that do not exist in the context in order to match dbt behavior.
251251
252252
Args:
253253
context: The dbt context this model resides within.
@@ -259,6 +259,7 @@ def remove_tests_with_invalid_refs(self, context: DbtContext) -> None:
259259
test
260260
for test in self.tests
261261
if all(ref in context.refs for ref in test.dependencies.refs)
262+
and all(source in context.sources for source in test.dependencies.sources)
262263
]
263264

264265
def check_for_circular_test_refs(self, context: DbtContext) -> None:

sqlmesh/dbt/context.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from sqlmesh.dbt.manifest import ManifestHelper
1313
from sqlmesh.dbt.target import TargetConfig
1414
from sqlmesh.utils import AttributeDict
15-
from sqlmesh.utils.errors import ConfigError, SQLMeshError, MissingModelError
15+
from sqlmesh.utils.errors import ConfigError, SQLMeshError, MissingModelError, MissingSourceError
1616
from sqlmesh.utils.jinja import (
1717
JinjaGlobalAttribute,
1818
JinjaMacroRegistry,
@@ -266,14 +266,13 @@ def context_for_dependencies(self, dependencies: Dependencies) -> DbtContext:
266266
else:
267267
models[ref] = t.cast(ModelConfig, model)
268268
else:
269-
exception = MissingModelError(ref)
270-
raise exception
269+
raise MissingModelError(ref)
271270

272271
for source in dependencies.sources:
273272
if source in self.sources:
274273
sources[source] = self.sources[source]
275274
else:
276-
raise ConfigError(f"Source '{source}' was not found.")
275+
raise MissingSourceError(source)
277276

278277
variables = {k: v for k, v in self.variables.items() if k in dependencies.variables}
279278

sqlmesh/dbt/loader.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from sqlmesh.dbt.project import Project
2424
from sqlmesh.dbt.target import TargetConfig
2525
from sqlmesh.utils import UniqueKeyDict
26-
from sqlmesh.utils.errors import ConfigError, MissingModelError
26+
from sqlmesh.utils.errors import ConfigError, MissingModelError, BaseMissingReferenceError
2727
from sqlmesh.utils.jinja import (
2828
JinjaMacroRegistry,
2929
make_jinja_registry,
@@ -161,11 +161,13 @@ def _load_audits(
161161
logger.debug("Converting '%s' to sqlmesh format", test.name)
162162
try:
163163
audits[test.name] = test.to_sqlmesh(package_context)
164-
except MissingModelError as e:
164+
except BaseMissingReferenceError as e:
165+
ref_type = "model" if isinstance(e, MissingModelError) else "source"
165166
logger.warning(
166-
"Skipping audit '%s' because model '%s' is not a valid ref",
167+
"Skipping audit '%s' because %s '%s' is not a valid ref",
167168
test.name,
168-
e.model_name,
169+
ref_type,
170+
e.ref,
169171
)
170172

171173
return audits

sqlmesh/utils/errors.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,17 @@ def __init__(self, message: str | Exception, location: t.Optional[Path] = None)
3333
self.location = Path(location) if isinstance(location, str) else location
3434

3535

36-
class MissingModelError(ConfigError):
36+
class BaseMissingReferenceError(ConfigError):
37+
def __init__(self, ref: str) -> None:
38+
self.ref = ref
39+
40+
41+
class MissingModelError(BaseMissingReferenceError):
3742
"""Raised when a model that is referenced is missing."""
3843

39-
def __init__(self, model_name: str) -> None:
40-
self.model_name = model_name
41-
super().__init__(f"Model '{model_name}' was not found.")
44+
45+
class MissingSourceError(BaseMissingReferenceError):
46+
"""Raised when a source that is referenced is missing."""
4247

4348

4449
class MissingDependencyError(SQLMeshError):

tests/dbt/test_model.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,15 @@ def test_load_invalid_ref_audit_constraints(
8787
"relationships": {
8888
"to": "ref('not_real_model')",
8989
"field": "cola",
90-
}
91-
}
90+
},
91+
},
92+
{
93+
# Reference a source that doesn't exist
94+
"relationships": {
95+
"to": "source('not_real_source', 'not_real_table')",
96+
"field": "cola",
97+
},
98+
},
9299
],
93100
}
94101
],
@@ -134,6 +141,10 @@ def test_load_invalid_ref_audit_constraints(
134141
"Skipping audit 'relationships_full_model_cola__cola__ref_not_real_model_' because model 'not_real_model' is not a valid ref"
135142
in caplog.text
136143
)
144+
assert (
145+
"Skipping audit 'relationships_full_model_cola__cola__source_not_real_source_not_real_table_' because source 'not_real_source.not_real_table' is not a valid ref"
146+
in caplog.text
147+
)
137148
fqn = '"local"."main"."full_model"'
138149
assert fqn in context.snapshots
139150
# The audit isn't loaded due to the invalid ref

0 commit comments

Comments
 (0)