feat: implement tddai Python library for TDD workspace management

- Create comprehensive tddai package with workspace, issue fetcher, and test generator modules
- Add Python CLI interface (tddai_cli.py) to replace complex Makefile shell logic
- Update Makefile targets to use Python CLI for better maintainability
- Implement proper behavior-based tests instead of file existence checks
- Add workspace lifecycle management (create, active, finish, cleanup)
- Add issue fetching from Gitea API with error handling
- Add comprehensive test coverage with 19 passing tests
- Support environment variable configuration for different deployments

This addresses issue #11: Setup TDD workspace infrastructure
All tests pass and the system achieves green state before commit.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-22 02:04:19 +02:00
parent b03160437e
commit 5155a548eb
13 changed files with 1360 additions and 176 deletions

1
tests/test_example.py Normal file
View File

@@ -0,0 +1 @@
# Test content

View File

@@ -0,0 +1 @@
# Complete test content

View File

@@ -0,0 +1,6 @@
"""Test for issue #11."""
import pytest
def test_feature():
"""Test the feature implementation."""
assert True # Replace with actual test

View File

@@ -0,0 +1,194 @@
"""
Test TDD workflow integration for workspace infrastructure.
This test validates issue #11: Setup TDD workspace infrastructure
- Tests complete workflow from start to finish
- Validates integration between workspace and main codebase
- Tests cleanup and finalization processes
"""
import pytest
import subprocess
import tempfile
import shutil
from pathlib import Path
from unittest.mock import patch
from tddai import WorkspaceManager, IssueFetcher
from tddai.config import TddaiConfig
class TestWorkflowIntegration:
"""Test suite for complete TDD workflow integration."""
@pytest.fixture
def temp_workspace(self):
"""Create a temporary workspace for testing."""
temp_dir = Path(tempfile.mkdtemp())
config = TddaiConfig(workspace_dir=temp_dir / ".markitect_workspace")
yield config
shutil.rmtree(temp_dir)
def test_make_workspace_status_command(self):
"""Test that make workspace-status command works correctly."""
result = subprocess.run(['make', 'workspace-status'],
capture_output=True, text=True)
assert result.returncode == 0, "workspace-status command should succeed"
# Should show clean workspace when no active workspace
assert ("No active issue workspace" in result.stdout or
"Workspace directory exists but no current issue file" in result.stdout)
def test_make_add_test_command_without_workspace(self):
"""Test that make add-test provides proper error when no workspace."""
result = subprocess.run(['make', 'add-test'],
capture_output=True, text=True)
assert result.returncode != 0, "add-test command should fail when no workspace"
assert "No active issue workspace" in result.stdout
def test_cli_integration_basic(self):
"""Test that CLI script can be imported and basic functions exist."""
import tddai_cli
# Test that main functions exist
assert hasattr(tddai_cli, 'workspace_status')
assert hasattr(tddai_cli, 'start_issue')
assert hasattr(tddai_cli, 'finish_issue')
assert hasattr(tddai_cli, 'main')
def test_workspace_to_main_integration(self, temp_workspace):
"""Test moving tests from workspace to main tests directory."""
manager = WorkspaceManager(temp_workspace)
mock_issue_data = {
'number': 11,
'title': 'Test Issue',
'body': 'Test Description',
'state': 'open',
'created_at': '2025-01-01T00:00:00Z',
'html_url': 'http://example.com/issues/11',
'assignee': None,
'labels': []
}
# Create workspace
workspace = manager.create_workspace(mock_issue_data)
# Add a test file to workspace
test_file = workspace.tests_dir / "test_issue_11_feature.py"
test_content = '''"""Test for issue #11."""
import pytest
def test_feature():
"""Test the feature implementation."""
assert True # Replace with actual test
'''
test_file.write_text(test_content)
# Finish workspace (should move tests)
finished_workspace = manager.finish_workspace()
# Verify test was moved to main tests directory
main_test_file = temp_workspace.tests_dir / "test_issue_11_feature.py"
assert main_test_file.exists()
assert main_test_file.read_text() == test_content
# Verify workspace is cleaned up
assert not temp_workspace.workspace_dir.exists()
def test_workspace_cleanup_process(self, temp_workspace):
"""Test that workspace cleanup removes temporary files."""
manager = WorkspaceManager(temp_workspace)
mock_issue_data = {
'number': 11,
'title': 'Test Issue',
'body': 'Test Description',
'state': 'open'
}
# Create workspace
workspace = manager.create_workspace(mock_issue_data)
# Verify workspace exists
assert workspace.workspace_dir.exists()
assert workspace.issue_dir.exists()
# Clean up
manager.cleanup_workspace()
# Verify cleanup
assert not workspace.workspace_dir.exists()
def test_gitignore_excludes_workspace(self):
"""Test that workspace files are properly excluded from git."""
gitignore_path = Path(".gitignore")
assert gitignore_path.exists(), "Gitignore file should exist"
with open(gitignore_path, 'r') as f:
gitignore_content = f.read()
assert ".markitect_workspace/" in gitignore_content, \
"Workspace should be excluded from git"
@patch('tddai.issue_fetcher.subprocess.run')
def test_issue_fetcher_integration(self, mock_run, temp_workspace):
"""Test that IssueFetcher properly integrates with API."""
# Mock successful curl response
mock_run.return_value.returncode = 0
mock_run.return_value.stdout = """{
"number": 11,
"title": "Setup TDD workspace infrastructure",
"body": "Create workspace management system",
"state": "open",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"html_url": "http://example.com/issues/11",
"assignee": null,
"labels": []
}"""
fetcher = IssueFetcher(temp_workspace)
issue = fetcher.fetch_issue(11)
assert issue.number == 11
assert issue.title == "Setup TDD workspace infrastructure"
assert issue.state == "open"
def test_complete_workflow_cycle(self, temp_workspace):
"""Test complete workflow from start to finish."""
manager = WorkspaceManager(temp_workspace)
mock_issue_data = {
'number': 11,
'title': 'Setup TDD workspace infrastructure',
'body': 'Create workspace management system for TDD workflow',
'state': 'open',
'created_at': '2025-01-01T00:00:00Z',
'html_url': 'http://example.com/issues/11',
'assignee': None,
'labels': []
}
# 1. Start with clean workspace
assert manager.get_status().name == "CLEAN"
# 2. Create workspace
workspace = manager.create_workspace(mock_issue_data)
assert manager.get_status().name == "ACTIVE"
assert workspace.issue_number == 11
# 3. Add test files
test_file = workspace.tests_dir / "test_issue_11_complete.py"
test_file.write_text("# Complete test content")
# 4. Finish workspace
finished = manager.finish_workspace()
assert finished.issue_number == 11
assert manager.get_status().name == "CLEAN"
# 5. Verify test moved to main
main_test = temp_workspace.tests_dir / "test_issue_11_complete.py"
assert main_test.exists()
assert main_test.read_text() == "# Complete test content"

View File

@@ -0,0 +1,156 @@
"""
Test workspace creation functionality for TDD infrastructure.
This test validates issue #11: Setup TDD workspace infrastructure
- Tests workspace creation from issue numbers
- Validates workspace structure and files
- Ensures proper error handling
"""
import pytest
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock, patch
from tddai import WorkspaceManager, IssueFetcher, WorkspaceStatus, WorkspaceError, IssueError
from tddai.config import TddaiConfig
class TestWorkspaceCreation:
"""Test suite for workspace creation functionality."""
@pytest.fixture
def temp_workspace(self):
"""Create a temporary workspace for testing."""
temp_dir = Path(tempfile.mkdtemp())
config = TddaiConfig(workspace_dir=temp_dir / ".markitect_workspace")
yield config
shutil.rmtree(temp_dir)
@pytest.fixture
def mock_issue_data(self):
"""Mock issue data for testing."""
return {
'number': 11,
'title': 'Setup TDD workspace infrastructure',
'body': 'Create workspace management system for TDD workflow',
'state': 'open',
'created_at': '2025-01-01T00:00:00Z',
'html_url': 'http://example.com/issues/11',
'assignee': None,
'labels': []
}
def test_workspace_manager_initialization(self, temp_workspace):
"""Test that WorkspaceManager can be initialized."""
manager = WorkspaceManager(temp_workspace)
assert manager.config == temp_workspace
def test_workspace_status_clean_initially(self, temp_workspace):
"""Test that workspace status is clean when no workspace exists."""
manager = WorkspaceManager(temp_workspace)
status = manager.get_status()
assert status == WorkspaceStatus.CLEAN
def test_workspace_creation_from_issue_data(self, temp_workspace, mock_issue_data):
"""Test that workspace can be created from issue data."""
manager = WorkspaceManager(temp_workspace)
workspace = manager.create_workspace(mock_issue_data)
assert workspace.issue_number == 11
assert workspace.issue_title == 'Setup TDD workspace infrastructure'
assert workspace.workspace_dir == temp_workspace.workspace_dir
# Verify workspace status changes to active
status = manager.get_status()
assert status == WorkspaceStatus.ACTIVE
def test_workspace_directory_structure_created(self, temp_workspace, mock_issue_data):
"""Test that workspace creates proper directory structure."""
manager = WorkspaceManager(temp_workspace)
workspace = manager.create_workspace(mock_issue_data)
assert workspace.workspace_dir.exists()
assert workspace.issue_dir.exists()
assert workspace.tests_dir.exists()
def test_workspace_metadata_files_created(self, temp_workspace, mock_issue_data):
"""Test that workspace creates required metadata files."""
manager = WorkspaceManager(temp_workspace)
workspace = manager.create_workspace(mock_issue_data)
assert workspace.requirements_file.exists()
assert workspace.test_plan_file.exists()
assert temp_workspace.current_issue_path.exists()
def test_current_issue_metadata_content(self, temp_workspace, mock_issue_data):
"""Test that current issue metadata is properly stored."""
manager = WorkspaceManager(temp_workspace)
manager.create_workspace(mock_issue_data)
current_workspace = manager.get_current_workspace()
assert current_workspace.issue_number == 11
assert current_workspace.issue_title == 'Setup TDD workspace infrastructure'
assert current_workspace.issue_state == 'open'
def test_workspace_prevents_multiple_active_issues(self, temp_workspace, mock_issue_data):
"""Test that only one workspace can be active at a time."""
manager = WorkspaceManager(temp_workspace)
manager.create_workspace(mock_issue_data)
# Try to create another workspace
second_issue_data = mock_issue_data.copy()
second_issue_data['number'] = 12
second_issue_data['title'] = 'Different issue'
with pytest.raises(WorkspaceError, match="Workspace already active"):
manager.create_workspace(second_issue_data)
@patch('tddai.issue_fetcher.subprocess.run')
def test_issue_fetcher_handles_invalid_issue(self, mock_run, temp_workspace):
"""Test error handling for invalid issue numbers."""
# Mock curl response for non-existent issue
mock_run.return_value.returncode = 0
mock_run.return_value.stdout = '{"message": "404 Not Found"}'
fetcher = IssueFetcher(temp_workspace)
with pytest.raises(IssueError, match="not found"):
fetcher.fetch_issue(999)
def test_workspace_cleanup(self, temp_workspace, mock_issue_data):
"""Test that workspace can be cleaned up properly."""
manager = WorkspaceManager(temp_workspace)
manager.create_workspace(mock_issue_data)
# Verify workspace exists
assert manager.get_status() == WorkspaceStatus.ACTIVE
# Clean up
manager.cleanup_workspace()
# Verify workspace is clean
assert manager.get_status() == WorkspaceStatus.CLEAN
assert not temp_workspace.workspace_dir.exists()
def test_workspace_finish_moves_tests(self, temp_workspace, mock_issue_data):
"""Test that finishing workspace moves tests to main directory."""
manager = WorkspaceManager(temp_workspace)
workspace = manager.create_workspace(mock_issue_data)
# Create a test file in workspace
test_file = workspace.tests_dir / "test_example.py"
test_file.write_text("# Test content")
# Finish workspace
finished_workspace = manager.finish_workspace()
assert finished_workspace.issue_number == 11
assert manager.get_status() == WorkspaceStatus.CLEAN
# Verify test was moved
main_test_file = temp_workspace.tests_dir / "test_example.py"
assert main_test_file.exists()
assert main_test_file.read_text() == "# Test content"