| # 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 pathlib import Path |
| from typing import Any |
| |
| |
| def assert_file_exists(path: Path, description: str = "") -> None: |
| """ |
| Assert that a file exists with a descriptive error message. |
| |
| Args: |
| path: Path to the file that should exist |
| description: Optional description for better error messages |
| """ |
| desc_msg = f" ({description})" if description else "" |
| assert path.exists(), f"Expected file {path}{desc_msg} to exist, but it doesn't" |
| assert path.is_file(), f"Expected {path}{desc_msg} to be a file, but it's not" |
| |
| |
| def assert_directory_exists(path: Path, description: str = "") -> None: |
| """ |
| Assert that a directory exists with a descriptive error message. |
| |
| Args: |
| path: Path to the directory that should exist |
| description: Optional description for better error messages |
| """ |
| desc_msg = f" ({description})" if description else "" |
| assert path.exists(), ( |
| f"Expected directory {path}{desc_msg} to exist, but it doesn't" |
| ) |
| assert path.is_dir(), f"Expected {path}{desc_msg} to be a directory, but it's not" |
| |
| |
| def assert_file_structure(base_path: Path, expected_files: list[str]) -> None: |
| """ |
| Assert that all expected files exist under the base path. |
| |
| Args: |
| base_path: Base directory path |
| expected_files: List of relative file paths that should exist |
| """ |
| for file_path in expected_files: |
| full_path = base_path / file_path |
| assert_file_exists(full_path, "part of expected structure") |
| |
| |
| def assert_directory_structure(base_path: Path, expected_dirs: list[str]) -> None: |
| """ |
| Assert that all expected directories exist under the base path. |
| |
| Args: |
| base_path: Base directory path |
| expected_dirs: List of relative directory paths that should exist |
| """ |
| for dir_path in expected_dirs: |
| full_path = base_path / dir_path |
| assert_directory_exists(full_path, "part of expected structure") |
| |
| |
| def get_directory_tree(path: Path, ignore: set[str] | None = None) -> set[str]: |
| """ |
| Get all files and directories under a path as relative string paths. |
| |
| Args: |
| path: Base path to scan |
| ignore: Set of file/directory names to ignore |
| |
| Returns: |
| Set of relative path strings |
| """ |
| ignore = ignore or {".DS_Store", "__pycache__", ".pytest_cache"} |
| tree: set[str] = set() |
| |
| if not path.exists(): |
| return tree |
| |
| for item in path.rglob("*"): |
| if any(ignored in item.parts for ignored in ignore): |
| continue |
| relative = item.relative_to(path) |
| tree.add(str(relative)) |
| |
| return tree |
| |
| |
| def load_json_file(path: Path) -> dict[str, Any]: |
| """ |
| Load and parse a JSON file. |
| |
| Args: |
| path: Path to the JSON file |
| |
| Returns: |
| Parsed JSON content |
| |
| Raises: |
| AssertionError: If file doesn't exist or isn't valid JSON |
| """ |
| assert_file_exists(path, "JSON file") |
| try: |
| content = json.loads(path.read_text()) |
| return content |
| except json.JSONDecodeError as e: |
| raise AssertionError(f"File {path} contains invalid JSON: {e}") |
| |
| |
| def assert_json_content(path: Path, expected_values: dict[str, Any]) -> None: |
| """ |
| Assert that a JSON file contains expected key-value pairs. |
| |
| Args: |
| path: Path to the JSON file |
| expected_values: Dictionary of expected key-value pairs |
| """ |
| content = load_json_file(path) |
| |
| for key, expected_value in expected_values.items(): |
| assert key in content, f"Expected key '{key}' not found in {path}" |
| actual_value = content[key] |
| assert actual_value == expected_value, ( |
| f"Expected {key}='{expected_value}' but got '{actual_value}' in {path}" |
| ) |
| |
| |
| def assert_file_contains(path: Path, text: str) -> None: |
| """ |
| Assert that a file contains specific text. |
| |
| Args: |
| path: Path to the file |
| text: Text that should be present in the file |
| """ |
| assert_file_exists(path, "text file") |
| content = path.read_text() |
| assert text in content, f"Expected text '{text}' not found in {path}" |
| |
| |
| def assert_file_content_matches(path: Path, expected_content: str) -> None: |
| """ |
| Assert that a file's content exactly matches expected content. |
| |
| Args: |
| path: Path to the file |
| expected_content: Expected file content |
| """ |
| assert_file_exists(path, "content file") |
| actual_content = path.read_text() |
| assert actual_content == expected_content, ( |
| f"File content mismatch in {path}\n" |
| f"Expected:\n{expected_content}\n" |
| f"Actual:\n{actual_content}" |
| ) |
| |
| |
| def create_test_extension_structure( |
| base_path: Path, |
| id_: str, |
| include_frontend: bool = True, |
| include_backend: bool = True, |
| ) -> dict[str, Any]: |
| """ |
| Helper to create expected extension structure for testing. |
| |
| Args: |
| base_path: Base path where extension should be created |
| id_: Unique identifier for extension |
| name: Extension name |
| include_frontend: Whether frontend should be included |
| include_backend: Whether backend should be included |
| |
| Returns: |
| Dictionary with expected paths and metadata |
| """ |
| extension_path = base_path / id_ |
| expected_files = ["extension.json"] |
| expected_dirs: list[str] = [] |
| |
| if include_frontend: |
| expected_dirs.append("frontend") |
| expected_files.append("frontend/package.json") |
| |
| if include_backend: |
| expected_dirs.append("backend") |
| expected_files.append("backend/pyproject.toml") |
| |
| expected = { |
| "extension_path": extension_path, |
| "expected_files": expected_files, |
| "expected_dirs": expected_dirs, |
| } |
| |
| return expected |