Files
2026-07-13 13:12:33 +08:00

348 lines
10 KiB
Python

from __future__ import annotations
import ast
import importlib
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[4]
CHAT_CMD = PROJECT_ROOT / "src/opensquilla/cli/chat_cmd.py"
TURN_STREAM = PROJECT_ROOT / "src/opensquilla/cli/chat/turn_stream.py"
TURN_BRIDGE = PROJECT_ROOT / "src/opensquilla/cli/tui/turn_bridge.py"
TURN_STREAM_DEFAULTS = (
PROJECT_ROOT / "src/opensquilla/cli/tui/adapters/turn_stream_defaults.py"
)
GATEWAY_SLASH_ADAPTER = (
PROJECT_ROOT / "src/opensquilla/cli/tui/adapters/slash_gateway.py"
)
STANDALONE_SLASH_ADAPTER = (
PROJECT_ROOT / "src/opensquilla/cli/tui/adapters/slash_standalone.py"
)
OPENTUI_RENDERER = "opensquilla.cli.tui.opentui.renderer"
REMOVED_TERMINAL_BRIDGE = "terminal" + "_bridge"
def _imports_name_from_module(path: Path, module: str, name: str) -> bool:
tree = ast.parse(path.read_text())
for node in ast.walk(tree):
if not isinstance(node, ast.ImportFrom):
continue
if node.module == module and any(alias.name == name for alias in node.names):
return True
return False
def _imports_from_module(path: Path, module: str) -> bool:
tree = ast.parse(path.read_text())
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module == module:
return True
if isinstance(node, ast.Import) and any(alias.name == module for alias in node.names):
return True
return False
def _imports_module(path: Path, module: str) -> bool:
tree = ast.parse(path.read_text())
for node in ast.walk(tree):
if isinstance(node, ast.Import) and any(alias.name == module for alias in node.names):
return True
if isinstance(node, ast.ImportFrom) and node.module == module:
return True
return False
def _imports_module_from_package(path: Path, package: str, module_name: str) -> bool:
tree = ast.parse(path.read_text())
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom):
if node.module == package and any(alias.name == module_name for alias in node.names):
return True
continue
if isinstance(node, ast.Import):
module = f"{package}.{module_name}"
if any(alias.name == module for alias in node.names):
return True
return False
def _defined_function_names(path: Path) -> set[str]:
tree = ast.parse(path.read_text())
return {
node.name
for node in tree.body
if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef)
}
def _module_aliases(path: Path, module_alias: str) -> dict[str, str]:
tree = ast.parse(path.read_text())
aliases: dict[str, str] = {}
for node in tree.body:
if not isinstance(node, ast.Assign):
continue
if len(node.targets) != 1 or not isinstance(node.targets[0], ast.Name):
continue
value = node.value
if (
isinstance(value, ast.Attribute)
and isinstance(value.value, ast.Name)
and value.value.id == module_alias
):
aliases[node.targets[0].id] = value.attr
return aliases
def test_chat_cmd_does_not_import_legacy_renderer_or_approval_handler() -> None:
assert not _imports_name_from_module(
CHAT_CMD,
"opensquilla.cli.repl.stream",
"StreamingRenderer",
)
assert not _imports_name_from_module(
CHAT_CMD,
"opensquilla.cli.repl.approval",
"maybe_handle_approval",
)
def test_chat_cmd_does_not_import_raw_slash_adapters_or_context() -> None:
assert not _imports_module_from_package(
CHAT_CMD,
"opensquilla.cli.repl",
"slash_adapter",
)
assert not _imports_module_from_package(
CHAT_CMD,
"opensquilla.cli.repl",
"standalone_slash_adapter",
)
assert not _imports_from_module(
CHAT_CMD,
"opensquilla.cli.repl.slash_adapter",
)
def test_chat_cmd_does_not_import_raw_runtime_or_removed_bridges() -> None:
assert not _imports_module_from_package(
CHAT_CMD,
"opensquilla.cli.repl",
"gateway_runtime",
)
assert not _imports_module_from_package(
CHAT_CMD,
"opensquilla.cli.repl",
"standalone_runtime",
)
assert not _imports_module_from_package(
CHAT_CMD,
"opensquilla.cli.repl",
REMOVED_TERMINAL_BRIDGE,
)
def test_chat_cmd_does_not_import_raw_input_assets() -> None:
assert not _imports_module_from_package(
CHAT_CMD,
"opensquilla.cli.repl",
"input_assets",
)
assert not _imports_from_module(
CHAT_CMD,
"opensquilla.cli.repl.input_assets",
)
def test_chat_cmd_does_not_import_backend_helper_bridges() -> None:
for module_name in ("input_bridge", "slash_bridge", "turn_bridge"):
assert not _imports_module_from_package(
CHAT_CMD,
"opensquilla.cli.repl",
module_name,
)
assert not _imports_from_module(
CHAT_CMD,
f"opensquilla.cli.repl.{module_name}",
)
def test_chat_cmd_private_compat_surface_is_dynamic_legacy_exports() -> None:
tree = ast.parse(CHAT_CMD.read_text())
assigned_names = {
target.id
for node in tree.body
if isinstance(node, ast.Assign)
for target in node.targets
if isinstance(target, ast.Name)
}
assert _defined_function_names(CHAT_CMD) == {"__dir__", "__getattr__", "run_chat"}
assert not any(
name.startswith("_") and not name.startswith("__")
for name in assigned_names
)
assert not _imports_module_from_package(
CHAT_CMD,
"opensquilla.cli.repl",
"chat_compat",
)
assert not _imports_from_module(
CHAT_CMD,
"opensquilla.cli.repl.chat_compat",
)
def test_chat_cmd_uses_typed_launch_request_instead_of_private_runner_names() -> None:
source = CHAT_CMD.read_text()
assert "_ChatCommandRequest" in source
assert "_ChatCommandLaunchOverrides" in source
assert "_run_chat_request" in source
assert "legacy_overrides" not in source
assert "_launch_bridge" not in source
assert "_standalone_repl" not in source
assert "_gateway_chat" not in source
def test_chat_cmd_star_import_keeps_legacy_public_names_visible() -> None:
chat_cmd = importlib.import_module("opensquilla.cli.chat_cmd")
assert "run_chat" in chat_cmd.__all__
assert "GATEWAY_SLASH_HANDLER_WORDS" in chat_cmd.__all__
assert "TurnResult" in chat_cmd.__all__
assert "_file_prompt_and_attachments" not in chat_cmd.__all__
assert "GATEWAY_SLASH_HANDLER_WORDS" in dir(chat_cmd)
assert "_file_prompt_and_attachments" in dir(chat_cmd)
namespace: dict[str, object] = {}
exec("from opensquilla.cli.chat_cmd import *", namespace)
assert "run_chat" in namespace
assert "GATEWAY_SLASH_HANDLER_WORDS" in namespace
assert "_file_prompt_and_attachments" not in namespace
def test_chat_cmd_only_defines_typer_entrypoint_and_export_hooks() -> None:
assert _defined_function_names(CHAT_CMD) == {"__dir__", "__getattr__", "run_chat"}
def test_chat_cmd_does_not_import_launch_presentation_details() -> None:
assert not _imports_module(CHAT_CMD, "asyncio")
assert not _imports_module(CHAT_CMD, "os")
assert not _imports_module(CHAT_CMD, "sys")
assert not _imports_name_from_module(CHAT_CMD, "rich.panel", "Panel")
assert not _imports_name_from_module(CHAT_CMD, "opensquilla.cli.ui", "ACCENT")
def test_chat_cmd_does_not_import_cli_presentation_defaults() -> None:
assert not _imports_from_module(CHAT_CMD, "opensquilla.cli.ui")
def test_chat_cmd_does_not_import_raw_turn_stream_facade() -> None:
assert not _imports_module_from_package(
CHAT_CMD,
"opensquilla.cli.repl",
"turn_stream",
)
assert not _imports_from_module(
CHAT_CMD,
"opensquilla.cli.repl.turn_stream",
)
assert not _imports_from_module(
CHAT_CMD,
"opensquilla.cli.repl.stream",
)
def test_turn_stream_does_not_import_raw_input_assets() -> None:
assert not _imports_module_from_package(
TURN_STREAM,
"opensquilla.cli.repl",
"input_assets",
)
assert not _imports_from_module(
TURN_STREAM,
"opensquilla.cli.repl.input_assets",
)
def test_turn_stream_does_not_import_frontend_default_dependencies() -> None:
assert not _imports_module_from_package(
TURN_STREAM,
"opensquilla.cli.repl",
"input_bridge",
)
assert not _imports_from_module(
TURN_STREAM,
"opensquilla.cli.repl.approval",
)
assert not _imports_name_from_module(
TURN_STREAM,
"opensquilla.cli.repl.stream",
"StreamingRenderer",
)
assert not _imports_from_module(
TURN_STREAM,
f"opensquilla.cli.repl.{REMOVED_TERMINAL_BRIDGE}",
)
assert not _imports_from_module(
TURN_STREAM,
"opensquilla.cli.ui",
)
assert not _imports_from_module(
TURN_STREAM,
"opensquilla.cli.repl.slash_adapter",
)
assert not _imports_from_module(
TURN_STREAM,
"opensquilla.cli.tui.contracts",
)
assert not _imports_from_module(
TURN_STREAM,
"opensquilla.engine.commands",
)
def test_turn_bridge_does_not_import_concrete_streaming_renderer() -> None:
assert not _imports_name_from_module(
TURN_BRIDGE,
"opensquilla.cli.repl.stream",
"StreamingRenderer",
)
def test_turn_stream_defaults_uses_opentui_renderer_default() -> None:
assert _imports_name_from_module(
TURN_STREAM_DEFAULTS,
OPENTUI_RENDERER,
"OpenTuiStreamRenderer",
)
assert not _imports_name_from_module(
TURN_STREAM_DEFAULTS,
"opensquilla.cli.repl.approval",
"maybe_handle_approval",
)
def test_turn_bridge_delegates_tui_approval_defaults() -> None:
assert _imports_from_module(
TURN_BRIDGE,
"opensquilla.cli.tui.adapters.turn_stream_defaults",
)
assert not _imports_name_from_module(
TURN_BRIDGE,
OPENTUI_RENDERER,
"OpenTuiStreamRenderer",
)
def test_slash_adapters_do_not_import_raw_input_assets() -> None:
for path in (GATEWAY_SLASH_ADAPTER, STANDALONE_SLASH_ADAPTER):
assert not _imports_module_from_package(
path,
"opensquilla.cli.repl",
"input_assets",
)
assert not _imports_from_module(
path,
"opensquilla.cli.repl.input_assets",
)