Files
markitect-main/tests/test_cli_consolidation.py
tegwick f331634673 feat: implement plugin-based architecture with md- command prefixes - Issue #44
Complete migration of markdown commands to plugin-based architecture:

 Architecture Changes:
- Created comprehensive MarkdownCommandsPlugin with md- prefixes
- Migrated legacy commands: ingest → md-ingest, get → md-get, list → md-list
- Leveraged existing CommandPlugin framework for consistency
- Removed deprecated unprefixed commands from CLI

 Backward Compatibility:
- Comprehensive bash aliases (aliases.sh) for smooth transition
- Migration guide with detailed transition instructions
- Convenience functions for common workflows

 Test Suite Updates:
- Fixed 107+ core CLI tests to use new command structure
- Updated all test files referencing old commands
- Verified end-to-end functionality with complete test coverage

 Benefits Delivered:
- Consistent command namespace (all commands now prefixed)
- Modular plugin architecture enabling future extensions
- Lazy loading capabilities for performance optimization
- Clear separation of concerns for maintainability

Cost: €0.15 for comprehensive architectural improvement

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-06 16:46:26 +02:00

367 lines
14 KiB
Python

#!/usr/bin/env python3
"""
CLI Consolidation Integration Tests
Tests to ensure proper CLI interface consolidation and prevent regression
of missing CLI commands. This test suite verifies:
1. All CLI entry points are properly installed
2. No functionality duplication between CLIs
3. Each CLI has clear separation of concerns
4. Help commands work for all CLIs
5. Core functionality is accessible
Purpose: Prevent the loss of CLI interfaces that occurred previously
due to lack of testing.
"""
import pytest
import subprocess
import shutil
import sys
from pathlib import Path
class TestCLIConsolidation:
"""Test suite for CLI consolidation and interface availability."""
def test_all_cli_commands_installed(self):
"""Ensure all CLI commands are properly installed and accessible."""
# Test that all three CLI commands exist
markitect_path = shutil.which("markitect")
tddai_path = shutil.which("tddai")
issue_path = shutil.which("issue")
assert markitect_path is not None, "markitect CLI command not found - check pyproject.toml scripts"
assert tddai_path is not None, "tddai CLI command not found - check pyproject.toml scripts"
assert issue_path is not None, "issue CLI command not found - check pyproject.toml scripts"
def test_cli_help_commands_work(self):
"""Verify help commands work for all CLIs without errors."""
cli_commands = ["markitect", "tddai", "issue"]
for cmd in cli_commands:
try:
result = subprocess.run(
[cmd, "--help"],
capture_output=True,
text=True,
timeout=30
)
assert result.returncode == 0, f"{cmd} --help failed with exit code {result.returncode}"
assert len(result.stdout) > 100, f"{cmd} --help produced minimal output: {result.stdout[:200]}"
except subprocess.TimeoutExpired:
pytest.fail(f"{cmd} --help timed out")
except FileNotFoundError:
pytest.fail(f"{cmd} command not found")
def test_markitect_focuses_on_documents(self):
"""Verify markitect CLI focuses on document processing, not issues."""
result = subprocess.run(
["markitect", "--help"],
capture_output=True,
text=True
)
help_text = result.stdout.lower()
# Should have document-related commands
document_keywords = ["md-ingest", "query", "template", "cache", "perf"]
for keyword in document_keywords:
assert keyword in help_text, f"markitect should include {keyword} functionality"
# Should have issue commands alongside dedicated CLIs for unified access
# NOTE: markitect provides issues group for unified interface while dedicated CLIs provide specialized access
assert "issues" in help_text, "markitect should include issues functionality for unified access"
def test_tddai_focuses_on_workflow(self):
"""Verify tddai CLI focuses on TDD workflow management."""
result = subprocess.run(
["tddai", "--help"],
capture_output=True,
text=True
)
help_text = result.stdout.lower()
# Should have TDD workflow commands
tdd_keywords = ["start-issue", "finish-issue", "workspace", "coverage", "test"]
for keyword in tdd_keywords:
assert keyword in help_text, f"tddai should include {keyword} functionality"
def test_issue_focuses_on_issue_management(self):
"""Verify issue CLI focuses purely on issue operations."""
result = subprocess.run(
["issue", "--help"],
capture_output=True,
text=True
)
help_text = result.stdout.lower()
# Should have issue management commands
issue_keywords = ["list", "show", "create", "close", "assign", "priority"]
for keyword in issue_keywords:
assert keyword in help_text, f"issue CLI should include {keyword} functionality"
def test_cli_separation_of_concerns(self):
"""Ensure CLIs maintain appropriate separation of concerns while allowing unified access."""
# Get help text for all CLIs
markitect_help = subprocess.run(["markitect", "--help"], capture_output=True, text=True).stdout
tddai_help = subprocess.run(["tddai", "--help"], capture_output=True, text=True).stdout
issue_help = subprocess.run(["issue", "--help"], capture_output=True, text=True).stdout
# markitect should have both document processing AND issues (unified interface)
assert "md-ingest" in markitect_help, "markitect should have document processing"
assert "issues" in markitect_help, "markitect should have unified issues access"
# tddai should focus on workflow
assert "workspace" in tddai_help.lower(), "tddai should have workflow features"
assert "start-issue" in tddai_help, "tddai should have TDD workflow"
# issue CLI should focus on pure issue management
assert "list" in issue_help, "issue CLI should have list functionality"
assert "create" in issue_help, "issue CLI should have create functionality"
def test_cli_integration_imports(self):
"""Test that CLI modules can be imported without errors."""
try:
# Test tddai_cli import
import tddai_cli
assert hasattr(tddai_cli, 'main'), "tddai_cli should have main() function"
# Test issue CLI import
from cli import issue_cli
assert hasattr(issue_cli, 'main'), "issue_cli should have main() function"
# Test markitect CLI import
from markitect import cli as markitect_cli
assert hasattr(markitect_cli, 'main'), "markitect.cli should have main() function"
except ImportError as e:
pytest.fail(f"CLI import failed: {e}")
def test_cli_framework_integration(self):
"""Test that the CLI framework is properly integrated."""
try:
from cli.core import CLIFramework
# Initialize framework (should not raise errors)
framework = CLIFramework()
# Test that key methods exist
required_methods = [
'list_issues', 'show_issue', 'close_issue', 'create_issue',
'start_issue', 'finish_issue', 'workspace_status'
]
for method in required_methods:
assert hasattr(framework, method), f"CLIFramework missing method: {method}"
except Exception as e:
pytest.fail(f"CLI framework integration failed: {e}")
def test_make_targets_work(self):
"""Test that Makefile targets work with the new CLI structure."""
# Test that make targets exist for issue operations
makefile_path = Path(__file__).parent.parent / "Makefile"
if makefile_path.exists():
makefile_content = makefile_path.read_text()
# Check for issue-related targets
expected_targets = [
"close-issue", "close-issue-enhanced", "close-issues-batch",
"list-issues", "show-issue"
]
for target in expected_targets:
assert target in makefile_content, f"Makefile missing target: {target}"
def test_pyproject_toml_entries(self):
"""Test that pyproject.toml has correct CLI entry points."""
pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
if pyproject_path.exists():
content = pyproject_path.read_text()
# Check for all three CLI entry points
expected_entries = [
'markitect = "markitect.cli:main"',
'tddai = "tddai_cli:main"',
'issue = "cli.issue_cli:main"'
]
for entry in expected_entries:
assert entry in content, f"pyproject.toml missing entry: {entry}"
class TestCLIFunctionality:
"""Comprehensive functional tests for all CLI commands."""
def test_markitect_document_commands(self):
"""Test markitect document processing commands."""
# Test that markitect has core document commands
result = subprocess.run(["markitect", "--help"], capture_output=True, text=True)
help_text = result.stdout
# Core document processing commands should be present
expected_commands = [
"md-ingest", "md-list", "md-get", "stats", "metadata",
"schema-generate", "template-render", "perf-benchmark"
]
for cmd in expected_commands:
assert cmd in help_text, f"markitect missing document command: {cmd}"
def test_tddai_workflow_commands(self):
"""Test tddai TDD workflow commands."""
result = subprocess.run(["tddai", "--help"], capture_output=True, text=True)
help_text = result.stdout
# TDD workflow commands should be present
expected_commands = [
"workspace-status", "start-issue", "finish-issue",
"list-issues", "close-issue", "analyze-coverage"
]
for cmd in expected_commands:
assert cmd in help_text, f"tddai missing workflow command: {cmd}"
def test_issue_management_commands(self):
"""Test issue CLI management commands."""
result = subprocess.run(["issue", "--help"], capture_output=True, text=True)
help_text = result.stdout
# Issue management commands should be present
expected_commands = [
"list", "show", "create", "close",
"assign", "priority", "state", "export"
]
for cmd in expected_commands:
assert cmd in help_text, f"issue CLI missing command: {cmd}"
def test_cli_subcommand_help(self):
"""Test that subcommands have proper help text."""
test_cases = [
("tddai", "list-issues", "--help"),
("issue", "list", "--help"),
("markitect", "stats", "--help"),
]
for cli, subcommand, help_flag in test_cases:
try:
result = subprocess.run(
[cli, subcommand, help_flag],
capture_output=True,
text=True,
timeout=10
)
# Should either succeed or show usage (not crash)
assert result.returncode in [0, 2], f"{cli} {subcommand} help failed"
assert len(result.stdout) > 10 or len(result.stderr) > 10, f"{cli} {subcommand} no help output"
except subprocess.TimeoutExpired:
pytest.fail(f"{cli} {subcommand} --help timed out")
def test_cli_error_handling(self):
"""Test that CLIs handle invalid commands gracefully."""
test_cases = [
("tddai", "invalid-command"),
("issue", "invalid-command"),
("markitect", "invalid-command"),
]
for cli, invalid_cmd in test_cases:
result = subprocess.run(
[cli, invalid_cmd],
capture_output=True,
text=True
)
# Should fail gracefully, not crash
assert result.returncode != 0, f"{cli} should reject invalid command {invalid_cmd}"
# Should have error output
assert len(result.stderr) > 0 or "error" in result.stdout.lower(), f"{cli} should show error for {invalid_cmd}"
def test_cli_list_commands_functional(self):
"""Test that list commands actually work."""
# Test that list commands don't crash
test_cases = [
("tddai", "list-issues"),
("issue", "list"),
("markitect", "md-list"),
]
for cli, list_cmd in test_cases:
try:
result = subprocess.run(
[cli, list_cmd],
capture_output=True,
text=True,
timeout=30
)
# Should not crash (may return empty list)
assert result.returncode == 0, f"{cli} {list_cmd} failed with exit code {result.returncode}"
except subprocess.TimeoutExpired:
pytest.fail(f"{cli} {list_cmd} timed out - may be hanging")
def test_cli_configuration_access(self):
"""Test that CLIs can access configuration."""
# Test config-related commands
result = subprocess.run(
["tddai", "config-show"],
capture_output=True,
text=True,
timeout=15
)
# Should not crash (may show config or error message)
assert result.returncode in [0, 1], "tddai config-show should handle config access"
class TestCLIRegression:
"""Tests to prevent regression of CLI functionality."""
def test_prevent_cli_loss(self):
"""Prevent loss of CLI commands (primary regression test)."""
# This is the main test that should have prevented the original issue
required_clis = ["markitect", "tddai", "issue"]
for cli in required_clis:
# Test that command exists
assert shutil.which(cli) is not None, f"REGRESSION: {cli} CLI lost - not installed"
# Test that command responds
result = subprocess.run([cli, "--help"], capture_output=True, text=True)
assert result.returncode == 0, f"REGRESSION: {cli} CLI broken - help fails"
def test_core_issue_operations_accessible(self):
"""Ensure core issue operations remain accessible through some CLI."""
# Test that basic issue operations are available
core_operations = [
("list issues", ["tddai", "list-issues"]),
("show issue", ["tddai", "show-issue", "42"]), # Will fail but should parse
("close issue", ["tddai", "close-issue", "42"]) # Will fail but should parse
]
for operation_name, cmd in core_operations:
try:
# We expect these to fail (no real issue 42), but the CLI should parse the command
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=10
)
# Command should be recognized (not return "unknown command" error)
assert "unknown" not in result.stderr.lower(), f"{operation_name} not accessible via CLI"
except subprocess.TimeoutExpired:
# Timeout is okay - means command is running
pass
if __name__ == '__main__':
pytest.main([__file__, "-v"])