154 lines
5.2 KiB
Python
154 lines
5.2 KiB
Python
# =================== AIPass ====================
|
|
# Name: tests/test_integration.py
|
|
# Description: Integration tests for CLI main() flow
|
|
# Version: 2.0.0
|
|
# Created: 2026-03-29
|
|
# Modified: 2026-03-30
|
|
# =============================================
|
|
|
|
"""Integration tests for CLI main() entry point."""
|
|
|
|
import subprocess
|
|
import sys
|
|
from io import StringIO
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from rich.console import Console
|
|
|
|
from aipass.cli import cli_entry
|
|
from aipass.cli.apps import cli as cli_module
|
|
from aipass.cli.apps.cli import main
|
|
from aipass.cli.apps.modules import display
|
|
|
|
|
|
# =============================================================================
|
|
# Helpers
|
|
# =============================================================================
|
|
|
|
|
|
def _make_capture_console():
|
|
"""Return (console, get_output) for capturing Rich output.
|
|
|
|
Uses no_color=True so assertions can match plain text without ANSI escapes.
|
|
"""
|
|
buf = StringIO()
|
|
cons = Console(file=buf, no_color=True, width=120, highlight=False)
|
|
|
|
def get_output() -> str:
|
|
return buf.getvalue()
|
|
|
|
return cons, get_output
|
|
|
|
|
|
# =============================================================================
|
|
# main() flow tests — mock sys.argv to simulate CLI invocation
|
|
# =============================================================================
|
|
|
|
|
|
class TestMainFlow:
|
|
"""Integration tests for the main() entry point."""
|
|
|
|
def test_main_no_args_returns_zero(self):
|
|
"""No args shows introspection and returns 0."""
|
|
cons, _get_output = _make_capture_console()
|
|
with (
|
|
patch.object(cli_module, "CONSOLE", cons),
|
|
patch.object(display, "CONSOLE", cons),
|
|
patch("sys.argv", ["cli"]),
|
|
):
|
|
result = main()
|
|
assert result == 0
|
|
|
|
def test_main_help_flag_returns_zero(self):
|
|
"""--help returns 0."""
|
|
cons, _get_output = _make_capture_console()
|
|
err_cons, _get_err = _make_capture_console()
|
|
with (
|
|
patch.object(cli_module, "CONSOLE", cons),
|
|
patch.object(display, "CONSOLE", cons),
|
|
patch.object(display, "err_console", err_cons),
|
|
patch.object(display, "_TRIGGER", None),
|
|
patch.object(display, "_TRIGGER_LOADED", True),
|
|
patch("sys.argv", ["cli", "--help"]),
|
|
):
|
|
result = main()
|
|
assert result == 0
|
|
|
|
def test_main_version_flag_returns_zero(self):
|
|
"""--version returns 0."""
|
|
cons, get_output = _make_capture_console()
|
|
with (
|
|
patch.object(cli_module, "CONSOLE", cons),
|
|
patch.object(display, "CONSOLE", cons),
|
|
patch("sys.argv", ["cli", "--version"]),
|
|
):
|
|
result = main()
|
|
assert result == 0
|
|
output = get_output()
|
|
assert "CLI v" in output
|
|
|
|
def test_main_unknown_command_returns_one(self):
|
|
"""Unknown command returns 1."""
|
|
cons, _get_output = _make_capture_console()
|
|
err_cons, get_err = _make_capture_console()
|
|
with (
|
|
patch.object(cli_module, "CONSOLE", cons),
|
|
patch.object(display, "CONSOLE", cons),
|
|
patch.object(display, "err_console", err_cons),
|
|
patch("sys.argv", ["cli", "nonexistent_cmd_xyz"]),
|
|
):
|
|
result = main()
|
|
assert result == 1
|
|
err_output = get_err()
|
|
assert "Unknown command" in err_output
|
|
|
|
def test_main_display_demo_returns_zero(self):
|
|
"""'display demo' returns 0."""
|
|
cons, _get_output = _make_capture_console()
|
|
err_cons, _get_err = _make_capture_console()
|
|
with (
|
|
patch.object(cli_module, "CONSOLE", cons),
|
|
patch.object(display, "CONSOLE", cons),
|
|
patch.object(display, "err_console", err_cons),
|
|
patch.object(display, "_TRIGGER", None),
|
|
patch.object(display, "_TRIGGER_LOADED", True),
|
|
patch("aipass.cli.apps.handlers.json.json_handler.log_operation"),
|
|
patch("sys.argv", ["cli", "display", "demo"]),
|
|
):
|
|
result = main()
|
|
assert result == 0
|
|
|
|
|
|
# =============================================================================
|
|
# __main__.py test — verify module is runnable
|
|
# =============================================================================
|
|
|
|
|
|
class TestModuleRunnable:
|
|
"""Verify python -m aipass.cli works as a subprocess."""
|
|
|
|
def test_module_runnable(self):
|
|
"""python -m aipass.cli --version runs successfully."""
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "aipass.cli", "--version"],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=30,
|
|
cwd=str(Path(__file__).resolve().parents[3]),
|
|
)
|
|
assert result.returncode == 0
|
|
|
|
def test_cli_entry_callable(self):
|
|
"""cli_entry() is the console_scripts entry point — verify it's callable."""
|
|
cons, _get_output = _make_capture_console()
|
|
with (
|
|
patch.object(cli_module, "CONSOLE", cons),
|
|
patch.object(display, "CONSOLE", cons),
|
|
patch("sys.argv", ["aipass", "--version"]),
|
|
pytest.raises(SystemExit) as exc_info,
|
|
):
|
|
cli_entry()
|
|
assert exc_info.value.code == 0
|