Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Code
d7f0f08758 test(dashboard-import): pin native filter scope rootPath preservation
Closes #19944

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 18:01:13 -07:00

View File

@@ -123,6 +123,121 @@ def test_update_native_filter_config_scope_excluded():
}
def test_update_native_filter_config_preserves_rootpath_and_remaps_excluded():
"""
Regression guard for #19944: a native filter's ``scope`` has two parts that
must both survive an export/import roundtrip:
- ``rootPath`` controls *which* dashboard sections (tabs/rows) the filter
applies to. It uses position keys (``ROOT_ID``, ``TAB-xxx``), not chart
IDs, so ``update_id_refs`` must leave it untouched.
- ``excluded`` is a list of chart IDs the filter does NOT apply to within
its rootPath. Those IDs must be remapped to destination-env IDs.
The original bug report — "filters are automatically applied to all charts,
even if a different scoping was defined before the export" — describes a
rootPath silently being collapsed back to ``["ROOT_ID"]`` (i.e. "apply
everywhere"). This test pins the post-refactor contract: the import path
must not mutate or drop ``rootPath``.
"""
from superset.commands.dashboard.importers.v1.utils import update_id_refs
config: dict[str, Any] = {
"position": {
"CHART1": {
"id": "CHART1",
"meta": {"chartId": 101, "uuid": "uuid1"},
"type": "CHART",
},
"CHART2": {
"id": "CHART2",
"meta": {"chartId": 102, "uuid": "uuid2"},
"type": "CHART",
},
"CHART3": {
"id": "CHART3",
"meta": {"chartId": 103, "uuid": "uuid3"},
"type": "CHART",
},
},
"metadata": {
"native_filter_configuration": [
{
"id": "NATIVE_FILTER-region",
"name": "Region",
"scope": {
# Filter applies only to charts under TAB-revenue,
# except chart 102 which is explicitly excluded.
"rootPath": ["TAB-revenue"],
"excluded": [102],
},
},
{
"id": "NATIVE_FILTER-product",
"name": "Product",
"scope": {
# Different filter, different rootPath; must not be
# cross-contaminated with the first filter's scope.
"rootPath": ["TAB-inventory", "TAB-revenue"],
"excluded": [101, 103],
},
},
],
},
}
chart_ids = {"uuid1": 1, "uuid2": 2, "uuid3": 3}
dataset_info: dict[str, dict[str, Any]] = {}
fixed = update_id_refs(config, chart_ids, dataset_info)
filters = fixed["metadata"]["native_filter_configuration"]
# rootPath uses position keys, not chart IDs — must pass through unchanged.
assert filters[0]["scope"]["rootPath"] == ["TAB-revenue"]
assert filters[1]["scope"]["rootPath"] == ["TAB-inventory", "TAB-revenue"]
# excluded uses chart IDs — must be remapped to destination-env IDs.
assert filters[0]["scope"]["excluded"] == [2]
assert filters[1]["scope"]["excluded"] == [1, 3]
def test_update_native_filter_config_default_rootpath_preserved():
"""
The "apply everywhere" default — ``rootPath: ["ROOT_ID"]`` — must also
survive untouched. A regression that special-cased this value (e.g. by
deleting it) would silently change "apply everywhere" into "apply nowhere"
on import, since downstream consumers treat a missing rootPath as empty
rather than as the default.
"""
from superset.commands.dashboard.importers.v1.utils import update_id_refs
config: dict[str, Any] = {
"position": {
"CHART1": {
"id": "CHART1",
"meta": {"chartId": 101, "uuid": "uuid1"},
"type": "CHART",
},
},
"metadata": {
"native_filter_configuration": [
{
"id": "NATIVE_FILTER-global",
"name": "Global",
"scope": {"rootPath": ["ROOT_ID"], "excluded": []},
}
],
},
}
chart_ids = {"uuid1": 1}
dataset_info: dict[str, dict[str, Any]] = {}
fixed = update_id_refs(config, chart_ids, dataset_info)
scope = fixed["metadata"]["native_filter_configuration"][0]["scope"]
assert scope["rootPath"] == ["ROOT_ID"]
assert scope["excluded"] == []
def test_update_id_refs_cross_filter_chart_configuration_key_and_excluded_mapping():
from superset.commands.dashboard.importers.v1.utils import update_id_refs