"""Tests for the post-Phase 1.3 architecture. Per module_taxonomy_refactor_20260627 Phase 1.3, src/command_palette.py was deleted and its content split by responsibility: - Command / ScoredCommand / CommandRegistry / fuzzy_match (data/ops) -> src/commands.py - render_palette_modal (view) -> src/gui_2.py src/commands.py is a thin data module and can be imported eagerly (no require_warmed lazy load is needed; the original lazy-load pattern in startup_speedup_20260606 was specifically to defer the heavy src/command_palette which pulled in imgui at module load). These tests run in fresh subprocesses to ensure no warmup state leaks from the test runner. We assert: - src/commands imports cleanly and exposes Command + CommandRegistry - src/gui_2 exposes render_palette_modal - src/commands does NOT import gui_2 at module level (avoids circular) - The static audit detects no top-level command_palette import (since the module no longer exists) """ import subprocess import sys import textwrap from pathlib import Path ROOT = Path(__file__).resolve().parent.parent def _run_in_subprocess(snippet: str) -> subprocess.CompletedProcess: script = textwrap.dedent(snippet) return subprocess.run( [sys.executable, "-c", script], capture_output=True, text=True, cwd=str(ROOT), timeout=30, ) def test_commands_exposes_command_and_registry() -> None: res = _run_in_subprocess(""" from src.commands import Command, CommandRegistry, fuzzy_match, ScoredCommand r = CommandRegistry() def my_cmd(app): pass r.register(my_cmd) print(len(r.all())) print(r.all()[0].id) """) assert res.returncode == 0, f"stderr: {res.stderr}" lines = res.stdout.strip().splitlines() assert lines[0] == "1" assert lines[1] == "my_cmd" def test_gui_2_exposes_render_palette_modal() -> None: res = _run_in_subprocess(""" from src.gui_2 import render_palette_modal print(callable(render_palette_modal)) """) assert res.returncode == 0, f"stderr: {res.stderr}" assert res.stdout.strip() == "True" def test_commands_does_not_import_gui_2_at_module_level() -> None: """src/commands is imported by src/commands registration sites and by gui_2.render_palette_modal. To avoid a circular import, commands.py must NOT import gui_2 at the top of the file. (TYPE_CHECKING imports are allowed because they don't execute at runtime.) """ res = _run_in_subprocess(""" import ast from pathlib import Path commands_path = Path('src') / 'commands.py' source = commands_path.read_text(encoding='utf-8') tree = ast.parse(source) for node in tree.body: if isinstance(node, (ast.Import, ast.ImportFrom)): mod = getattr(node, 'module', None) or (node.names[0].name if node.names else '') if mod and ('gui_2' in mod or mod.endswith('gui_2')): print('VIOLATION:', mod) print('OK') """) assert res.returncode == 0, f"stderr: {res.stderr}" assert "VIOLATION" not in res.stdout assert "OK" in res.stdout def test_command_palette_module_no_longer_exists() -> None: """src/command_palette.py was deleted in Phase 1.3; this is a regression guard.""" res = _run_in_subprocess(""" import importlib try: importlib.import_module('src.command_palette') print('EXISTS') except ModuleNotFoundError: print('NOT_FOUND') """) assert res.returncode == 0, f"stderr: {res.stderr}" assert res.stdout.strip() == "NOT_FOUND"