mikebridge commented on code in PR #45395:
URL: https://github.com/apache/superset/pull/45395#discussion_r4344613589


##########
superset-core/tests/semantic_layers/conformance_provider.py:
##########
@@ -0,0 +1,237 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Test-only SQLite adapter for the conformance examples; no external 
service."""
+
+import sqlite3
+from contextlib import closing
+from datetime import date
+from typing import Any
+
+import pyarrow as pa
+from example_provider import ExampleView
+from superset_core.semantic_layers.errors import (
+    SemanticQueryErrorCode,
+    SemanticQueryRejectedError,
+    SemanticResultCompletenessError,
+    SemanticResultCompletenessReason,
+)
+from superset_core.semantic_layers.types import (
+    Dimension,
+    Filter,
+    FilterExpression,
+    Grains,
+    Metric,
+    Operator,
+    OrFilter,
+    SemanticQuery,
+    SemanticResult,
+)
+from superset_core.semantic_layers.view import SemanticViewFeature
+
+
+class Backend:
+    """Controlled backend responses and a changing metadata catalog."""
+
+    def __init__(self) -> None:
+        self.revision: int = 0
+        self.fault: bool = False
+        self.completeness: SemanticResultCompletenessReason | None = None
+
+    def execute(self, sql: str, parameters: list[object]) -> list[tuple[Any, 
...]]:
+        """Execute against deliberately unsorted data, including a NULL 
group."""
+        if self.fault:
+            raise ConnectionError("test backend unavailable")
+        connection: sqlite3.Connection
+        with closing(sqlite3.connect(":memory:")) as connection:
+            connection.execute(
+                "CREATE TABLE records(category TEXT, occurred TEXT, amount 
INT)"
+            )
+            connection.executemany(
+                "INSERT INTO records VALUES (?, ?, ?)",
+                [
+                    ("B", "2026-02-04", 8),

Review Comment:
   Fixed in e088fd1ed70d95d00be72a32abc2a2401cc97120. The seed includes a 
February C row with amount 6, which satisfies the outer bound but must be 
excluded by the OR group. The correct result stays NULL=1, A=5, B=8; dropping 
the group adds C=6. The new mutation regression executes the real SQLite 
provider and failed before this seed change. The other expected totals have 
been recalculated independently.



##########
superset-core/src/superset_core/semantic_layers/testing.py:
##########
@@ -59,3 +79,313 @@ def semantic_layer_registration(monkeypatch: 
pytest.MonkeyPatch) -> None:
     integration lane. Use in isolated provider test processes, not a live host.
     """
     monkeypatch.setattr(decorators, "semantic_layer", _registration)
+
+
+# Keep test data and provider construction in the provider's own fixtures. This
+# suite consumes the real SDK types rather than a copy of a factory signature.
+class SemanticProviderConformance:
+    """Subclass in a ``Test...`` class and supply the documented provider 
fixtures.
+
+    Query fixtures seed discriminating data and provide independent expected
+    Arrow tables for identity, grain, NULL, grouped OR, and order-before-limit.
+    Error fixtures call real provider methods against controlled backend 
outcomes.
+    See the provider author guide for every fixture and the limits of this 
suite.
+    """
+
+    def test_catalog_identity(
+        self, semantic_view: SemanticView, semantic_reloaded_view: SemanticView
+    ) -> None:
+        """Stable selections survive reloads without ambiguous catalog 
members."""
+        assert semantic_view.uid()
+        assert semantic_view.uid() == semantic_reloaded_view.uid()
+        assert _catalog(semantic_view) == _catalog(semantic_reloaded_view)
+
+    @pytest.mark.parametrize(
+        "case", ["identity", "grain", "null", "not_null", "or", "order_limit"]
+    )
+    def test_query_outputs(
+        self,
+        case: str,
+        semantic_view: SemanticView,
+        semantic_query_cases: Mapping[str, tuple[SemanticQuery, pa.Table]],
+    ) -> None:
+        """Compare executed results, including column names and row order."""
+        query: SemanticQuery
+        expected: pa.Table
+        query, expected = semantic_query_cases[case]
+        assert expected.num_rows > 0, (
+            f"{case} must distinguish a correct result from no data"
+        )
+        assert query.order or expected.num_rows == 1, (
+            f"{case}: multi-row fixtures need explicit order"
+        )
+        assert set(query.dimensions) <= semantic_view.get_dimensions()
+        assert set(query.metrics) <= semantic_view.get_metrics()
+        if case == "grain":
+            assert any(dimension.grain is not None for dimension in 
query.dimensions)

Review Comment:
   Agreed; fixed in e088fd1ed70d95d00be72a32abc2a2401cc97120. The grain case 
skips with an explicit reason when the view declares no grain-bearing 
dimension, before looking up the grain fixture entry or executing a query. Such 
providers can omit that entry without inventing a temporal dimension. Views 
that declare a grain still run the existing check, and the guide describes the 
skip as unexercised coverage. Public SDK types and fixture names are unchanged.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to