mikebridge commented on code in PR #45103: URL: https://github.com/apache/superset/pull/45103#discussion_r4253966440
########## tests/e2e_extensions/semantic_stub/dist/backend/src/superset_e2e_semantic_stub/__init__.py: ########## @@ -0,0 +1,259 @@ +# 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. +""" +In-memory semantic layer for end-to-end tests. + +Loaded only as a ``LOCAL_EXTENSIONS`` entry by the Playwright semantic-view +configuration (``tests.integration_tests.superset_test_config_semantic``). It +registers through the public ``@semantic_layer`` decorator like any provider, +so the host code under test is the code that runs in production; only the +warehouse is replaced by a fixed table. + +Anything the stub does not implement raises instead of being ignored, so a +host change that starts sending a new query shape fails the browser test +rather than rendering plausible but unfiltered data. +""" + +from __future__ import annotations + +from collections import defaultdict +from operator import itemgetter +from typing import Any + +import pyarrow as pa +from pydantic import BaseModel, ConfigDict +from superset_core.semantic_layers.decorators import semantic_layer +from superset_core.semantic_layers.layer import SemanticLayer +from superset_core.semantic_layers.types import ( + AggregationType, + Dimension, + Filter, + FilterValues, + Metric, + Operator, + OrderDirection, + PredicateType, + SemanticQuery, + SemanticRequest, + SemanticResult, +) +from superset_core.semantic_layers.view import SemanticView + +VIEW_NAME: str = "orders" + +CATEGORY: Dimension = Dimension( + "category", "category", pa.string(), verbose_name="Category" +) +REGION: Dimension = Dimension("region", "region", pa.string(), verbose_name="Region") +TOTAL_AMOUNT: Metric = Metric( + "total_amount", + "total_amount", + pa.int64(), + "SUM(amount)", + aggregation=AggregationType.SUM, + verbose_name="Total amount", +) +ORDER_COUNT: Metric = Metric( + "order_count", + "order_count", + pa.int64(), + "COUNT(*)", + aggregation=AggregationType.COUNT, + verbose_name="Order count", +) + +# Small enough to reason about in an assertion: Books totals 10 + 5 = 15. +ROWS: tuple[dict[str, Any], ...] = ( + {"category": "Books", "region": "East", "amount": 10}, + {"category": "Books", "region": "West", "amount": 5}, + {"category": "Games", "region": "East", "amount": 7}, + {"category": "Music", "region": "West", "amount": 3}, +) + +SUPPORTED_OPERATORS: frozenset[Operator] = frozenset( + {Operator.EQUALS, Operator.NOT_EQUALS, Operator.IN, Operator.NOT_IN} +) + + +class StubConfiguration(BaseModel): + """The stub needs no connection settings; reject anything supplied.""" + + model_config = ConfigDict(extra="forbid") + + +def _matches(row: dict[str, Any], filter_: Filter) -> bool: + if filter_.type != PredicateType.WHERE or not isinstance(filter_.column, Dimension): + raise ValueError("The E2E stub only supports WHERE filters on dimensions") + if filter_.operator not in SUPPORTED_OPERATORS: + raise ValueError(f"The E2E stub does not support {filter_.operator.value}") + + actual: Any = row[filter_.column.id] + values: frozenset[FilterValues] = ( + frozenset(filter_.value) + if isinstance(filter_.value, (frozenset, tuple)) + else frozenset({filter_.value}) + ) + if filter_.operator in {Operator.EQUALS, Operator.IN}: + return actual in values + return actual not in values + + +def _filtered_rows(filters: set[Filter] | None) -> list[dict[str, Any]]: + return [row for row in ROWS if all(_matches(row, f) for f in filters or set())] Review Comment: Confirmed and fixed in 0608e2203b. The stub validates every filter's predicate type, dimension column and operator before scanning rows, so a no-match `region IN ['North']` filter cannot hide an unsupported `category LIKE 'B%'` predicate. The regression forces both iteration orders across table results, value suggestions and row counts, and covers unsupported operators, HAVING predicates and metric columns. Before the fix, all nine cases with the no-match filter first failed to raise; afterward all 24 focused tests pass. A supported no-match query still returns an empty result through the host chart-data API. Changed-file and branch-wide hooks passed, along with all three helper Jest tests. I also checked the packaging: this `dist/` entrypoint is the checked-in fixture loaded directly through `LOCAL_EXTENSIONS`; there is no separate generated source to update. Codex implemented and verified the fix, and an independent Claude review approved the exact committed tree. -- 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]
