Files
AIPass/src/aipass/cli/tests/test_templates.py
T
f19f7ac12d feat: system-wide compliance push + 896 tests across 10 branches (#124)
* feat(seedgo): deep nesting bypasses, dead code cleanup, json structure compliance

Co-Authored-By: @seedgo <seedgo@aipass>

* feat(memory): seedgo certification: introspection fixes, subprocess bypasses, silent catch cleanup

Co-Authored-By: @memory <memory@aipass>

* feat(api): seedgo certification: 94%→97%, 31/33 standards at 100%

Co-Authored-By: @api <api@aipass>

* feat(seedgo): deep nesting 100%, limit 3→4, checker refactors, @ validation, bypass cleanup

Co-Authored-By: @seedgo <seedgo@aipass>

* feat: seedgo cert sprint — 10 branches dispatched, drone introspection rebuilt, system-wide compliance push

Session 49-50 cert sprint results:
- drone: introspection rebuilt (proper auto-discovery), silent_catch 92%→100%, overall 97%
- api: 94%→97%, json_handler fixed, PR #116
- backup: 93%→94%, json_handler load_template→inline
- memory: 88%→91%, introspection 79%→100%, 10 bypasses for subprocess files
- skills: 97%, json_structure→100%, introspection→100%
- spawn: 97%→99%, 32/34 standards at 100%
- ai_mail: 95%→97%, 12 unused functions removed, 32/34 at 100%
- seedgo: checker improvements (deep_nesting threshold 3→4, various fixes)
- drone: removed from _MODULE_REGISTRY (DPLAN-0053 consensus)
- commons: introspection bypasses (22 entries), python3→drone refs fixed
- trigger/cli/prax/daemon/flow/backup: various cert fixes

New DPLANs: 0053 (drone audit), 0054 (bypass tracker), 0055 (persistent git branches)
New FPLAN: 0134 (persistent citizen git branches — drone build)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(seedgo): audit display dynamic rendering, 176 unit tests, test coverage 6→93%

Co-Authored-By: @seedgo <seedgo@aipass>

* feat(memory): seedgo compliance: 92% → 96%, fixes + 70 bypasses

Co-Authored-By: @memory <memory@aipass>

* feat: night shift — compliance push, drone persistent branches + module routing, dead code cleanup

Autonomous night shift (DPLAN-0057). System avg 93% → 96%, all 14 branches 95%+.

Drone: persistent citizen/{name} branches (FPLAN-0134), module routing fix (FPLAN-0136),
19 logger.info→console.print across 6 modules, @ enforcement hints.

Compliance: backup 94→95%, daemon 94→95%, flow 93→96%, prax 94→96%, trigger 93→96%.
Prax: 27 dead functions removed, monitoring cleanup. Flow: dead code removal, bypass.json.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(memory): unit tests: 194 tests, 7 test files, 43% module coverage

Co-Authored-By: @memory <memory@aipass>

* feat(api): unit tests: 117 tests, 5 test files, 40% module coverage (3-round audit)

Co-Authored-By: @api <api@aipass>

* feat: system-wide compliance push + 896 tests across 10 branches

S51-S52 accumulated work:
- Stale cleanup: flow dead code removed (write_plan_outputs.py, 139 lines from process.py), trimmed command_parser/display/registry_ops
- Daemon refactor: scheduler_cron.py split (920→388 lines), new action_processor + plugin_processor handlers
- Seedgo checker fixes: architecture_check, json_structure_check, silent_catch_check, unused_function_check improved
- Seedgo cleanup: mock_standard_1 removed, bypass.py removed, bypass_handler trimmed
- Compliance: bypass.json updates across 8 branches, pytest.ini + conftest.py standardized
- Test dispatch: 46 new test files across ai_mail(4), backup(11), cli(4), daemon(4), flow(6), prax(6), trigger(4), commons(6)
- Small fixes: drone lock_handler, json_handlers, prax operations/event_queue, commons writers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: @seedgo <seedgo@aipass>
Co-authored-by: @memory <memory@aipass>
Co-authored-by: @api <api@aipass>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 21:57:54 -07:00

200 lines
7.4 KiB
Python

"""Tests for the CLI templates module — operation output templates."""
import pytest
from io import StringIO
from unittest.mock import patch, MagicMock
from rich.console import Console
from aipass.cli.apps.modules import templates
from aipass.cli.apps.modules import display
@pytest.fixture
def plain_console():
"""Rich Console that writes plain text (no ANSI codes) for assertions."""
buf = StringIO()
cons = Console(file=buf, no_color=True, width=120)
def get_output() -> str:
return buf.getvalue()
return cons, get_output
# ============================================================================
# handle_command ROUTING TESTS
# ============================================================================
class TestHandleCommandRouting:
"""Tests for handle_command dispatch logic."""
@patch.object(templates, "run_demo")
def test_demo_command_calls_run_demo(self, mock_run_demo):
result = templates.handle_command("demo", [])
mock_run_demo.assert_called_once()
assert result is True
@patch.object(templates, "print_introspection")
def test_templates_no_args_calls_introspection(self, mock_introspection):
result = templates.handle_command("templates", [])
mock_introspection.assert_called_once()
assert result is True
@patch.object(templates, "print_help")
def test_templates_help_flag(self, mock_help):
result = templates.handle_command("templates", ["--help"])
mock_help.assert_called_once()
assert result is True
@patch.object(templates, "print_help")
def test_templates_dash_h_flag(self, mock_help):
result = templates.handle_command("templates", ["-h"])
mock_help.assert_called_once()
assert result is True
@patch.object(templates, "print_help")
def test_templates_help_word(self, mock_help):
result = templates.handle_command("templates", ["help"])
mock_help.assert_called_once()
assert result is True
@patch.object(templates, "run_demo")
def test_templates_demo_subcommand(self, mock_run_demo):
result = templates.handle_command("templates", ["demo"])
mock_run_demo.assert_called_once()
assert result is True
def test_unknown_command_returns_false(self):
result = templates.handle_command("unknown", [])
assert result is False
def test_templates_unknown_subcommand_returns_false(self):
result = templates.handle_command("templates", ["bogus"])
assert result is False
# ============================================================================
# operation_start OUTPUT TESTS
# ============================================================================
class TestOperationStart:
"""Tests for operation_start output formatting."""
def test_operation_name_in_output(self, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.operation_start("Building files")
output = get_output()
assert "Building files" in output
def test_details_in_output(self, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.operation_start("Deploying", target="/srv/app", mode="fast")
output = get_output()
assert "target: /srv/app" in output
assert "mode: fast" in output
def test_no_details_omits_detail_lines(self, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.operation_start("Simple op")
output = get_output()
# Should contain the operation name but no key-value detail lines
assert "Simple op" in output
lines = [line.strip() for line in output.splitlines() if line.strip()]
# Only the operation line should have content; no "key: value" lines
detail_lines = [l for l in lines if ": " in l and "Simple op" not in l]
assert len(detail_lines) == 0
# ============================================================================
# operation_complete OUTPUT TESTS
# ============================================================================
class TestOperationComplete:
"""Tests for operation_complete output formatting."""
def test_summary_header_present(self, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.operation_complete(created=3)
output = get_output()
assert "Summary:" in output
def test_summary_kwargs_in_output(self, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.operation_complete(created=5, skipped=2)
output = get_output()
assert "created: 5" in output
assert "skipped: 2" in output
def test_time_kwarg_shows_completion_line(self, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.operation_complete(files=10, time="2.5s")
output = get_output()
assert "Completed in 2.5s" in output
def test_no_time_kwarg_omits_completion_line(self, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.operation_complete(files=10)
output = get_output()
assert "Completed in" not in output
def test_separator_line_present(self, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.operation_complete(created=1)
output = get_output()
assert "─" * 50 in output
def test_no_kwargs_shows_empty_summary(self, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.operation_complete()
output = get_output()
assert "Summary:" in output
# No key-value summary lines should appear after "Summary:"
lines = output.splitlines()
summary_idx = next(i for i, l in enumerate(lines) if "Summary:" in l)
after_summary = [
l.strip() for l in lines[summary_idx + 1:] if l.strip()
]
kv_lines = [l for l in after_summary if ": " in l]
assert len(kv_lines) == 0
# ============================================================================
# run_demo TEST
# ============================================================================
class TestRunDemo:
"""Tests for run_demo execution."""
@patch.object(templates, "json_handler")
def test_run_demo_logs_operation(self, mock_json, plain_console):
console, get_output = plain_console
with patch.object(templates, "CONSOLE", console):
templates.run_demo()
mock_json.log_operation.assert_called_once_with("templates_demo")
@patch.object(templates, "json_handler")
def test_run_demo_renders_expected_content(self, mock_json, plain_console):
console, get_output = plain_console
with (
patch.object(templates, "CONSOLE", console),
patch.object(display, "CONSOLE", console),
):
templates.run_demo()
output = get_output()
assert "Creating new branch" in output
assert "Demo" in output