Files
superset2/tests/unit_tests/examples/utils_test.py
2026-02-12 14:12:12 -08:00

207 lines
7.4 KiB
Python

# 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.
"""Tests for examples/utils.py - YAML config loading and content assembly."""
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import MagicMock, patch
import yaml
def _create_example_tree(base_dir: Path) -> Path:
"""Create a minimal example directory tree under base_dir/superset/examples/.
Returns the 'superset' directory (what files("superset") would return).
"""
superset_dir = base_dir / "superset"
examples_dir = superset_dir / "examples"
# _shared configs
shared_dir = examples_dir / "_shared"
shared_dir.mkdir(parents=True)
(shared_dir / "database.yaml").write_text(
"database_name: examples\n"
"sqlalchemy_uri: __SQLALCHEMY_EXAMPLES_URI__\n"
"uuid: a2dc77af-e654-49bb-b321-40f6b559a1ee\n"
"version: '1.0.0'\n"
)
(shared_dir / "metadata.yaml").write_text(
"version: '1.0.0'\ntimestamp: '2020-12-11T22:52:56.534241+00:00'\n"
)
# An example with dataset, dashboard, and chart
example_dir = examples_dir / "test_example"
example_dir.mkdir()
(example_dir / "dataset.yaml").write_text(
yaml.dump(
{
"table_name": "test_table",
"schema": "main",
"uuid": "14f48794-ebfa-4f60-a26a-582c49132f1b",
"database_uuid": "a2dc77af-e654-49bb-b321-40f6b559a1ee",
"version": "1.0.0",
}
)
)
(example_dir / "dashboard.yaml").write_text(
yaml.dump(
{
"dashboard_title": "Test Dashboard",
"uuid": "dddddddd-dddd-dddd-dddd-dddddddddddd",
"version": "1.0.0",
}
)
)
charts_dir = example_dir / "charts"
charts_dir.mkdir()
(charts_dir / "test_chart.yaml").write_text(
yaml.dump(
{
"slice_name": "Test Chart",
"uuid": "cccccccc-cccc-cccc-cccc-cccccccccccc",
"dataset_uuid": "14f48794-ebfa-4f60-a26a-582c49132f1b",
"version": "1.0.0",
}
)
)
return superset_dir
def test_load_contents_builds_correct_import_structure():
"""load_contents() must produce the key structure ImportExamplesCommand expects.
This tests the orchestration entry point: YAML files are discovered from
the examples directory, the shared database config has its URI placeholder
replaced, and the result has the correct key prefixes (databases/, datasets/,
metadata.yaml).
"""
from superset.examples.utils import load_contents
with TemporaryDirectory() as tmpdir:
superset_dir = _create_example_tree(Path(tmpdir))
test_examples_uri = "sqlite:///path/to/examples.db"
mock_app = MagicMock()
mock_app.config = {"SQLALCHEMY_EXAMPLES_URI": test_examples_uri}
with patch("superset.examples.utils.files", return_value=superset_dir):
with patch("flask.current_app", mock_app):
contents = load_contents()
# Verify database config is present with placeholder replaced
assert "databases/examples.yaml" in contents
db_content = contents["databases/examples.yaml"]
assert "__SQLALCHEMY_EXAMPLES_URI__" not in db_content
assert test_examples_uri in db_content
# Verify metadata is present
assert "metadata.yaml" in contents
# Verify dataset is discovered with correct key prefix
assert "datasets/examples/test_example.yaml" in contents
# Verify dashboard is discovered with correct key prefix
assert "dashboards/test_example.yaml" in contents
# Verify chart is discovered with correct key prefix
assert "charts/test_example/test_chart.yaml" in contents
# Verify schema normalization happened (main -> null)
dataset_content = contents["datasets/examples/test_example.yaml"]
assert "schema: main" not in dataset_content
assert "schema: null" in dataset_content
def test_load_contents_replaces_sqlalchemy_examples_uri_placeholder():
"""The __SQLALCHEMY_EXAMPLES_URI__ placeholder must be replaced with the real URI.
If this placeholder is not replaced, the database import will fail with an
invalid connection string, preventing all examples from loading.
"""
from superset.examples.utils import _load_shared_configs
with TemporaryDirectory() as tmpdir:
superset_dir = _create_example_tree(Path(tmpdir))
examples_root = Path("examples")
test_uri = "postgresql://user:pass@host/db"
mock_app = MagicMock()
mock_app.config = {"SQLALCHEMY_EXAMPLES_URI": test_uri}
with patch("superset.examples.utils.files", return_value=superset_dir):
with patch("flask.current_app", mock_app):
contents = _load_shared_configs(examples_root)
assert "databases/examples.yaml" in contents
assert test_uri in contents["databases/examples.yaml"]
assert "__SQLALCHEMY_EXAMPLES_URI__" not in contents["databases/examples.yaml"]
@patch("superset.examples.utils.ImportExamplesCommand")
@patch("superset.examples.utils.load_contents")
def test_load_examples_from_configs_wires_command_correctly(
mock_load_contents,
mock_command_cls,
):
"""load_examples_from_configs() must construct ImportExamplesCommand
with overwrite=True and thread force_data through.
A wiring regression here would silently skip overwriting existing
examples or ignore the force_data flag.
"""
from superset.examples.utils import load_examples_from_configs
mock_load_contents.return_value = {"databases/examples.yaml": "content"}
mock_command = MagicMock()
mock_command_cls.return_value = mock_command
load_examples_from_configs(force_data=True)
mock_load_contents.assert_called_once_with(False)
mock_command_cls.assert_called_once_with(
{"databases/examples.yaml": "content"},
overwrite=True,
force_data=True,
)
mock_command.run.assert_called_once()
@patch("superset.examples.utils.ImportExamplesCommand")
@patch("superset.examples.utils.load_contents")
def test_load_examples_from_configs_defaults(
mock_load_contents,
mock_command_cls,
):
"""Default call should pass force_data=False and load_test_data=False."""
from superset.examples.utils import load_examples_from_configs
mock_load_contents.return_value = {}
mock_command = MagicMock()
mock_command_cls.return_value = mock_command
load_examples_from_configs()
mock_load_contents.assert_called_once_with(False)
mock_command_cls.assert_called_once_with(
{},
overwrite=True,
force_data=False,
)
mock_command.run.assert_called_once()