mirror of
https://github.com/apache/superset.git
synced 2026-04-07 18:35:15 +00:00
250 lines
8.3 KiB
Python
250 lines
8.3 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.
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import threading
|
|
import time
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
from superset_core.extensions.types import Manifest
|
|
from superset_extensions_cli.cli import app, FrontendChangeHandler
|
|
|
|
|
|
# Dev Command Tests
|
|
@pytest.mark.cli
|
|
@patch("superset_extensions_cli.cli.Observer")
|
|
@patch("superset_extensions_cli.cli.init_frontend_deps")
|
|
@patch("superset_extensions_cli.cli.rebuild_frontend")
|
|
@patch("superset_extensions_cli.cli.rebuild_backend")
|
|
@patch("superset_extensions_cli.cli.build_manifest")
|
|
@patch("superset_extensions_cli.cli.write_manifest")
|
|
def test_dev_command_starts_watchers(
|
|
mock_write_manifest,
|
|
mock_build_manifest,
|
|
mock_rebuild_backend,
|
|
mock_rebuild_frontend,
|
|
mock_init_frontend_deps,
|
|
mock_observer_class,
|
|
cli_runner,
|
|
isolated_filesystem,
|
|
extension_setup_for_dev,
|
|
):
|
|
"""Test dev command starts file watchers."""
|
|
# Setup mocks
|
|
mock_rebuild_frontend.return_value = "remoteEntry.abc123.js"
|
|
mock_build_manifest.return_value = Manifest(
|
|
id="test-org.test-extension",
|
|
publisher="test-org",
|
|
name="test-extension",
|
|
displayName="Test Extension",
|
|
version="1.0.0",
|
|
)
|
|
|
|
mock_observer = Mock()
|
|
mock_observer_class.return_value = mock_observer
|
|
|
|
extension_setup_for_dev(isolated_filesystem)
|
|
|
|
# Run dev command in a thread since it's blocking
|
|
def run_dev():
|
|
try:
|
|
cli_runner.invoke(app, ["dev"], catch_exceptions=False)
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
dev_thread = threading.Thread(target=run_dev)
|
|
dev_thread.daemon = True
|
|
dev_thread.start()
|
|
|
|
# Let it start up
|
|
time.sleep(0.1)
|
|
|
|
# Verify observer methods were called
|
|
mock_observer.schedule.assert_called()
|
|
mock_observer.start.assert_called_once()
|
|
|
|
# Initial setup calls
|
|
mock_init_frontend_deps.assert_called_once()
|
|
mock_rebuild_frontend.assert_called()
|
|
mock_rebuild_backend.assert_called()
|
|
mock_build_manifest.assert_called()
|
|
mock_write_manifest.assert_called()
|
|
|
|
|
|
@pytest.mark.cli
|
|
@patch("superset_extensions_cli.cli.init_frontend_deps")
|
|
@patch("superset_extensions_cli.cli.rebuild_frontend")
|
|
@patch("superset_extensions_cli.cli.rebuild_backend")
|
|
@patch("superset_extensions_cli.cli.build_manifest")
|
|
@patch("superset_extensions_cli.cli.write_manifest")
|
|
def test_dev_command_initial_build(
|
|
mock_write_manifest,
|
|
mock_build_manifest,
|
|
mock_rebuild_backend,
|
|
mock_rebuild_frontend,
|
|
mock_init_frontend_deps,
|
|
cli_runner,
|
|
isolated_filesystem,
|
|
extension_setup_for_dev,
|
|
):
|
|
"""Test dev command performs initial build setup."""
|
|
# Setup mocks
|
|
mock_rebuild_frontend.return_value = "remoteEntry.abc123.js"
|
|
mock_build_manifest.return_value = Manifest(
|
|
id="test-org.test-extension",
|
|
publisher="test-org",
|
|
name="test-extension",
|
|
displayName="Test Extension",
|
|
version="1.0.0",
|
|
)
|
|
|
|
extension_setup_for_dev(isolated_filesystem)
|
|
|
|
with patch("superset_extensions_cli.cli.Observer") as mock_observer_class:
|
|
mock_observer = Mock()
|
|
mock_observer_class.return_value = mock_observer
|
|
|
|
with patch("time.sleep", side_effect=KeyboardInterrupt):
|
|
try:
|
|
cli_runner.invoke(app, ["dev"], catch_exceptions=False)
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
# Verify initial build steps
|
|
frontend_dir = isolated_filesystem / "frontend"
|
|
mock_init_frontend_deps.assert_called_once_with(frontend_dir)
|
|
mock_rebuild_frontend.assert_called_once_with(isolated_filesystem, frontend_dir)
|
|
mock_rebuild_backend.assert_called_once_with(isolated_filesystem)
|
|
|
|
|
|
# FrontendChangeHandler Tests
|
|
@pytest.mark.unit
|
|
def test_frontend_change_handler_init():
|
|
"""Test FrontendChangeHandler initialization."""
|
|
mock_trigger = Mock()
|
|
handler = FrontendChangeHandler(trigger_build=mock_trigger)
|
|
|
|
assert handler.trigger_build == mock_trigger
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_frontend_change_handler_ignores_dist_changes():
|
|
"""Test FrontendChangeHandler ignores changes in dist directory."""
|
|
mock_trigger = Mock()
|
|
handler = FrontendChangeHandler(trigger_build=mock_trigger)
|
|
|
|
# Create mock event with dist path
|
|
mock_event = Mock()
|
|
mock_event.src_path = "/path/to/frontend/dist/file.js"
|
|
|
|
handler.on_any_event(mock_event)
|
|
|
|
# Should not trigger build for dist changes
|
|
mock_trigger.assert_not_called()
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.parametrize(
|
|
"source_path",
|
|
[
|
|
"/path/to/frontend/src/component.tsx",
|
|
"/path/to/frontend/webpack.config.js",
|
|
"/path/to/frontend/package.json",
|
|
],
|
|
)
|
|
def test_frontend_change_handler_triggers_on_source_changes(source_path):
|
|
"""Test FrontendChangeHandler triggers build on source changes."""
|
|
mock_trigger = Mock()
|
|
handler = FrontendChangeHandler(trigger_build=mock_trigger)
|
|
|
|
# Create mock event with source path
|
|
mock_event = Mock()
|
|
mock_event.src_path = source_path
|
|
|
|
handler.on_any_event(mock_event)
|
|
|
|
# Should trigger build for source changes
|
|
mock_trigger.assert_called_once()
|
|
|
|
|
|
# Dev Utility Functions Tests
|
|
@pytest.mark.unit
|
|
def test_frontend_watcher_function_coverage(isolated_filesystem):
|
|
"""Test frontend watcher function for coverage."""
|
|
# Create extension.json
|
|
extension_json = {
|
|
"publisher": "test-org",
|
|
"name": "test-extension",
|
|
"displayName": "Test Extension",
|
|
"version": "1.0.0",
|
|
"permissions": [],
|
|
}
|
|
(isolated_filesystem / "extension.json").write_text(json.dumps(extension_json))
|
|
|
|
# Create dist directory
|
|
dist_dir = isolated_filesystem / "dist"
|
|
dist_dir.mkdir()
|
|
|
|
mock_manifest = Manifest(
|
|
id="test-org.test-extension",
|
|
publisher="test-org",
|
|
name="test-extension",
|
|
displayName="Test Extension",
|
|
version="1.0.0",
|
|
)
|
|
with patch("superset_extensions_cli.cli.rebuild_frontend") as mock_rebuild:
|
|
with patch("superset_extensions_cli.cli.build_manifest") as mock_build:
|
|
with patch("superset_extensions_cli.cli.write_manifest") as mock_write:
|
|
mock_rebuild.return_value = "remoteEntry.abc123.js"
|
|
mock_build.return_value = mock_manifest
|
|
|
|
# Simulate frontend watcher function logic
|
|
frontend_dir = isolated_filesystem / "frontend"
|
|
frontend_dir.mkdir()
|
|
|
|
# Actually call the functions to simulate the frontend_watcher
|
|
if (
|
|
remote_entry := mock_rebuild(isolated_filesystem, frontend_dir)
|
|
) is not None:
|
|
manifest = mock_build(isolated_filesystem, remote_entry)
|
|
mock_write(isolated_filesystem, manifest)
|
|
|
|
mock_rebuild.assert_called_once_with(isolated_filesystem, frontend_dir)
|
|
mock_build.assert_called_once_with(
|
|
isolated_filesystem, "remoteEntry.abc123.js"
|
|
)
|
|
mock_write.assert_called_once_with(isolated_filesystem, mock_manifest)
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_backend_watcher_function_coverage(isolated_filesystem):
|
|
"""Test backend watcher function only rebuilds backend files."""
|
|
# Create backend directory
|
|
backend_dir = isolated_filesystem / "backend"
|
|
backend_dir.mkdir()
|
|
|
|
with patch("superset_extensions_cli.cli.rebuild_backend") as mock_rebuild:
|
|
# Simulate backend watcher function - it only rebuilds backend
|
|
if backend_dir.exists():
|
|
mock_rebuild(isolated_filesystem)
|
|
|
|
# Backend watcher should only call rebuild_backend
|
|
mock_rebuild.assert_called_once_with(isolated_filesystem)
|