From 57062f759c269bf3698cef84fc6a2d2853d1e114 Mon Sep 17 00:00:00 2001 From: Tori Wei Date: Tue, 23 Sep 2025 10:24:03 -0700 Subject: [PATCH 1/7] handle duplicate audit names --- sqlmesh/dbt/loader.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sqlmesh/dbt/loader.py b/sqlmesh/dbt/loader.py index f7d97e74c8..034c4cccd5 100644 --- a/sqlmesh/dbt/loader.py +++ b/sqlmesh/dbt/loader.py @@ -172,7 +172,17 @@ def _load_audits( for test in package.tests.values(): logger.debug("Converting '%s' to sqlmesh format", test.name) try: - audits[test.name] = test.to_sqlmesh(package_context) + sqlmesh_audit = test.to_sqlmesh(package_context) + qualified_name = f"{package.name}.{test.name}" + is_from_dbt_packages = "dbt_packages" in str(test.path) + + if is_from_dbt_packages: + audits[qualified_name] = sqlmesh_audit + if test.name not in audits: + audits[test.name] = sqlmesh_audit + else: + audits[test.name] = sqlmesh_audit + except BaseMissingReferenceError as e: ref_type = "model" if isinstance(e, MissingModelError) else "source" logger.warning( From 55f9c37ba6e42a2c427db9cb14d975fe0e51bf32 Mon Sep 17 00:00:00 2001 From: Tori Wei Date: Tue, 23 Sep 2025 15:23:06 -0700 Subject: [PATCH 2/7] update --- sqlmesh/dbt/loader.py | 12 ++---------- sqlmesh/dbt/test.py | 3 +++ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/sqlmesh/dbt/loader.py b/sqlmesh/dbt/loader.py index 034c4cccd5..41a5ecf1f4 100644 --- a/sqlmesh/dbt/loader.py +++ b/sqlmesh/dbt/loader.py @@ -172,16 +172,8 @@ def _load_audits( for test in package.tests.values(): logger.debug("Converting '%s' to sqlmesh format", test.name) try: - sqlmesh_audit = test.to_sqlmesh(package_context) - qualified_name = f"{package.name}.{test.name}" - is_from_dbt_packages = "dbt_packages" in str(test.path) - - if is_from_dbt_packages: - audits[qualified_name] = sqlmesh_audit - if test.name not in audits: - audits[test.name] = sqlmesh_audit - else: - audits[test.name] = sqlmesh_audit + audit = test.to_sqlmesh(package_context) + audits[audit.name] = audit except BaseMissingReferenceError as e: ref_type = "model" if isinstance(e, MissingModelError) else "source" diff --git a/sqlmesh/dbt/test.py b/sqlmesh/dbt/test.py index 747c9d469c..6ecefc809d 100644 --- a/sqlmesh/dbt/test.py +++ b/sqlmesh/dbt/test.py @@ -154,6 +154,9 @@ def to_sqlmesh(self, context: DbtContext) -> Audit: blocking = self.severity == Severity.ERROR audit: Audit + if self.package_name != context.project_name: + self.name = f"{self.package_name}.{self.name}" + if self.is_standalone: jinja_macros.add_globals({"this": self.relation_info}) audit = StandaloneAudit( From 53c4f6a1f4c22cae6cc04553d35a5293fb37c352 Mon Sep 17 00:00:00 2001 From: Tori Wei Date: Tue, 23 Sep 2025 15:46:28 -0700 Subject: [PATCH 3/7] only append package name if package name exists --- sqlmesh/dbt/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmesh/dbt/test.py b/sqlmesh/dbt/test.py index 6ecefc809d..5222da8cd7 100644 --- a/sqlmesh/dbt/test.py +++ b/sqlmesh/dbt/test.py @@ -154,7 +154,7 @@ def to_sqlmesh(self, context: DbtContext) -> Audit: blocking = self.severity == Severity.ERROR audit: Audit - if self.package_name != context.project_name: + if self.package_name and self.package_name != context.project_name: self.name = f"{self.package_name}.{self.name}" if self.is_standalone: From e8297d58d2be8e735ca734c97c65922aaf7a4d1a Mon Sep 17 00:00:00 2001 From: Tori Wei Date: Tue, 23 Sep 2025 16:27:01 -0700 Subject: [PATCH 4/7] change variable --- sqlmesh/dbt/test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sqlmesh/dbt/test.py b/sqlmesh/dbt/test.py index 5222da8cd7..57f6e2f7c9 100644 --- a/sqlmesh/dbt/test.py +++ b/sqlmesh/dbt/test.py @@ -154,13 +154,14 @@ def to_sqlmesh(self, context: DbtContext) -> Audit: blocking = self.severity == Severity.ERROR audit: Audit + audit_name = self.name if self.package_name and self.package_name != context.project_name: - self.name = f"{self.package_name}.{self.name}" + audit_name = f"{self.package_name}.{self.name}" if self.is_standalone: jinja_macros.add_globals({"this": self.relation_info}) audit = StandaloneAudit( - name=self.name, + name=audit_name, dbt_node_info=self.node_info, dialect=self.dialect(context), skip=skip, @@ -177,7 +178,7 @@ def to_sqlmesh(self, context: DbtContext) -> Audit: ) else: audit = ModelAudit( - name=self.name, + name=audit_name, dbt_node_info=self.node_info, dialect=self.dialect(context), skip=skip, From 5c0660ad8c8cc831e2fbbd2fce41803011dad761 Mon Sep 17 00:00:00 2001 From: Tori Wei Date: Wed, 24 Sep 2025 10:33:50 -0700 Subject: [PATCH 5/7] create and use canonical_name for TestConfig --- sqlmesh/dbt/basemodel.py | 2 +- sqlmesh/dbt/loader.py | 3 +-- sqlmesh/dbt/test.py | 11 ++++++----- tests/dbt/test_model.py | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sqlmesh/dbt/basemodel.py b/sqlmesh/dbt/basemodel.py index 4637bbf91c..4dcf44a0af 100644 --- a/sqlmesh/dbt/basemodel.py +++ b/sqlmesh/dbt/basemodel.py @@ -305,7 +305,7 @@ def sqlmesh_model_kwargs( jinja_macros.add_globals(self._model_jinja_context(model_context, dependencies)) model_kwargs = { - "audits": [(test.name, {}) for test in self.tests], + "audits": [(test.canonical_name, {}) for test in self.tests], "column_descriptions": column_descriptions_to_sqlmesh(self.columns) or None, "depends_on": { model.canonical_name(context) for model in model_context.refs.values() diff --git a/sqlmesh/dbt/loader.py b/sqlmesh/dbt/loader.py index 41a5ecf1f4..eb117a3e40 100644 --- a/sqlmesh/dbt/loader.py +++ b/sqlmesh/dbt/loader.py @@ -172,8 +172,7 @@ def _load_audits( for test in package.tests.values(): logger.debug("Converting '%s' to sqlmesh format", test.name) try: - audit = test.to_sqlmesh(package_context) - audits[audit.name] = audit + audits[test.canonical_name] = test.to_sqlmesh(package_context) except BaseMissingReferenceError as e: ref_type = "model" if isinstance(e, MissingModelError) else "source" diff --git a/sqlmesh/dbt/test.py b/sqlmesh/dbt/test.py index 57f6e2f7c9..8908f416cc 100644 --- a/sqlmesh/dbt/test.py +++ b/sqlmesh/dbt/test.py @@ -109,6 +109,10 @@ def _validate_severity(cls, v: t.Union[Severity, str]) -> Severity: def _lowercase_name(cls, v: str) -> str: return v.lower() + @property + def canonical_name(self) -> str: + return f"{self.package_name}.{self.name}" if self.package_name else self.name + @property def is_standalone(self) -> bool: # A test is standalone if: @@ -154,14 +158,11 @@ def to_sqlmesh(self, context: DbtContext) -> Audit: blocking = self.severity == Severity.ERROR audit: Audit - audit_name = self.name - if self.package_name and self.package_name != context.project_name: - audit_name = f"{self.package_name}.{self.name}" if self.is_standalone: jinja_macros.add_globals({"this": self.relation_info}) audit = StandaloneAudit( - name=audit_name, + name=self.name, dbt_node_info=self.node_info, dialect=self.dialect(context), skip=skip, @@ -178,7 +179,7 @@ def to_sqlmesh(self, context: DbtContext) -> Audit: ) else: audit = ModelAudit( - name=audit_name, + name=self.name, dbt_node_info=self.node_info, dialect=self.dialect(context), skip=skip, diff --git a/tests/dbt/test_model.py b/tests/dbt/test_model.py index a64b29e89d..d212872cb7 100644 --- a/tests/dbt/test_model.py +++ b/tests/dbt/test_model.py @@ -190,23 +190,23 @@ def test_manifest_filters_standalone_tests_from_models( # Should only have "not_null" test, not the "relationships" test model1_audit_names = [audit[0] for audit in model1_snapshot.model.audits] assert len(model1_audit_names) == 1 - assert model1_audit_names[0] == "not_null_model1_id" + assert model1_audit_names[0] == "local.not_null_model1_id" # Verify model2 has its non-standalone test model2_audit_names = [audit[0] for audit in model2_snapshot.model.audits] assert len(model2_audit_names) == 1 - assert model2_audit_names[0] == "not_null_model2_id" + assert model2_audit_names[0] == "local.not_null_model2_id" # Verify the standalone test (relationships) exists as a StandaloneAudit all_non_standalone_audits = [name for name in context._audits] assert sorted(all_non_standalone_audits) == [ - "not_null_model1_id", - "not_null_model2_id", + "local.not_null_model1_id", + "local.not_null_model2_id", ] standalone_audits = [name for name in context._standalone_audits] assert len(standalone_audits) == 1 - assert standalone_audits[0] == "relationships_model1_id__id__ref_model2_" + assert standalone_audits[0] == "local.relationships_model1_id__id__ref_model2_" plan_builder = context.plan_builder() dag = plan_builder._build_dag() From 8c842d1c527b95f839a4fc6480562c044192292b Mon Sep 17 00:00:00 2001 From: Tori Wei Date: Wed, 24 Sep 2025 10:36:27 -0700 Subject: [PATCH 6/7] style fix --- sqlmesh/dbt/test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sqlmesh/dbt/test.py b/sqlmesh/dbt/test.py index 8908f416cc..1bd8a8e6e2 100644 --- a/sqlmesh/dbt/test.py +++ b/sqlmesh/dbt/test.py @@ -158,7 +158,6 @@ def to_sqlmesh(self, context: DbtContext) -> Audit: blocking = self.severity == Severity.ERROR audit: Audit - if self.is_standalone: jinja_macros.add_globals({"this": self.relation_info}) audit = StandaloneAudit( From 64d2a37a8e66a663480875da315e7d92469619df Mon Sep 17 00:00:00 2001 From: Tori Wei Date: Wed, 24 Sep 2025 11:27:22 -0700 Subject: [PATCH 7/7] fix test --- tests/core/integration/test_dbt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/integration/test_dbt.py b/tests/core/integration/test_dbt.py index 5e600899dd..6f23acb97e 100644 --- a/tests/core/integration/test_dbt.py +++ b/tests/core/integration/test_dbt.py @@ -48,7 +48,7 @@ def test_dbt_is_incremental_table_is_missing(sushi_test_dbt_context: Context): model = context.get_model("sushi.waiter_revenue_by_day_v2") model = model.copy(update={"kind": IncrementalUnmanagedKind(), "start": "2023-01-01"}) context.upsert_model(model) - context._standalone_audits["test_top_waiters"].start = "2023-01-01" + context._standalone_audits["sushi.test_top_waiters"].start = "2023-01-01" context.plan("prod", auto_apply=True, no_prompts=True, skip_tests=True)