Files
markitect-main/tests/fixtures/api_responses.py
tegwick 21a5d1d734
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
feat: Implement comprehensive Testing Architecture Enhancement
Establishes robust testing framework with clean architecture patterns:

## Phase 1: Test Infrastructure Foundation
- Global test configuration with pytest.ini and conftest.py
- Isolated test workspaces and environment management
- Comprehensive fixture library for all test types
- Test requirements and dependency management

## Phase 2: Advanced Testing Patterns
- Test builders using builder pattern for domain objects
- Mock factories for repositories, services, and configs
- API response builders for external system simulation
- Enhanced unit tests with proper mocking and isolation

## Phase 3: Test Performance and Quality
- Performance testing framework with benchmarks
- Memory usage monitoring and leak detection
- Custom assertions for domain-specific validation
- Parametrized testing for comprehensive coverage

## Phase 4: CI/CD Integration
- GitHub Actions workflow for automated testing
- Multi-stage testing: unit → integration → e2e → performance
- Code quality checks with flake8, mypy, black, isort
- Security scanning with safety and bandit

## Testing Architecture Benefits
 100+ new test infrastructure components
 Standardized test organization (unit/integration/e2e)
 Mock-based testing with no external dependencies
 Performance regression detection
 Comprehensive fixture library
 CI/CD pipeline with quality gates

The testing framework supports the domain logic separation and provides
a solid foundation for maintaining high code quality as the system evolves.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-26 22:36:35 +02:00

332 lines
11 KiB
Python

"""
API response builders and mock data for testing external integrations.
"""
from typing import Dict, List, Any, Optional
from datetime import datetime, timezone
class GiteaApiResponseBuilder:
"""Builder for creating mock Gitea API responses."""
def __init__(self):
self.issue_data = {
"number": 1,
"title": "Test Issue",
"body": "Test issue description",
"state": "open",
"labels": [],
"milestone": None,
"assignees": [],
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"closed_at": None,
"html_url": "https://test-gitea.com/test/repo/issues/1",
"user": {
"login": "testuser",
"id": 1,
"avatar_url": "https://test-gitea.com/avatars/1"
}
}
def with_number(self, number: int) -> "GiteaApiResponseBuilder":
"""Set issue number."""
self.issue_data["number"] = number
self.issue_data["html_url"] = f"https://test-gitea.com/test/repo/issues/{number}"
return self
def with_title(self, title: str) -> "GiteaApiResponseBuilder":
"""Set issue title."""
self.issue_data["title"] = title
return self
def with_body(self, body: str) -> "GiteaApiResponseBuilder":
"""Set issue body/description."""
self.issue_data["body"] = body
return self
def with_state(self, state: str) -> "GiteaApiResponseBuilder":
"""Set issue state (open/closed)."""
if state not in ["open", "closed"]:
raise ValueError("State must be 'open' or 'closed'")
self.issue_data["state"] = state
if state == "closed" and self.issue_data["closed_at"] is None:
self.issue_data["closed_at"] = "2025-01-02T00:00:00Z"
return self
def with_labels(self, *labels: str) -> "GiteaApiResponseBuilder":
"""Add labels to the issue."""
self.issue_data["labels"] = [
{
"id": i + 1,
"name": label,
"color": "red",
"description": f"Label: {label}"
}
for i, label in enumerate(labels)
]
return self
def with_milestone(self, title: str, id: int = 1, state: str = "open") -> "GiteaApiResponseBuilder":
"""Add milestone to the issue."""
self.issue_data["milestone"] = {
"id": id,
"title": title,
"description": f"Milestone: {title}",
"state": state,
"open_issues": 5,
"closed_issues": 3,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"due_date": "2025-12-31T23:59:59Z"
}
return self
def with_assignees(self, *usernames: str) -> "GiteaApiResponseBuilder":
"""Add assignees to the issue."""
self.issue_data["assignees"] = [
{
"login": username,
"id": i + 1,
"avatar_url": f"https://test-gitea.com/avatars/{i + 1}"
}
for i, username in enumerate(usernames)
]
return self
def with_timestamps(self, created_at: str, updated_at: str, closed_at: Optional[str] = None) -> "GiteaApiResponseBuilder":
"""Set issue timestamps."""
self.issue_data["created_at"] = created_at
self.issue_data["updated_at"] = updated_at
if closed_at:
self.issue_data["closed_at"] = closed_at
return self
def build(self) -> Dict[str, Any]:
"""Build the final issue data."""
return self.issue_data.copy()
class GiteaProjectResponseBuilder:
"""Builder for creating mock Gitea project/repository responses."""
def __init__(self):
self.project_data = {
"id": 1,
"name": "test-repo",
"full_name": "test/test-repo",
"description": "Test repository",
"private": False,
"fork": False,
"html_url": "https://test-gitea.com/test/test-repo",
"clone_url": "https://test-gitea.com/test/test-repo.git",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"owner": {
"login": "test",
"id": 1,
"avatar_url": "https://test-gitea.com/avatars/1"
},
"permissions": {
"admin": True,
"push": True,
"pull": True
},
"open_issues_count": 5,
"stargazers_count": 10,
"watchers_count": 3,
"forks_count": 2,
"size": 1024,
"default_branch": "main",
"archived": False,
"disabled": False
}
def with_name(self, name: str, owner: str = "test") -> "GiteaProjectResponseBuilder":
"""Set repository name and owner."""
self.project_data["name"] = name
self.project_data["full_name"] = f"{owner}/{name}"
self.project_data["html_url"] = f"https://test-gitea.com/{owner}/{name}"
self.project_data["clone_url"] = f"https://test-gitea.com/{owner}/{name}.git"
self.project_data["owner"]["login"] = owner
return self
def with_description(self, description: str) -> "GiteaProjectResponseBuilder":
"""Set repository description."""
self.project_data["description"] = description
return self
def with_visibility(self, private: bool) -> "GiteaProjectResponseBuilder":
"""Set repository visibility."""
self.project_data["private"] = private
return self
def with_stats(self, open_issues: int = 5, stars: int = 10, watchers: int = 3, forks: int = 2) -> "GiteaProjectResponseBuilder":
"""Set repository statistics."""
self.project_data["open_issues_count"] = open_issues
self.project_data["stargazers_count"] = stars
self.project_data["watchers_count"] = watchers
self.project_data["forks_count"] = forks
return self
def with_permissions(self, admin: bool = True, push: bool = True, pull: bool = True) -> "GiteaProjectResponseBuilder":
"""Set user permissions."""
self.project_data["permissions"] = {
"admin": admin,
"push": push,
"pull": pull
}
return self
def build(self) -> Dict[str, Any]:
"""Build the final project data."""
return self.project_data.copy()
class GiteaMilestoneResponseBuilder:
"""Builder for creating mock Gitea milestone responses."""
def __init__(self):
self.milestone_data = {
"id": 1,
"title": "Version 1.0",
"description": "First release milestone",
"state": "open",
"open_issues": 5,
"closed_issues": 3,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"due_date": "2025-12-31T23:59:59Z"
}
def with_id(self, id: int) -> "GiteaMilestoneResponseBuilder":
"""Set milestone ID."""
self.milestone_data["id"] = id
return self
def with_title(self, title: str) -> "GiteaMilestoneResponseBuilder":
"""Set milestone title."""
self.milestone_data["title"] = title
return self
def with_description(self, description: str) -> "GiteaMilestoneResponseBuilder":
"""Set milestone description."""
self.milestone_data["description"] = description
return self
def with_state(self, state: str) -> "GiteaMilestoneResponseBuilder":
"""Set milestone state."""
if state not in ["open", "closed"]:
raise ValueError("State must be 'open' or 'closed'")
self.milestone_data["state"] = state
return self
def with_issue_counts(self, open_issues: int, closed_issues: int) -> "GiteaMilestoneResponseBuilder":
"""Set issue counts."""
self.milestone_data["open_issues"] = open_issues
self.milestone_data["closed_issues"] = closed_issues
return self
def with_due_date(self, due_date: str) -> "GiteaMilestoneResponseBuilder":
"""Set milestone due date."""
self.milestone_data["due_date"] = due_date
return self
def build(self) -> Dict[str, Any]:
"""Build the final milestone data."""
return self.milestone_data.copy()
# Pre-built common responses
SAMPLE_ISSUE_RESPONSE = (
GiteaApiResponseBuilder()
.with_number(123)
.with_title("Sample Issue")
.with_body("This is a sample issue for testing")
.with_labels("bug", "priority:high", "status:in-progress")
.with_milestone("Version 1.0")
.with_assignees("testuser")
.build()
)
SAMPLE_PROJECT_RESPONSE = (
GiteaProjectResponseBuilder()
.with_name("sample-project", "testorg")
.with_description("A sample project for testing")
.with_stats(open_issues=10, stars=25, watchers=8, forks=3)
.build()
)
SAMPLE_MILESTONE_RESPONSE = (
GiteaMilestoneResponseBuilder()
.with_title("Version 2.0")
.with_description("Second major release")
.with_issue_counts(8, 12)
.with_due_date("2025-06-30T23:59:59Z")
.build()
)
# Error responses
ERROR_RESPONSES = {
"not_found": {
"message": "404 Not Found",
"documentation_url": "https://docs.gitea.io/en-us/api-usage/"
},
"unauthorized": {
"message": "401 Unauthorized",
"documentation_url": "https://docs.gitea.io/en-us/api-usage/"
},
"forbidden": {
"message": "403 Forbidden",
"documentation_url": "https://docs.gitea.io/en-us/api-usage/"
},
"validation_failed": {
"message": "Validation Failed",
"errors": [
{
"resource": "Issue",
"field": "title",
"code": "missing_field"
}
]
},
"rate_limit": {
"message": "API rate limit exceeded",
"documentation_url": "https://docs.gitea.io/en-us/api-usage/"
}
}
def get_paginated_response(items: List[Dict[str, Any]], page: int = 1, per_page: int = 30) -> Dict[str, Any]:
"""Create a paginated response wrapper."""
start_idx = (page - 1) * per_page
end_idx = start_idx + per_page
page_items = items[start_idx:end_idx]
return {
"data": page_items,
"pagination": {
"page": page,
"per_page": per_page,
"total": len(items),
"total_pages": (len(items) + per_page - 1) // per_page,
"has_next": end_idx < len(items),
"has_prev": page > 1
}
}
def create_bulk_issues(count: int, base_number: int = 1) -> List[Dict[str, Any]]:
"""Create a list of test issues for bulk operations."""
issues = []
for i in range(count):
issue = (
GiteaApiResponseBuilder()
.with_number(base_number + i)
.with_title(f"Test Issue {base_number + i}")
.with_body(f"Description for test issue {base_number + i}")
.with_labels("test")
.build()
)
issues.append(issue)
return issues