feat: Complete CLI consolidation - fix redundancy and missing interfaces
🎯 MAJOR CLI ARCHITECTURE CONSOLIDATION: ✅ Added Missing CLI Entry Points: • tddai = "tddai_cli:main" - TDD workflow management • issue = "cli.issue_cli:main" - Pure issue management • All three CLIs now properly installed: markitect, tddai, issue 🧹 Eliminated Functionality Redundancy: • Removed issue commands from markitect/cli.py (clean separation) • MarkiTect now focuses purely on document processing • TDD workflow in tddai CLI, issue management in issue CLI 🏗️ Clean Architecture Implementation: • Created cli/issue_cli.py - Dedicated pure issue management • Enhanced cli/commands/export.py with export_issues_csv/json • Updated cli/core.py with proper export method delegation • Fixed pyproject.toml to include all required packages 🧪 Comprehensive Testing: • Added tests/test_cli_consolidation.py - Prevents CLI regression • Tests ensure all CLIs are installed and functional • Tests verify no functionality duplication • Regression protection against missing CLI commands 📋 Clear Separation of Concerns: • markitect CLI - Document processing, templates, performance • tddai CLI - TDD workflow, workspace management, coverage • issue CLI - Pure issue operations, project management, export 🔧 Package Configuration: • Updated pyproject.toml to include cli*, tddai*, services*, etc. • Added py-modules for tddai_cli standalone module • Fixed import paths and dependencies This consolidation resolves the major redundancy identified in issues functionality and ensures proper CLI interfaces are available and tested. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
245
tests/test_cli_consolidation.py
Normal file
245
tests/test_cli_consolidation.py
Normal file
@@ -0,0 +1,245 @@
|
||||
#!/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 = ["ingest", "query", "template", "cache", "perf"]
|
||||
for keyword in document_keywords:
|
||||
assert keyword in help_text, f"markitect should include {keyword} functionality"
|
||||
|
||||
# Should NOT have issue commands (they're moved to dedicated CLIs)
|
||||
issue_keywords = ["issue", "close-issue", "create-issue"]
|
||||
for keyword in issue_keywords:
|
||||
assert keyword not in help_text, f"markitect should not include {keyword} - use 'issue' or 'tddai' CLI"
|
||||
|
||||
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_no_functionality_duplication(self):
|
||||
"""Ensure functionality is not duplicated across CLIs."""
|
||||
# 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
|
||||
|
||||
# Check that markitect doesn't duplicate issue functionality
|
||||
markitect_commands = set()
|
||||
for line in markitect_help.split('\n'):
|
||||
if line.strip().startswith('markitect '):
|
||||
cmd = line.strip().split()[1] if len(line.strip().split()) > 1 else ""
|
||||
if cmd:
|
||||
markitect_commands.add(cmd)
|
||||
|
||||
# Issue commands should not be in markitect
|
||||
issue_specific = {"list-issues", "show-issue", "create-issue", "close-issue"}
|
||||
overlap = markitect_commands.intersection(issue_specific)
|
||||
assert len(overlap) == 0, f"markitect duplicates issue commands: {overlap}"
|
||||
|
||||
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 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"])
|
||||
Reference in New Issue
Block a user