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


##########
superset-core/src/superset_core/semantic_layers/testing.py:
##########
@@ -59,3 +78,250 @@ 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)
+        elif case in {"null", "not_null"}:
+            operator: Operator = (
+                Operator.IS_NULL if case == "null" else Operator.IS_NOT_NULL
+            )
+            assert any(
+                isinstance(item, Filter) and item.operator == operator
+                for item in query.filters or set()
+            )
+        elif case == "or":
+            assert any(isinstance(item, OrFilter) for item in query.filters or 
set())
+        elif case == "order_limit":
+            assert query.order
+            assert query.limit is not None
+            assert query.limit > 0
+            full: SemanticResult = semantic_view.get_table(
+                replace(query, limit=None, offset=None)
+            )
+            assert full.results.num_rows > query.limit, "seed more rows than 
the limit"
+            assert full.results.slice(query.offset or 0, 
query.limit).equals(expected)
+        result: SemanticResult = semantic_view.get_table(query)
+        assert result.results.equals(expected), (
+            f"{case}: expected {expected.to_pydict()}, got 
{result.results.to_pydict()}"
+        )
+        assert set(result.results.column_names) == {
+            member.name for member in [*query.dimensions, *query.metrics]
+        }
+
+    @pytest.mark.parametrize("method", ["get_table", "get_values", 
"get_row_count"])
+    @pytest.mark.parametrize("kind", ["refusal", "fault", "incomplete", 
"unverified"])
+    def test_error_categories(
+        self,
+        method: str,
+        kind: str,
+        semantic_error_cases: Mapping[str, Callable[[], SemanticResult]],
+    ) -> None:
+        """Refusals and completeness are typed; operational faults stay 
faults."""
+        operation: Callable[[], SemanticResult] = semantic_error_cases[
+            f"{method}:{kind}"
+        ]
+        error: pytest.ExceptionInfo[Exception]
+        # Operational fault classes are provider-owned; classify the caught 
error below.
+        with pytest.raises(Exception) as error:  # noqa: PT011
+            operation()
+        if kind == "refusal":
+            assert isinstance(error.value, SemanticQueryRejectedError)
+            assert isinstance(error.value.code, SemanticQueryErrorCode)
+        elif kind in {"incomplete", "unverified"}:
+            assert isinstance(error.value, SemanticResultCompletenessError)
+            assert error.value.reason == kind
+        else:
+            assert not isinstance(

Review Comment:
   **Non-blocking (vacuous).** The fault lane passes on any exception that is 
not an SDK refusal or completeness error, including `TypeError`, 
`AttributeError`, `KeyError` or `AssertionError` from a provider bug or a 
broken fixture. With the example's fault path changed to `raise 
TypeError("'NoneType' object is not subscriptable")`, all fault cases pass. 
Consider letting the fixture declare the expected fault type, for example 
`{"get_table:fault": (callable, ConnectionError)}`, or at least rejecting 
`TypeError`, `AttributeError`, `NameError` and `AssertionError` here.



##########
superset-core/src/superset_core/semantic_layers/testing.py:
##########
@@ -59,3 +78,250 @@ 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)
+        elif case in {"null", "not_null"}:
+            operator: Operator = (
+                Operator.IS_NULL if case == "null" else Operator.IS_NOT_NULL
+            )
+            assert any(
+                isinstance(item, Filter) and item.operator == operator
+                for item in query.filters or set()
+            )
+        elif case == "or":
+            assert any(isinstance(item, OrFilter) for item in query.filters or 
set())
+        elif case == "order_limit":
+            assert query.order
+            assert query.limit is not None
+            assert query.limit > 0
+            full: SemanticResult = semantic_view.get_table(
+                replace(query, limit=None, offset=None)
+            )
+            assert full.results.num_rows > query.limit, "seed more rows than 
the limit"
+            assert full.results.slice(query.offset or 0, 
query.limit).equals(expected)
+        result: SemanticResult = semantic_view.get_table(query)
+        assert result.results.equals(expected), (
+            f"{case}: expected {expected.to_pydict()}, got 
{result.results.to_pydict()}"
+        )
+        assert set(result.results.column_names) == {
+            member.name for member in [*query.dimensions, *query.metrics]
+        }
+
+    @pytest.mark.parametrize("method", ["get_table", "get_values", 
"get_row_count"])
+    @pytest.mark.parametrize("kind", ["refusal", "fault", "incomplete", 
"unverified"])
+    def test_error_categories(
+        self,
+        method: str,
+        kind: str,
+        semantic_error_cases: Mapping[str, Callable[[], SemanticResult]],
+    ) -> None:
+        """Refusals and completeness are typed; operational faults stay 
faults."""
+        operation: Callable[[], SemanticResult] = semantic_error_cases[
+            f"{method}:{kind}"
+        ]
+        error: pytest.ExceptionInfo[Exception]
+        # Operational fault classes are provider-owned; classify the caught 
error below.
+        with pytest.raises(Exception) as error:  # noqa: PT011
+            operation()
+        if kind == "refusal":
+            assert isinstance(error.value, SemanticQueryRejectedError)
+            assert isinstance(error.value.code, SemanticQueryErrorCode)
+        elif kind in {"incomplete", "unverified"}:
+            assert isinstance(error.value, SemanticResultCompletenessError)
+            assert error.value.reason == kind
+        else:
+            assert not isinstance(
+                error.value,
+                (SemanticQueryRejectedError, SemanticResultCompletenessError),
+            )
+
+    def test_advertised_capabilities(
+        self,
+        semantic_view: SemanticView,
+        semantic_capability_cases: Mapping[str, tuple[SemanticQuery, 
pa.Table]],
+    ) -> None:
+        """Every advertised feature has a result-level witness in this lane."""
+        feature: SemanticViewFeature | str
+        for feature in semantic_view.features:
+            name: str = (
+                feature.value if isinstance(feature, SemanticViewFeature) else 
feature
+            )
+            query: SemanticQuery
+            expected: pa.Table
+            query, expected = semantic_capability_cases[name]
+            assert expected.num_rows > 0
+            assert query.order or expected.num_rows == 1, (
+                f"{name}: multi-row fixtures need explicit order"
+            )
+            if name == "ROW_OFFSET":
+                assert query.offset is not None
+                assert query.offset > 0
+                assert query.limit is not None
+                assert query.limit > 0
+            elif name in {"GROUP_LIMIT", "GROUP_OTHERS"}:

Review Comment:
   **Non-blocking (looser).** Only `ROW_OFFSET` and `GROUP_*` witnesses are 
checked for actually using the feature. For `ADHOC_COLUMN_EXPRESSIONS` and 
`ADHOC_EXPRESSIONS_IN_ORDERBY`, any plain query satisfies the lane, so "every 
advertised feature has a result-level witness" does not hold for them. An 
`AdhocExpression` should be required in `dimensions` or `order` respectively.
   
   Also, a single test loops over every feature, so the first failing feature 
hides the others. Parametrizing over `SemanticViewFeature`, with a skip when 
the feature is not advertised, would report each one separately.



##########
superset-core/src/superset_core/semantic_layers/testing.py:
##########
@@ -59,3 +78,250 @@ 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)
+        elif case in {"null", "not_null"}:
+            operator: Operator = (
+                Operator.IS_NULL if case == "null" else Operator.IS_NOT_NULL
+            )
+            assert any(
+                isinstance(item, Filter) and item.operator == operator
+                for item in query.filters or set()
+            )
+        elif case == "or":
+            assert any(isinstance(item, OrFilter) for item in query.filters or 
set())
+        elif case == "order_limit":
+            assert query.order
+            assert query.limit is not None
+            assert query.limit > 0
+            full: SemanticResult = semantic_view.get_table(
+                replace(query, limit=None, offset=None)
+            )
+            assert full.results.num_rows > query.limit, "seed more rows than 
the limit"
+            assert full.results.slice(query.offset or 0, 
query.limit).equals(expected)
+        result: SemanticResult = semantic_view.get_table(query)
+        assert result.results.equals(expected), (

Review Comment:
   **Non-blocking (stricter than the contract).** `pa.Table.equals` compares 
column order and field nullability. The SDK only promises "one column per 
selected member name" (`types.py`, `view.py`), and the host selects by name 
(`cache_transform.py`: `results.select(columns)`). A provider that returns 
metrics before dimensions, or marks null-free columns `nullable=False`, fails 
all 6 query cases and the capability case even though the host accepts it.
   
   The nullability failure is also hard to diagnose because the message prints 
identical data:
   
   ```
   AssertionError: identity: expected {'category': [None, 'A', 'B'], 'total': 
[3, 7, 8]}, got {'category': [None, 'A', 'B'], 'total': [3, 7, 8]}
   ```
   
   Suggest comparing `result.results.select(expected.column_names)` after 
normalizing nullability (or comparing types and values per column), and 
including `schema` in the failure message. The same applies to line 207.



##########
superset-core/src/superset_core/semantic_layers/testing.py:
##########
@@ -59,3 +78,250 @@ 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)
+        elif case in {"null", "not_null"}:
+            operator: Operator = (
+                Operator.IS_NULL if case == "null" else Operator.IS_NOT_NULL
+            )
+            assert any(
+                isinstance(item, Filter) and item.operator == operator
+                for item in query.filters or set()
+            )
+        elif case == "or":
+            assert any(isinstance(item, OrFilter) for item in query.filters or 
set())
+        elif case == "order_limit":
+            assert query.order
+            assert query.limit is not None
+            assert query.limit > 0
+            full: SemanticResult = semantic_view.get_table(
+                replace(query, limit=None, offset=None)
+            )
+            assert full.results.num_rows > query.limit, "seed more rows than 
the limit"
+            assert full.results.slice(query.offset or 0, 
query.limit).equals(expected)
+        result: SemanticResult = semantic_view.get_table(query)
+        assert result.results.equals(expected), (
+            f"{case}: expected {expected.to_pydict()}, got 
{result.results.to_pydict()}"
+        )
+        assert set(result.results.column_names) == {
+            member.name for member in [*query.dimensions, *query.metrics]
+        }
+
+    @pytest.mark.parametrize("method", ["get_table", "get_values", 
"get_row_count"])
+    @pytest.mark.parametrize("kind", ["refusal", "fault", "incomplete", 
"unverified"])
+    def test_error_categories(
+        self,
+        method: str,
+        kind: str,
+        semantic_error_cases: Mapping[str, Callable[[], SemanticResult]],
+    ) -> None:
+        """Refusals and completeness are typed; operational faults stay 
faults."""
+        operation: Callable[[], SemanticResult] = semantic_error_cases[
+            f"{method}:{kind}"
+        ]
+        error: pytest.ExceptionInfo[Exception]
+        # Operational fault classes are provider-owned; classify the caught 
error below.
+        with pytest.raises(Exception) as error:  # noqa: PT011
+            operation()
+        if kind == "refusal":
+            assert isinstance(error.value, SemanticQueryRejectedError)
+            assert isinstance(error.value.code, SemanticQueryErrorCode)
+        elif kind in {"incomplete", "unverified"}:
+            assert isinstance(error.value, SemanticResultCompletenessError)
+            assert error.value.reason == kind
+        else:
+            assert not isinstance(
+                error.value,
+                (SemanticQueryRejectedError, SemanticResultCompletenessError),
+            )
+
+    def test_advertised_capabilities(
+        self,
+        semantic_view: SemanticView,
+        semantic_capability_cases: Mapping[str, tuple[SemanticQuery, 
pa.Table]],
+    ) -> None:
+        """Every advertised feature has a result-level witness in this lane."""
+        feature: SemanticViewFeature | str
+        for feature in semantic_view.features:
+            name: str = (
+                feature.value if isinstance(feature, SemanticViewFeature) else 
feature
+            )
+            query: SemanticQuery
+            expected: pa.Table
+            query, expected = semantic_capability_cases[name]
+            assert expected.num_rows > 0
+            assert query.order or expected.num_rows == 1, (
+                f"{name}: multi-row fixtures need explicit order"
+            )
+            if name == "ROW_OFFSET":
+                assert query.offset is not None
+                assert query.offset > 0
+                assert query.limit is not None
+                assert query.limit > 0
+            elif name in {"GROUP_LIMIT", "GROUP_OTHERS"}:
+                assert query.group_limit is not None
+                if name == "GROUP_OTHERS":
+                    assert query.group_limit.group_others
+            assert semantic_view.get_table(query).results.equals(expected), 
name
+
+    def test_metadata_generation_and_unbound_bypass(
+        self,
+        semantic_make_view: Callable[[str | None], SemanticView],
+        semantic_change_catalog: Callable[[], None],
+    ) -> None:
+        """Changed generations and unbound calls cannot reuse a stale catalog.
+
+        The change fixture must add a member on each call. Factories and 
transport
+        stay provider-owned; uncached legacy providers may report a None token.
+        """
+        first: SemanticView = semantic_make_view("generation-a")
+        before: tuple[set[str], set[tuple[str, str | None]]] = _catalog(first)
+        assert first.metadata_cache_token in {None, "generation-a"}
+        semantic_change_catalog()
+        second: SemanticView = semantic_make_view("generation-b")
+        after: tuple[set[str], set[tuple[str, str | None]]] = _catalog(second)
+        assert after != before
+        assert second.metadata_cache_token in {None, "generation-b"}
+        # A captured view must never claim a later discovery token.
+        assert first.metadata_cache_token in {None, "generation-a"}
+        semantic_change_catalog()
+        unbound: SemanticView = semantic_make_view(None)

Review Comment:
   **Non-blocking (stricter than the contract).** No host path constructs a 
provider without a token. `SemanticLayer.from_configuration` takes 
`cache_token: str` (required, not optional), and its only host caller 
(`execution.py`, the `bind` method) always passes `layer.metadata_generation`. 
The "unbound" obligation exists only in this kit and in the sentence this PR 
adds to `semantic-layer-cache-clear.md`.
   
   As a result, adapters have to invent a tokenless path. 
preset-io/superset-shell#5607 already calls `from_configuration(..., 
cache_token=None)` against the `str` annotation. Either drop the `None` leg 
(keep the two-generation freshness and no-relabel checks), or make the SDK 
signature `str | None` and document when the host uses it.



##########
superset-core/src/superset_core/semantic_layers/testing.py:
##########
@@ -59,3 +78,250 @@ 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)
+        elif case in {"null", "not_null"}:
+            operator: Operator = (
+                Operator.IS_NULL if case == "null" else Operator.IS_NOT_NULL
+            )
+            assert any(
+                isinstance(item, Filter) and item.operator == operator
+                for item in query.filters or set()
+            )
+        elif case == "or":
+            assert any(isinstance(item, OrFilter) for item in query.filters or 
set())
+        elif case == "order_limit":
+            assert query.order
+            assert query.limit is not None
+            assert query.limit > 0
+            full: SemanticResult = semantic_view.get_table(
+                replace(query, limit=None, offset=None)
+            )
+            assert full.results.num_rows > query.limit, "seed more rows than 
the limit"
+            assert full.results.slice(query.offset or 0, 
query.limit).equals(expected)
+        result: SemanticResult = semantic_view.get_table(query)
+        assert result.results.equals(expected), (
+            f"{case}: expected {expected.to_pydict()}, got 
{result.results.to_pydict()}"
+        )
+        assert set(result.results.column_names) == {
+            member.name for member in [*query.dimensions, *query.metrics]
+        }
+
+    @pytest.mark.parametrize("method", ["get_table", "get_values", 
"get_row_count"])
+    @pytest.mark.parametrize("kind", ["refusal", "fault", "incomplete", 
"unverified"])
+    def test_error_categories(
+        self,
+        method: str,
+        kind: str,
+        semantic_error_cases: Mapping[str, Callable[[], SemanticResult]],
+    ) -> None:
+        """Refusals and completeness are typed; operational faults stay 
faults."""
+        operation: Callable[[], SemanticResult] = semantic_error_cases[
+            f"{method}:{kind}"
+        ]
+        error: pytest.ExceptionInfo[Exception]
+        # Operational fault classes are provider-owned; classify the caught 
error below.
+        with pytest.raises(Exception) as error:  # noqa: PT011
+            operation()
+        if kind == "refusal":
+            assert isinstance(error.value, SemanticQueryRejectedError)
+            assert isinstance(error.value.code, SemanticQueryErrorCode)
+        elif kind in {"incomplete", "unverified"}:
+            assert isinstance(error.value, SemanticResultCompletenessError)
+            assert error.value.reason == kind
+        else:
+            assert not isinstance(
+                error.value,
+                (SemanticQueryRejectedError, SemanticResultCompletenessError),
+            )
+
+    def test_advertised_capabilities(
+        self,
+        semantic_view: SemanticView,
+        semantic_capability_cases: Mapping[str, tuple[SemanticQuery, 
pa.Table]],
+    ) -> None:
+        """Every advertised feature has a result-level witness in this lane."""
+        feature: SemanticViewFeature | str
+        for feature in semantic_view.features:
+            name: str = (
+                feature.value if isinstance(feature, SemanticViewFeature) else 
feature
+            )
+            query: SemanticQuery
+            expected: pa.Table
+            query, expected = semantic_capability_cases[name]
+            assert expected.num_rows > 0
+            assert query.order or expected.num_rows == 1, (
+                f"{name}: multi-row fixtures need explicit order"
+            )
+            if name == "ROW_OFFSET":
+                assert query.offset is not None
+                assert query.offset > 0
+                assert query.limit is not None
+                assert query.limit > 0
+            elif name in {"GROUP_LIMIT", "GROUP_OTHERS"}:
+                assert query.group_limit is not None
+                if name == "GROUP_OTHERS":
+                    assert query.group_limit.group_others
+            assert semantic_view.get_table(query).results.equals(expected), 
name
+
+    def test_metadata_generation_and_unbound_bypass(
+        self,
+        semantic_make_view: Callable[[str | None], SemanticView],
+        semantic_change_catalog: Callable[[], None],
+    ) -> None:
+        """Changed generations and unbound calls cannot reuse a stale catalog.
+
+        The change fixture must add a member on each call. Factories and 
transport
+        stay provider-owned; uncached legacy providers may report a None token.
+        """
+        first: SemanticView = semantic_make_view("generation-a")
+        before: tuple[set[str], set[tuple[str, str | None]]] = _catalog(first)
+        assert first.metadata_cache_token in {None, "generation-a"}
+        semantic_change_catalog()
+        second: SemanticView = semantic_make_view("generation-b")
+        after: tuple[set[str], set[tuple[str, str | None]]] = _catalog(second)
+        assert after != before
+        assert second.metadata_cache_token in {None, "generation-b"}
+        # A captured view must never claim a later discovery token.
+        assert first.metadata_cache_token in {None, "generation-a"}
+        semantic_change_catalog()
+        unbound: SemanticView = semantic_make_view(None)
+        assert _catalog(unbound) != after
+        assert unbound.metadata_cache_token is None
+
+    def test_imports_and_static_schema(
+        self, semantic_provider_class: tuple[str, str]
+    ) -> None:
+        """Import in a fresh interpreter; reject host imports and schema 
I/O."""
+        import os
+        import subprocess
+        import sys
+
+        # Fixed interpreter/code; provider metadata is argv, never shell text.
+        completed: subprocess.CompletedProcess[str] = subprocess.run(  # noqa: 
S603
+            [
+                sys.executable,
+                "-c",
+                "from superset_core.semantic_layers.testing import 
_check_isolation; "
+                "import sys; _check_isolation(*sys.argv[1:])",
+                *semantic_provider_class,
+            ],
+            env={**os.environ, "PYTHONPATH": os.pathsep.join(sys.path)},
+            capture_output=True,
+            text=True,
+            timeout=30,
+            check=False,
+        )
+        assert completed.returncode == 0, completed.stdout + completed.stderr
+
+    # TODO: add the default chronological-order case after its SDK rule lands.

Review Comment:
   **Nit.** This TODO is stale. #45389 landed the rule, and it is host-side: 
`with_default_order` fills `order` before dispatch, and the `SemanticQuery` 
docstring says a `get_table` call "receives an empty `order` only when no 
temporal dimension is selected". The provider-side obligation is just "honour 
the explicit order", which `order_limit`/`grain` already cover. The same 
applies to the "Default chronological ordering will be added after that SDK 
rule lands" sentence in `semantic-layer-provider-testing.md`.



-- 
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