mirror of
https://github.com/apache/superset.git
synced 2026-04-07 10:31:50 +00:00
678 lines
22 KiB
Python
678 lines
22 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
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
from superset_extensions_cli.cli import (
|
|
app,
|
|
build_manifest,
|
|
clean_dist,
|
|
copy_backend_files,
|
|
copy_frontend_dist,
|
|
init_frontend_deps,
|
|
)
|
|
|
|
from tests.utils import (
|
|
assert_directory_exists,
|
|
assert_file_exists,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def extension_with_build_structure():
|
|
"""Create extension structure suitable for build testing."""
|
|
|
|
def _create(base_path, include_frontend=True, include_backend=True):
|
|
# Create required directories
|
|
if include_frontend:
|
|
frontend_dir = base_path / "frontend"
|
|
frontend_dir.mkdir()
|
|
|
|
# Create conventional frontend entry point
|
|
frontend_src_dir = frontend_dir / "src"
|
|
frontend_src_dir.mkdir()
|
|
(frontend_src_dir / "index.tsx").write_text("// Frontend entry point")
|
|
|
|
if include_backend:
|
|
backend_dir = base_path / "backend"
|
|
backend_dir.mkdir()
|
|
|
|
# Create conventional backend structure
|
|
backend_src_dir = backend_dir / "src" / "test_org" / "test_extension"
|
|
backend_src_dir.mkdir(parents=True)
|
|
|
|
# Create conventional entry point file
|
|
(backend_src_dir / "entrypoint.py").write_text("# Backend entry point")
|
|
(backend_src_dir / "__init__.py").write_text("")
|
|
|
|
# Create parent __init__.py files for namespace packages
|
|
(backend_dir / "src" / "test_org" / "__init__.py").write_text("")
|
|
|
|
# Create pyproject.toml matching the template structure
|
|
pyproject_content = """[project]
|
|
name = "test_org-test_extension"
|
|
version = "1.0.0"
|
|
license = "Apache-2.0"
|
|
|
|
[tool.apache_superset_extensions.build]
|
|
# Files to include in the extension build/bundle
|
|
include = [
|
|
"src/test_org/test_extension/**/*.py",
|
|
]
|
|
exclude = []
|
|
"""
|
|
(backend_dir / "pyproject.toml").write_text(pyproject_content)
|
|
|
|
# Create extension.json
|
|
extension_json = {
|
|
"publisher": "test-org",
|
|
"name": "test-extension",
|
|
"displayName": "Test Extension",
|
|
"version": "1.0.0",
|
|
"permissions": [],
|
|
}
|
|
|
|
(base_path / "extension.json").write_text(json.dumps(extension_json))
|
|
|
|
return {
|
|
"frontend_dir": frontend_dir if include_frontend else None,
|
|
"backend_dir": backend_dir if include_backend else None,
|
|
}
|
|
|
|
return _create
|
|
|
|
|
|
# Build Command Tests
|
|
@pytest.mark.cli
|
|
@patch("superset_extensions_cli.cli.validate_npm")
|
|
@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.read_toml")
|
|
def test_build_command_success_flow(
|
|
mock_read_toml,
|
|
mock_rebuild_backend,
|
|
mock_rebuild_frontend,
|
|
mock_init_frontend_deps,
|
|
mock_validate_npm,
|
|
cli_runner,
|
|
isolated_filesystem,
|
|
extension_with_build_structure,
|
|
):
|
|
"""Test build command success flow."""
|
|
# Setup mocks
|
|
mock_rebuild_frontend.return_value = "remoteEntry.abc123.js"
|
|
mock_read_toml.return_value = {
|
|
"project": {"name": "test", "version": "1.0.0"},
|
|
"tool": {
|
|
"apache_superset_extensions": {
|
|
"build": {"include": ["src/test_org/test_extension/**/*.py"]}
|
|
}
|
|
},
|
|
}
|
|
|
|
# Create extension structure
|
|
dirs = extension_with_build_structure(isolated_filesystem)
|
|
|
|
result = cli_runner.invoke(app, ["build"])
|
|
|
|
assert result.exit_code == 0
|
|
assert "✅ Full build completed in dist/" in result.output
|
|
|
|
# Verify function calls
|
|
mock_validate_npm.assert_called_once()
|
|
mock_init_frontend_deps.assert_called_once_with(dirs["frontend_dir"])
|
|
mock_rebuild_frontend.assert_called_once()
|
|
mock_rebuild_backend.assert_called_once()
|
|
|
|
|
|
@pytest.mark.cli
|
|
@patch("superset_extensions_cli.cli.validate_npm")
|
|
@patch("superset_extensions_cli.cli.init_frontend_deps")
|
|
@patch("superset_extensions_cli.cli.rebuild_frontend")
|
|
@patch("superset_extensions_cli.cli.read_toml")
|
|
def test_build_command_handles_frontend_build_failure(
|
|
mock_read_toml,
|
|
mock_rebuild_frontend,
|
|
mock_init_frontend_deps,
|
|
mock_validate_npm,
|
|
cli_runner,
|
|
isolated_filesystem,
|
|
extension_with_build_structure,
|
|
):
|
|
"""Test build command handles frontend build failure."""
|
|
# Setup mocks
|
|
mock_rebuild_frontend.return_value = None # Indicates failure
|
|
mock_read_toml.return_value = {
|
|
"project": {"name": "test", "version": "1.0.0"},
|
|
"tool": {
|
|
"apache_superset_extensions": {
|
|
"build": {"include": ["src/test_org/test_extension/**/*.py"]}
|
|
}
|
|
},
|
|
}
|
|
|
|
# Create extension structure
|
|
extension_with_build_structure(isolated_filesystem)
|
|
|
|
result = cli_runner.invoke(app, ["build"])
|
|
|
|
# Command should complete and create manifest even with frontend failure
|
|
assert result.exit_code == 0
|
|
assert "✅ Full build completed in dist/" in result.output
|
|
|
|
|
|
# Clean Dist Tests
|
|
@pytest.mark.unit
|
|
def test_clean_dist_removes_existing_dist_directory(isolated_filesystem):
|
|
"""Test clean_dist removes existing dist directory and recreates it."""
|
|
# Create dist directory with some content
|
|
dist_dir = isolated_filesystem / "dist"
|
|
dist_dir.mkdir()
|
|
(dist_dir / "some_file.txt").write_text("test content")
|
|
(dist_dir / "subdir").mkdir()
|
|
|
|
clean_dist(isolated_filesystem)
|
|
|
|
# Should exist but be empty
|
|
assert_directory_exists(dist_dir)
|
|
assert list(dist_dir.iterdir()) == []
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_clean_dist_creates_dist_directory_if_missing(isolated_filesystem):
|
|
"""Test clean_dist creates dist directory when it doesn't exist."""
|
|
dist_dir = isolated_filesystem / "dist"
|
|
assert not dist_dir.exists()
|
|
|
|
clean_dist(isolated_filesystem)
|
|
|
|
assert_directory_exists(dist_dir)
|
|
|
|
|
|
# Frontend Dependencies Tests
|
|
@pytest.mark.unit
|
|
@patch("subprocess.run")
|
|
def test_init_frontend_deps_skips_when_node_modules_exists(
|
|
mock_run, isolated_filesystem
|
|
):
|
|
"""Test init_frontend_deps skips npm ci when node_modules exists."""
|
|
frontend_dir = isolated_filesystem / "frontend"
|
|
frontend_dir.mkdir()
|
|
(frontend_dir / "node_modules").mkdir()
|
|
|
|
init_frontend_deps(frontend_dir)
|
|
|
|
# Should not call subprocess.run for npm ci
|
|
mock_run.assert_not_called()
|
|
|
|
|
|
@pytest.mark.unit
|
|
@patch("subprocess.run")
|
|
@patch("superset_extensions_cli.cli.validate_npm")
|
|
def test_init_frontend_deps_runs_npm_i_when_missing(
|
|
mock_validate_npm, mock_run, isolated_filesystem
|
|
):
|
|
"""Test init_frontend_deps runs npm ci when node_modules is missing."""
|
|
frontend_dir = isolated_filesystem / "frontend"
|
|
frontend_dir.mkdir()
|
|
|
|
# Mock successful npm ci
|
|
mock_run.return_value = Mock(returncode=0)
|
|
|
|
init_frontend_deps(frontend_dir)
|
|
|
|
# Should validate npm and run npm ci
|
|
mock_validate_npm.assert_called_once()
|
|
mock_run.assert_called_once_with(["npm", "i"], cwd=frontend_dir, text=True)
|
|
|
|
|
|
@pytest.mark.unit
|
|
@patch("subprocess.run")
|
|
@patch("superset_extensions_cli.cli.validate_npm")
|
|
def test_init_frontend_deps_exits_on_npm_ci_failure(
|
|
mock_validate_npm, mock_run, isolated_filesystem
|
|
):
|
|
"""Test init_frontend_deps exits when npm ci fails."""
|
|
frontend_dir = isolated_filesystem / "frontend"
|
|
frontend_dir.mkdir()
|
|
|
|
# Mock failed npm ci
|
|
mock_run.return_value = Mock(returncode=1)
|
|
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
init_frontend_deps(frontend_dir)
|
|
|
|
assert exc_info.value.code == 1
|
|
|
|
|
|
# Build Manifest Tests
|
|
@pytest.mark.unit
|
|
def test_build_manifest_creates_correct_manifest_structure(
|
|
isolated_filesystem, extension_with_build_structure
|
|
):
|
|
"""Test build_manifest creates correct manifest from extension.json."""
|
|
# Create extension structure with both frontend and backend
|
|
extension_with_build_structure(
|
|
isolated_filesystem, include_frontend=True, include_backend=True
|
|
)
|
|
|
|
# Update extension.json with additional fields
|
|
extension_data = {
|
|
"publisher": "test-org",
|
|
"name": "test-extension",
|
|
"displayName": "Test Extension",
|
|
"version": "1.0.0",
|
|
"permissions": ["read_data"],
|
|
"dependencies": ["some_dep"],
|
|
}
|
|
extension_json = isolated_filesystem / "extension.json"
|
|
extension_json.write_text(json.dumps(extension_data))
|
|
|
|
manifest = build_manifest(isolated_filesystem, "remoteEntry.abc123.js")
|
|
|
|
# Verify manifest structure
|
|
assert manifest.id == "test-org.test-extension" # Composite ID
|
|
assert manifest.publisher == "test-org"
|
|
assert manifest.name == "test-extension"
|
|
assert manifest.displayName == "Test Extension"
|
|
assert manifest.version == "1.0.0"
|
|
assert manifest.permissions == ["read_data"]
|
|
assert manifest.dependencies == ["some_dep"]
|
|
|
|
# Verify frontend section
|
|
assert manifest.frontend is not None
|
|
assert manifest.frontend.remoteEntry == "remoteEntry.abc123.js"
|
|
assert manifest.frontend.moduleFederationName == "testOrg_testExtension"
|
|
|
|
# Verify backend section and conventional entrypoint
|
|
assert manifest.backend is not None
|
|
assert manifest.backend.entrypoint == "test_org.test_extension.entrypoint"
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_build_manifest_handles_minimal_extension(isolated_filesystem):
|
|
"""Test build_manifest with minimal extension.json (no frontend/backend)."""
|
|
extension_data = {
|
|
"publisher": "minimal-org",
|
|
"name": "minimal-extension",
|
|
"displayName": "Minimal Extension",
|
|
"version": "0.1.0",
|
|
"permissions": [],
|
|
}
|
|
extension_json = isolated_filesystem / "extension.json"
|
|
extension_json.write_text(json.dumps(extension_data))
|
|
|
|
manifest = build_manifest(isolated_filesystem, None)
|
|
|
|
assert manifest.id == "minimal-org.minimal-extension" # Composite ID
|
|
assert manifest.publisher == "minimal-org"
|
|
assert manifest.name == "minimal-extension"
|
|
assert manifest.displayName == "Minimal Extension"
|
|
assert manifest.version == "0.1.0"
|
|
assert manifest.permissions == []
|
|
assert manifest.dependencies == [] # Default empty list
|
|
assert manifest.frontend is None
|
|
assert manifest.backend is None
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_build_manifest_exits_when_extension_json_missing(isolated_filesystem):
|
|
"""Test build_manifest exits when extension.json is missing."""
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
build_manifest(isolated_filesystem, "remoteEntry.js")
|
|
|
|
assert exc_info.value.code == 1
|
|
|
|
|
|
# Frontend Build Tests
|
|
@pytest.mark.unit
|
|
def test_clean_dist_frontend_removes_frontend_dist(isolated_filesystem):
|
|
"""Test clean_dist_frontend removes frontend/dist directory specifically."""
|
|
from superset_extensions_cli.cli import clean_dist_frontend
|
|
|
|
# Create dist/frontend structure
|
|
dist_dir = isolated_filesystem / "dist"
|
|
dist_dir.mkdir(parents=True)
|
|
frontend_dist = dist_dir / "frontend"
|
|
frontend_dist.mkdir()
|
|
(frontend_dist / "some_file.js").write_text("content")
|
|
|
|
clean_dist_frontend(isolated_filesystem)
|
|
|
|
# Frontend dist should be removed, but dist should remain
|
|
assert dist_dir.exists()
|
|
assert not frontend_dist.exists()
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_clean_dist_frontend_handles_nonexistent_directory(isolated_filesystem):
|
|
"""Test clean_dist_frontend handles case where frontend dist doesn't exist."""
|
|
from superset_extensions_cli.cli import clean_dist_frontend
|
|
|
|
# No dist directory exists
|
|
clean_dist_frontend(isolated_filesystem)
|
|
|
|
# Should not raise error
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_run_frontend_build_with_output_messages(isolated_filesystem):
|
|
"""Test run_frontend_build produces expected output messages."""
|
|
from superset_extensions_cli.cli import run_frontend_build
|
|
|
|
frontend_dir = isolated_filesystem / "frontend"
|
|
frontend_dir.mkdir()
|
|
|
|
with patch("subprocess.run") as mock_run:
|
|
mock_result = Mock(returncode=0)
|
|
mock_run.return_value = mock_result
|
|
|
|
result = run_frontend_build(frontend_dir)
|
|
|
|
assert result.returncode == 0
|
|
mock_run.assert_called_once_with(
|
|
["npm", "run", "build"], cwd=frontend_dir, text=True
|
|
)
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.parametrize(
|
|
"return_code,expected_result",
|
|
[
|
|
(0, "remoteEntry.abc123.js"),
|
|
(1, None),
|
|
],
|
|
)
|
|
def test_rebuild_frontend_handles_build_results(
|
|
isolated_filesystem, return_code, expected_result
|
|
):
|
|
"""Test rebuild_frontend handles different build results."""
|
|
from superset_extensions_cli.cli import rebuild_frontend
|
|
|
|
# Create frontend structure
|
|
frontend_dir = isolated_filesystem / "frontend"
|
|
frontend_dir.mkdir()
|
|
|
|
if return_code == 0:
|
|
# Create frontend/dist with remoteEntry for success case
|
|
frontend_dist = frontend_dir / "dist"
|
|
frontend_dist.mkdir()
|
|
(frontend_dist / "remoteEntry.abc123.js").write_text("content")
|
|
|
|
# Create dist directory
|
|
dist_dir = isolated_filesystem / "dist"
|
|
dist_dir.mkdir()
|
|
|
|
with patch("superset_extensions_cli.cli.run_frontend_build") as mock_build:
|
|
mock_build.return_value = Mock(returncode=return_code)
|
|
|
|
result = rebuild_frontend(isolated_filesystem, frontend_dir)
|
|
|
|
assert result == expected_result
|
|
|
|
|
|
# Backend Build Tests
|
|
@pytest.mark.unit
|
|
def test_rebuild_backend_calls_copy_and_shows_message(isolated_filesystem):
|
|
"""Test rebuild_backend calls copy_backend_files and shows success message."""
|
|
from superset_extensions_cli.cli import rebuild_backend
|
|
|
|
# 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))
|
|
|
|
with patch("superset_extensions_cli.cli.copy_backend_files") as mock_copy:
|
|
rebuild_backend(isolated_filesystem)
|
|
|
|
mock_copy.assert_called_once_with(isolated_filesystem)
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_copy_backend_files_skips_non_files(isolated_filesystem):
|
|
"""Test copy_backend_files skips directories and non-files."""
|
|
# Create backend structure with directory
|
|
backend_dir = isolated_filesystem / "backend"
|
|
backend_src = backend_dir / "src" / "test_org" / "test_ext"
|
|
backend_src.mkdir(parents=True)
|
|
(backend_src / "__init__.py").write_text("# init")
|
|
|
|
# Create a subdirectory (should be skipped)
|
|
subdir = backend_src / "subdir"
|
|
subdir.mkdir()
|
|
|
|
# Create pyproject.toml with build configuration
|
|
pyproject_content = """[project]
|
|
name = "test_org-test_ext"
|
|
version = "1.0.0"
|
|
license = "Apache-2.0"
|
|
|
|
[tool.apache_superset_extensions.build]
|
|
include = [
|
|
"src/test_org/test_ext/**/*",
|
|
]
|
|
exclude = []
|
|
"""
|
|
(backend_dir / "pyproject.toml").write_text(pyproject_content)
|
|
|
|
# Create extension.json
|
|
extension_data = {
|
|
"publisher": "test-org",
|
|
"name": "test-ext",
|
|
"displayName": "Test Extension",
|
|
"version": "1.0.0",
|
|
"permissions": [],
|
|
}
|
|
(isolated_filesystem / "extension.json").write_text(json.dumps(extension_data))
|
|
|
|
# Create dist directory
|
|
clean_dist(isolated_filesystem)
|
|
|
|
copy_backend_files(isolated_filesystem)
|
|
|
|
# Verify only files were copied, not directories
|
|
dist_dir = isolated_filesystem / "dist"
|
|
assert_file_exists(
|
|
dist_dir / "backend" / "src" / "test_org" / "test_ext" / "__init__.py"
|
|
)
|
|
|
|
# Directory should not be copied as a file
|
|
copied_subdir = dist_dir / "backend" / "src" / "test_org" / "test_ext" / "subdir"
|
|
# The directory might exist but should be empty since we skip non-files
|
|
if copied_subdir.exists():
|
|
assert list(copied_subdir.iterdir()) == []
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_copy_backend_files_copies_matched_files(isolated_filesystem):
|
|
"""Test copy_backend_files copies files matching patterns from pyproject.toml."""
|
|
# Create backend source files
|
|
backend_dir = isolated_filesystem / "backend"
|
|
backend_src = backend_dir / "src" / "test_org" / "test_ext"
|
|
backend_src.mkdir(parents=True)
|
|
(backend_src / "__init__.py").write_text("# init")
|
|
(backend_src / "main.py").write_text("# main")
|
|
|
|
# Create pyproject.toml with build configuration
|
|
pyproject_content = """[project]
|
|
name = "test_org-test_ext"
|
|
version = "1.0.0"
|
|
license = "Apache-2.0"
|
|
|
|
[tool.apache_superset_extensions.build]
|
|
include = [
|
|
"src/test_org/test_ext/**/*.py",
|
|
]
|
|
exclude = []
|
|
"""
|
|
(backend_dir / "pyproject.toml").write_text(pyproject_content)
|
|
|
|
# Create extension.json
|
|
extension_data = {
|
|
"publisher": "test-org",
|
|
"name": "test-ext",
|
|
"displayName": "Test Extension",
|
|
"version": "1.0.0",
|
|
"permissions": [],
|
|
}
|
|
(isolated_filesystem / "extension.json").write_text(json.dumps(extension_data))
|
|
|
|
# Create dist directory
|
|
clean_dist(isolated_filesystem)
|
|
|
|
copy_backend_files(isolated_filesystem)
|
|
|
|
# Verify files were copied
|
|
dist_dir = isolated_filesystem / "dist"
|
|
assert_file_exists(
|
|
dist_dir / "backend" / "src" / "test_org" / "test_ext" / "__init__.py"
|
|
)
|
|
assert_file_exists(
|
|
dist_dir / "backend" / "src" / "test_org" / "test_ext" / "main.py"
|
|
)
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_copy_backend_files_handles_various_glob_patterns(isolated_filesystem):
|
|
"""Test copy_backend_files correctly handles different glob pattern formats."""
|
|
# Create backend structure with files in different locations
|
|
backend_dir = isolated_filesystem / "backend"
|
|
backend_src = backend_dir / "src" / "test_org" / "test_ext"
|
|
backend_src.mkdir(parents=True)
|
|
|
|
# Create files that should match different pattern types
|
|
(backend_src / "__init__.py").write_text("# init")
|
|
(backend_src / "main.py").write_text("# main")
|
|
(backend_dir / "config.py").write_text("# config") # Root level file
|
|
|
|
# Create subdirectory with files
|
|
subdir = backend_src / "utils"
|
|
subdir.mkdir()
|
|
(subdir / "helper.py").write_text("# helper")
|
|
|
|
# Create pyproject.toml with various glob patterns that would fail with old logic
|
|
pyproject_content = """[project]
|
|
name = "test_org-test_ext"
|
|
version = "1.0.0"
|
|
license = "Apache-2.0"
|
|
|
|
[tool.apache_superset_extensions.build]
|
|
include = [
|
|
"config.py", # No '/' - would break old logic
|
|
"**/*.py", # Starts with '**' - would break old logic
|
|
"src/test_org/test_ext/main.py", # Specific file
|
|
]
|
|
exclude = []
|
|
"""
|
|
(backend_dir / "pyproject.toml").write_text(pyproject_content)
|
|
|
|
# Create extension.json
|
|
extension_data = {
|
|
"publisher": "test-org",
|
|
"name": "test-ext",
|
|
"displayName": "Test Extension",
|
|
"version": "1.0.0",
|
|
"permissions": [],
|
|
}
|
|
(isolated_filesystem / "extension.json").write_text(json.dumps(extension_data))
|
|
|
|
# Create dist directory
|
|
clean_dist(isolated_filesystem)
|
|
|
|
copy_backend_files(isolated_filesystem)
|
|
|
|
# Verify files were copied according to patterns
|
|
dist_dir = isolated_filesystem / "dist"
|
|
|
|
# config.py (pattern: "config.py")
|
|
assert_file_exists(dist_dir / "backend" / "config.py")
|
|
|
|
# All .py files should be included (pattern: "**/*.py")
|
|
assert_file_exists(
|
|
dist_dir / "backend" / "src" / "test_org" / "test_ext" / "__init__.py"
|
|
)
|
|
assert_file_exists(
|
|
dist_dir / "backend" / "src" / "test_org" / "test_ext" / "utils" / "helper.py"
|
|
)
|
|
|
|
# Specific file (pattern: "src/test_org/test_ext/main.py")
|
|
assert_file_exists(
|
|
dist_dir / "backend" / "src" / "test_org" / "test_ext" / "main.py"
|
|
)
|
|
|
|
|
|
# Removed obsolete tests:
|
|
# - test_copy_backend_files_handles_no_backend_config: This scenario can't happen since copy_backend_files is only called when backend exists
|
|
# - test_copy_backend_files_exits_when_extension_json_missing: Validation catches this before copy_backend_files is called
|
|
|
|
|
|
# Frontend Dist Copy Tests
|
|
@pytest.mark.unit
|
|
def test_copy_frontend_dist_copies_files_correctly(isolated_filesystem):
|
|
"""Test copy_frontend_dist copies frontend build files to dist."""
|
|
# Create frontend/dist structure
|
|
frontend_dist = isolated_filesystem / "frontend" / "dist"
|
|
frontend_dist.mkdir(parents=True)
|
|
|
|
# Create some files including remoteEntry
|
|
(frontend_dist / "remoteEntry.abc123.js").write_text("remote entry content")
|
|
(frontend_dist / "main.js").write_text("main js content")
|
|
|
|
# Create subdirectory with file
|
|
assets_dir = frontend_dist / "assets"
|
|
assets_dir.mkdir()
|
|
(assets_dir / "style.css").write_text("css content")
|
|
|
|
# Create dist directory
|
|
clean_dist(isolated_filesystem)
|
|
|
|
remote_entry = copy_frontend_dist(isolated_filesystem)
|
|
|
|
assert remote_entry == "remoteEntry.abc123.js"
|
|
|
|
# Verify files were copied
|
|
dist_dir = isolated_filesystem / "dist"
|
|
assert_file_exists(dist_dir / "frontend" / "dist" / "remoteEntry.abc123.js")
|
|
assert_file_exists(dist_dir / "frontend" / "dist" / "main.js")
|
|
assert_file_exists(dist_dir / "frontend" / "dist" / "assets" / "style.css")
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_copy_frontend_dist_exits_when_no_remote_entry(isolated_filesystem):
|
|
"""Test copy_frontend_dist exits when no remoteEntry file found."""
|
|
# Create frontend/dist without remoteEntry file
|
|
frontend_dist = isolated_filesystem / "frontend" / "dist"
|
|
frontend_dist.mkdir(parents=True)
|
|
(frontend_dist / "main.js").write_text("main content")
|
|
|
|
clean_dist(isolated_filesystem)
|
|
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
copy_frontend_dist(isolated_filesystem)
|
|
|
|
assert exc_info.value.code == 1
|