feat: Implement comprehensive Testing Architecture Enhancement
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
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
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>
This commit is contained in:
3
tests/fixtures/__init__.py
vendored
Normal file
3
tests/fixtures/__init__.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Test fixtures and data builders for MarkiTect tests.
|
||||
"""
|
||||
332
tests/fixtures/api_responses.py
vendored
Normal file
332
tests/fixtures/api_responses.py
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
"""
|
||||
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
|
||||
302
tests/fixtures/markdown_samples.py
vendored
Normal file
302
tests/fixtures/markdown_samples.py
vendored
Normal file
@@ -0,0 +1,302 @@
|
||||
"""
|
||||
Markdown document builders and sample generators for testing.
|
||||
"""
|
||||
|
||||
from typing import Dict, List, Optional
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
class MarkdownDocumentBuilder:
|
||||
"""Builder pattern for creating test markdown documents."""
|
||||
|
||||
def __init__(self):
|
||||
self.content_parts: List[str] = []
|
||||
self.metadata: Dict[str, str] = {}
|
||||
|
||||
def with_heading(self, text: str, level: int = 1) -> "MarkdownDocumentBuilder":
|
||||
"""Add a heading to the document."""
|
||||
if level < 1 or level > 6:
|
||||
raise ValueError("Heading level must be between 1 and 6")
|
||||
|
||||
heading_marker = "#" * level
|
||||
self.content_parts.append(f"{heading_marker} {text}")
|
||||
return self
|
||||
|
||||
def with_paragraph(self, text: str) -> "MarkdownDocumentBuilder":
|
||||
"""Add a paragraph to the document."""
|
||||
self.content_parts.append(text)
|
||||
return self
|
||||
|
||||
def with_list(self, items: List[str], ordered: bool = False) -> "MarkdownDocumentBuilder":
|
||||
"""Add a list to the document."""
|
||||
if ordered:
|
||||
list_items = [f"{i+1}. {item}" for i, item in enumerate(items)]
|
||||
else:
|
||||
list_items = [f"- {item}" for item in items]
|
||||
|
||||
self.content_parts.append("\n".join(list_items))
|
||||
return self
|
||||
|
||||
def with_code_block(self, code: str, language: str = "python") -> "MarkdownDocumentBuilder":
|
||||
"""Add a code block to the document."""
|
||||
self.content_parts.append(f"```{language}\n{code}\n```")
|
||||
return self
|
||||
|
||||
def with_link(self, text: str, url: str) -> "MarkdownDocumentBuilder":
|
||||
"""Add a link to the document."""
|
||||
self.content_parts.append(f"[{text}]({url})")
|
||||
return self
|
||||
|
||||
def with_metadata(self, key: str, value: str) -> "MarkdownDocumentBuilder":
|
||||
"""Add metadata (front matter) to the document."""
|
||||
self.metadata[key] = value
|
||||
return self
|
||||
|
||||
def with_table(self, headers: List[str], rows: List[List[str]]) -> "MarkdownDocumentBuilder":
|
||||
"""Add a table to the document."""
|
||||
table_lines = []
|
||||
|
||||
# Header row
|
||||
table_lines.append("| " + " | ".join(headers) + " |")
|
||||
|
||||
# Separator row
|
||||
table_lines.append("| " + " | ".join(["-" * len(header) for header in headers]) + " |")
|
||||
|
||||
# Data rows
|
||||
for row in rows:
|
||||
table_lines.append("| " + " | ".join(row) + " |")
|
||||
|
||||
self.content_parts.append("\n".join(table_lines))
|
||||
return self
|
||||
|
||||
def with_blockquote(self, text: str) -> "MarkdownDocumentBuilder":
|
||||
"""Add a blockquote to the document."""
|
||||
quote_lines = [f"> {line}" for line in text.split("\n")]
|
||||
self.content_parts.append("\n".join(quote_lines))
|
||||
return self
|
||||
|
||||
def build(self) -> str:
|
||||
"""Build the final markdown document."""
|
||||
content = "\n\n".join(self.content_parts)
|
||||
|
||||
if self.metadata:
|
||||
metadata_lines = [f"{k}: {v}" for k, v in self.metadata.items()]
|
||||
content = "---\n" + "\n".join(metadata_lines) + "\n---\n\n" + content
|
||||
|
||||
return content
|
||||
|
||||
|
||||
class LargeMarkdownGenerator:
|
||||
"""Generator for creating large markdown documents for performance testing."""
|
||||
|
||||
def __init__(self, seed: Optional[int] = None):
|
||||
self.random = random.Random(seed)
|
||||
|
||||
def generate_document(self, size: str = "1mb") -> str:
|
||||
"""Generate a large markdown document of specified size."""
|
||||
size_bytes = self._parse_size(size)
|
||||
builder = MarkdownDocumentBuilder()
|
||||
|
||||
# Add metadata
|
||||
builder.with_metadata("title", "Large Test Document")
|
||||
builder.with_metadata("author", "Test Generator")
|
||||
builder.with_metadata("size", size)
|
||||
|
||||
# Add content until we reach target size
|
||||
current_size = 0
|
||||
section_count = 0
|
||||
|
||||
while current_size < size_bytes:
|
||||
section_count += 1
|
||||
section_title = f"Section {section_count}"
|
||||
builder.with_heading(section_title, level=2)
|
||||
|
||||
# Add paragraphs
|
||||
for _ in range(self.random.randint(3, 8)):
|
||||
paragraph = self._generate_paragraph()
|
||||
builder.with_paragraph(paragraph)
|
||||
current_size += len(paragraph) + 2 # +2 for newlines
|
||||
|
||||
if current_size >= size_bytes:
|
||||
break
|
||||
|
||||
# Add a list occasionally
|
||||
if self.random.random() < 0.3:
|
||||
items = [self._generate_sentence() for _ in range(self.random.randint(3, 7))]
|
||||
builder.with_list(items)
|
||||
current_size += sum(len(item) for item in items) + len(items) * 3 # Approximate
|
||||
|
||||
# Add a code block occasionally
|
||||
if self.random.random() < 0.2:
|
||||
code = self._generate_code_block()
|
||||
builder.with_code_block(code)
|
||||
current_size += len(code) + 10 # +10 for code block markers
|
||||
|
||||
return builder.build()
|
||||
|
||||
def _parse_size(self, size: str) -> int:
|
||||
"""Parse size string (e.g., '1mb', '500kb') to bytes."""
|
||||
size = size.lower()
|
||||
if size.endswith("kb"):
|
||||
return int(size[:-2]) * 1024
|
||||
elif size.endswith("mb"):
|
||||
return int(size[:-2]) * 1024 * 1024
|
||||
elif size.endswith("gb"):
|
||||
return int(size[:-2]) * 1024 * 1024 * 1024
|
||||
else:
|
||||
return int(size)
|
||||
|
||||
def _generate_paragraph(self) -> str:
|
||||
"""Generate a paragraph of random text."""
|
||||
sentences = []
|
||||
for _ in range(self.random.randint(3, 8)):
|
||||
sentences.append(self._generate_sentence())
|
||||
return " ".join(sentences)
|
||||
|
||||
def _generate_sentence(self) -> str:
|
||||
"""Generate a random sentence."""
|
||||
words = []
|
||||
for _ in range(self.random.randint(5, 15)):
|
||||
words.append(self._generate_word())
|
||||
|
||||
sentence = " ".join(words).capitalize()
|
||||
return sentence + "."
|
||||
|
||||
def _generate_word(self) -> str:
|
||||
"""Generate a random word."""
|
||||
length = self.random.randint(3, 12)
|
||||
return "".join(self.random.choices(string.ascii_lowercase, k=length))
|
||||
|
||||
def _generate_code_block(self) -> str:
|
||||
"""Generate a random code block."""
|
||||
lines = []
|
||||
for _ in range(self.random.randint(5, 15)):
|
||||
line = self._generate_code_line()
|
||||
lines.append(line)
|
||||
return "\n".join(lines)
|
||||
|
||||
def _generate_code_line(self) -> str:
|
||||
"""Generate a line of code-like text."""
|
||||
templates = [
|
||||
"def {func_name}({params}):",
|
||||
" return {expression}",
|
||||
"if {condition}:",
|
||||
" {statement}",
|
||||
"# {comment}",
|
||||
"class {class_name}:",
|
||||
" self.{attr} = {value}",
|
||||
"import {module}",
|
||||
"from {module} import {name}",
|
||||
]
|
||||
|
||||
template = self.random.choice(templates)
|
||||
variables = {
|
||||
"func_name": self._generate_word(),
|
||||
"params": ", ".join([self._generate_word() for _ in range(self.random.randint(0, 3))]),
|
||||
"expression": f"{self._generate_word()}({self._generate_word()})",
|
||||
"condition": f"{self._generate_word()} == {self.random.randint(1, 100)}",
|
||||
"statement": f"{self._generate_word()} = {self.random.randint(1, 100)}",
|
||||
"comment": " ".join([self._generate_word() for _ in range(self.random.randint(2, 6))]),
|
||||
"class_name": self._generate_word().capitalize(),
|
||||
"attr": self._generate_word(),
|
||||
"value": str(self.random.randint(1, 100)),
|
||||
"module": self._generate_word(),
|
||||
"name": self._generate_word(),
|
||||
}
|
||||
|
||||
return template.format(**variables)
|
||||
|
||||
|
||||
# Pre-built sample documents
|
||||
SAMPLE_SIMPLE_DOCUMENT = """# Simple Document
|
||||
|
||||
This is a simple test document.
|
||||
|
||||
## Features
|
||||
|
||||
- Feature 1
|
||||
- Feature 2
|
||||
- Feature 3
|
||||
"""
|
||||
|
||||
SAMPLE_COMPLEX_DOCUMENT = (
|
||||
MarkdownDocumentBuilder()
|
||||
.with_metadata("title", "Complex Test Document")
|
||||
.with_metadata("author", "Test Suite")
|
||||
.with_metadata("tags", "test, complex, sample")
|
||||
.with_heading("Complex Test Document")
|
||||
.with_paragraph("This is a complex test document with various markdown features.")
|
||||
.with_heading("Table of Contents", level=2)
|
||||
.with_list([
|
||||
"Introduction",
|
||||
"Features",
|
||||
"Examples",
|
||||
"Conclusion"
|
||||
], ordered=True)
|
||||
.with_heading("Introduction", level=2)
|
||||
.with_paragraph("This document demonstrates various markdown features.")
|
||||
.with_blockquote("This is an important note about the document.")
|
||||
.with_heading("Features", level=2)
|
||||
.with_list([
|
||||
"**Bold text**",
|
||||
"*Italic text*",
|
||||
"`Code inline`",
|
||||
"[Links](https://example.com)"
|
||||
])
|
||||
.with_heading("Code Example", level=3)
|
||||
.with_code_block('''def hello_world():
|
||||
"""Print hello world message."""
|
||||
print("Hello, World!")
|
||||
return "success"''')
|
||||
.with_heading("Data Table", level=3)
|
||||
.with_table(
|
||||
["Name", "Type", "Description"],
|
||||
[
|
||||
["title", "string", "Document title"],
|
||||
["author", "string", "Document author"],
|
||||
["tags", "array", "Document tags"]
|
||||
]
|
||||
)
|
||||
.with_heading("Conclusion", level=2)
|
||||
.with_paragraph("This document shows the power of markdown for documentation.")
|
||||
.build()
|
||||
)
|
||||
|
||||
SAMPLE_TECHNICAL_DOCUMENT = (
|
||||
MarkdownDocumentBuilder()
|
||||
.with_metadata("title", "API Documentation")
|
||||
.with_metadata("version", "1.0.0")
|
||||
.with_metadata("category", "technical")
|
||||
.with_heading("API Documentation")
|
||||
.with_paragraph("This document describes the REST API endpoints.")
|
||||
.with_heading("Authentication", level=2)
|
||||
.with_paragraph("All API requests require authentication via API key.")
|
||||
.with_code_block('''curl -H "Authorization: Bearer YOUR_API_KEY" \\
|
||||
https://api.example.com/v1/endpoint''', "bash")
|
||||
.with_heading("Endpoints", level=2)
|
||||
.with_heading("GET /users", level=3)
|
||||
.with_paragraph("Retrieve a list of users.")
|
||||
.with_table(
|
||||
["Parameter", "Type", "Required", "Description"],
|
||||
[
|
||||
["limit", "integer", "No", "Maximum number of results"],
|
||||
["offset", "integer", "No", "Number of results to skip"],
|
||||
["filter", "string", "No", "Filter criteria"]
|
||||
]
|
||||
)
|
||||
.with_heading("Response", level=4)
|
||||
.with_code_block('''{
|
||||
"users": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"offset": 0,
|
||||
"limit": 10
|
||||
}''', "json")
|
||||
.build()
|
||||
)
|
||||
Reference in New Issue
Block a user