Files
markitect-main/tests/test_l3_domain_performance_validation.py
tegwick b13de9b2ad feat: Revolutionary Test Architecture - 7-Layer Organization with Advanced Testing Capabilities
ARCHITECTURAL MILESTONE: Complete transformation of test suite from issue-based to sophisticated
architectural layer organization with 348 tests across 7 layers (Foundation → Infrastructure →
Integration → Domain → Service → Application → Presentation).

Major Components:

🏗️ ARCHITECTURAL TEST ORGANIZATION:
• Renamed 23 test files to architectural layers (e.g. test_parser.py → test_l7_foundation_markdown_parsing.py)
• Created reverse dependency execution order for 60-80% faster feedback
• Foundation layer (10 tests, ~9s) provides immediate failure detection
• Complete dependency mapping across all 7 architectural layers

🎯 ADVANCED TEST RUNNERS:
• run_architectural_tests.py - Reverse dependency execution with performance metrics
• run_randomized_tests.py - Seed-based randomization for dependency detection
• Comprehensive error handling and colored output for optimal UX
• Support for layer-specific execution and early termination on failures

📋 COMPREHENSIVE DOCUMENTATION:
• ARCHITECTURE.md - 7-layer architecture blueprint with migration strategy
• CAPABILITIES.md - Complete inventory of 73+ system capabilities across 15 categories
• TEST_ARCHITECTURE.md - Detailed test execution strategy and naming conventions
• ARCHITECTURAL_CHAOS_TESTING_ISSUE.md - Chaos engineering gameplan (Issue #35)

🔧 MAKEFILE INTEGRATION:
• 15+ new testing targets (test-arch, test-foundation, test-random, etc.)
• Layer-specific execution (test-infrastructure, test-domain, test-service)
• Advanced options (test-quick, test-layers, test-random-repeat)
• Comprehensive help system with organized testing categories

🎲 RANDOMIZED TESTING:
• Seed-based reproducible test execution for debugging
• Multi-iteration testing to detect flaky tests and hidden dependencies
• Enhanced randomization support with pytest-randomly integration
• Performance analysis across different execution orders

🚀 PERFORMANCE OPTIMIZATION:
• Foundation-first execution prevents cascade failure debugging
• Quick testing (foundation + infrastructure) completes in ~22 seconds
• Layer isolation enables targeted debugging and development
• Optimal feedback loops for architectural development

This revolutionary testing infrastructure establishes MarkiTect as having enterprise-grade
test organization with architectural principles, performance optimization, and advanced
testing methodologies including chaos engineering foundations.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-29 12:18:25 +02:00

363 lines
14 KiB
Python

"""
Performance tests for domain operations.
Demonstrates:
- Domain operation performance benchmarks
- Memory usage monitoring
- Bulk operation testing
- Performance regression detection
"""
import pytest
import time
import gc
from typing import List
from domain.issues.models import Issue, Label, IssueState
from domain.issues.services import IssueStatusService, IssueValidationService
from domain.projects.models import Project, Milestone, ProjectState
from domain.projects.services import ProjectManagementService
from tests.utils.test_builders import IssueBuilder, LabelBuilder, MilestoneBuilder, ProjectBuilder
from tests.utils.assertions import assert_performance_within_bounds, assert_memory_usage_within_bounds
class TestDomainPerformance:
"""Performance tests for domain operations."""
def test_issue_creation_performance(self, performance_timer):
"""Test performance of creating many issues."""
# Arrange
issue_count = 1000
# Act
performance_timer.start()
issues = []
for i in range(issue_count):
issue = (IssueBuilder()
.with_number(i + 1)
.with_title(f"Performance Test Issue {i + 1}")
.as_bug()
.with_priority("medium")
.build())
issues.append(issue)
performance_timer.stop()
# Assert
assert len(issues) == issue_count
assert_performance_within_bounds(performance_timer.elapsed, 1.0, f"creating {issue_count} issues")
print(f"Created {issue_count} issues in {performance_timer.elapsed:.3f}s ({issue_count/performance_timer.elapsed:.0f} issues/sec)")
def test_label_categorization_performance(self, performance_timer):
"""Test performance of label categorization operations."""
# Arrange
issues = []
for i in range(500):
issue = (IssueBuilder()
.with_number(i + 1)
.with_title(f"Issue {i + 1}")
.with_labels(
"bug", "priority:high", "status:in-progress",
"frontend", "needs-testing", "documentation"
)
.build())
issues.append(issue)
# Act
performance_timer.start()
categorized_results = []
for issue in issues:
categories = issue.categorize_labels()
categorized_results.append(categories)
performance_timer.stop()
# Assert
assert len(categorized_results) == 500
assert_performance_within_bounds(performance_timer.elapsed, 0.5, "categorizing labels for 500 issues")
# Verify categorization correctness
for categories in categorized_results:
assert "bug" in categories.type_labels
assert "priority:high" in categories.priority_labels
assert "status:in-progress" in categories.state_labels
assert "frontend" in categories.other_labels
def test_issue_status_service_performance(self, performance_timer):
"""Test performance of issue status service operations."""
# Arrange
service = IssueStatusService()
issues = []
project_info = {"kanban_columns": ["Todo", "In Progress", "Review", "Done"]}
for i in range(1000):
labels = ["bug", f"priority:{'high' if i % 3 == 0 else 'medium'}", f"status:{'in-progress' if i % 2 == 0 else 'new'}"]
issue = (IssueBuilder()
.with_number(i + 1)
.with_title(f"Status Test Issue {i + 1}")
.with_labels(*labels)
.build())
issues.append(issue)
# Act
performance_timer.start()
results = []
for issue in issues:
kanban_column = service.determine_kanban_column(issue, project_info)
priority_info = service.extract_priority_info(issue)
results.append((kanban_column, priority_info))
performance_timer.stop()
# Assert
assert len(results) == 1000
assert_performance_within_bounds(performance_timer.elapsed, 0.8, "processing 1000 issues through status service")
# Verify correctness
in_progress_count = sum(1 for kanban, _ in results if kanban == "In Progress")
todo_count = sum(1 for kanban, _ in results if kanban == "Todo")
assert in_progress_count > 0
assert todo_count > 0
def test_project_progress_calculation_performance(self, performance_timer):
"""Test performance of project progress calculations."""
# Arrange
projects = []
for i in range(100):
milestones = []
for j in range(20): # 20 milestones per project
milestone = (MilestoneBuilder()
.with_id(j + 1)
.with_title(f"Milestone {j + 1}")
.with_issue_counts(
open_issues=10 - (j % 8),
closed_issues=j % 12
)
.build())
milestones.append(milestone)
project = (ProjectBuilder()
.with_name(f"Performance Project {i + 1}")
.with_milestones(*milestones)
.build())
projects.append(project)
# Act
performance_timer.start()
progress_results = []
for project in projects:
overall_progress = project.calculate_overall_progress()
active_milestones = project.get_active_milestones()
completed_milestones = project.get_completed_milestones()
total_issues = project.get_total_issues()
progress_results.append({
"overall_progress": overall_progress,
"active_count": len(active_milestones),
"completed_count": len(completed_milestones),
"total_issues": total_issues
})
performance_timer.stop()
# Assert
assert len(progress_results) == 100
assert_performance_within_bounds(performance_timer.elapsed, 0.5, "calculating progress for 100 projects with 20 milestones each")
# Verify calculations are reasonable
for result in progress_results:
assert 0 <= result["overall_progress"] <= 100
assert result["total_issues"] > 0
def test_bulk_issue_validation_performance(self, performance_timer):
"""Test performance of bulk issue validation."""
# Arrange
validation_service = IssueValidationService()
issue_data_list = []
for i in range(2000):
issue_data = {
"title": f"Validation Test Issue {i + 1}" if i % 10 != 0 else "", # 10% invalid
"labels": ["bug", "priority:medium"] if i % 5 != 0 else ["bug", "priority:high", "priority:low"] # 20% invalid
}
issue_data_list.append(issue_data)
# Act
performance_timer.start()
validation_results = []
for issue_data in issue_data_list:
try:
validation_service.validate_issue_creation(issue_data["title"], issue_data["labels"])
validation_results.append(True)
except Exception:
validation_results.append(False)
performance_timer.stop()
# Assert
assert len(validation_results) == 2000
assert_performance_within_bounds(performance_timer.elapsed, 1.0, "validating 2000 issues")
# Verify validation correctness
valid_count = sum(1 for result in validation_results if result)
invalid_count = sum(1 for result in validation_results if not result)
# Expect about 70% valid (90% have valid titles AND 80% have valid labels = 72%)
assert 1200 <= valid_count <= 1600 # Allow some tolerance
assert 400 <= invalid_count <= 800
@pytest.mark.slow
def test_memory_usage_with_large_datasets(self, performance_timer):
"""Test memory usage with large datasets."""
try:
import psutil
import os
except ImportError:
pytest.skip("psutil not available for memory testing")
# Measure initial memory
process = psutil.Process(os.getpid())
initial_memory_mb = process.memory_info().rss / (1024 * 1024)
# Create large dataset
performance_timer.start()
large_issues = []
for i in range(10000):
issue = (IssueBuilder()
.with_number(i + 1)
.with_title(f"Large Dataset Issue {i + 1}")
.with_labels("bug", "priority:medium", "status:new", "backend", "database")
.build())
large_issues.append(issue)
# Perform operations on dataset
for issue in large_issues:
categories = issue.categorize_labels()
# Simulate some processing
_ = len(categories.type_labels) + len(categories.priority_labels)
performance_timer.stop()
# Measure final memory
final_memory_mb = process.memory_info().rss / (1024 * 1024)
memory_increase_mb = final_memory_mb - initial_memory_mb
# Force garbage collection and measure again
gc.collect()
gc_memory_mb = process.memory_info().rss / (1024 * 1024)
gc_reduction_mb = final_memory_mb - gc_memory_mb
# Assert
assert len(large_issues) == 10000
assert_performance_within_bounds(performance_timer.elapsed, 5.0, "processing 10,000 issues")
assert_memory_usage_within_bounds(memory_increase_mb, 50.0, "creating and processing 10,000 issues")
print(f"Memory usage: Initial={initial_memory_mb:.2f}MB, Final={final_memory_mb:.2f}MB, "
f"Increase={memory_increase_mb:.2f}MB, GC Reduction={gc_reduction_mb:.2f}MB")
# Memory should be reasonable for the dataset size
assert memory_increase_mb > 0 # Should use some memory
assert gc_reduction_mb >= 0 # GC should not increase memory
@pytest.mark.performance
def test_concurrent_domain_operations_simulation(self, performance_timer):
"""Simulate concurrent domain operations for performance testing."""
# Arrange
project_service = ProjectManagementService()
projects = []
# Create test projects
for i in range(10):
milestones = [
MilestoneBuilder().with_id(j + 1).with_title(f"M{j + 1}")
.with_issue_counts(5, 3).build()
for j in range(5)
]
project = (ProjectBuilder()
.with_name(f"Concurrent Project {i + 1}")
.with_milestones(*milestones)
.build())
projects.append(project)
# Act - Simulate concurrent operations
performance_timer.start()
results = []
# Simulate multiple "users" performing operations
for iteration in range(100):
for project in projects:
# Simulate various operations
health_status = project_service.determine_project_health(project)
progress = project.calculate_overall_progress()
active_milestones = project.get_active_milestones()
results.append({
"iteration": iteration,
"project_name": project.name,
"health_status": health_status,
"progress": progress,
"active_milestones": len(active_milestones)
})
performance_timer.stop()
# Assert
expected_operations = 100 * 10 # 100 iterations * 10 projects
assert len(results) == expected_operations
assert_performance_within_bounds(performance_timer.elapsed, 2.0, f"simulating {expected_operations} concurrent operations")
# Verify result consistency
valid_health_statuses = {"Excellent", "Good", "Fair", "At Risk", "Stalled", "Needs Attention", "Inactive"}
for result in results:
assert result["health_status"] in valid_health_statuses
assert 0 <= result["progress"] <= 100
assert result["active_milestones"] >= 0
ops_per_second = expected_operations / performance_timer.elapsed
print(f"Simulated {expected_operations} operations in {performance_timer.elapsed:.3f}s ({ops_per_second:.0f} ops/sec)")
def test_domain_operation_consistency_under_load(self, performance_timer):
"""Test that domain operations remain consistent under load."""
# Arrange
reference_issue = (IssueBuilder()
.with_number(1)
.with_title("Reference Issue")
.with_labels("bug", "priority:high", "status:blocked")
.build())
# Get reference results
reference_categories = reference_issue.categorize_labels()
status_service = IssueStatusService()
reference_kanban = status_service.determine_kanban_column(reference_issue, {})
# Act - Perform same operations many times
performance_timer.start()
consistency_results = []
for i in range(5000):
# Create identical issue
test_issue = (IssueBuilder()
.with_number(1)
.with_title("Reference Issue")
.with_labels("bug", "priority:high", "status:blocked")
.build())
# Perform operations
categories = test_issue.categorize_labels()
kanban = status_service.determine_kanban_column(test_issue, {})
# Check consistency
categories_match = (
categories.type_labels == reference_categories.type_labels and
categories.priority_labels == reference_categories.priority_labels and
categories.state_labels == reference_categories.state_labels
)
kanban_matches = kanban == reference_kanban
consistency_results.append(categories_match and kanban_matches)
performance_timer.stop()
# Assert
assert len(consistency_results) == 5000
assert all(consistency_results), "All operations should produce consistent results"
assert_performance_within_bounds(performance_timer.elapsed, 1.5, "consistency test with 5000 operations")
print(f"Consistency test: {len(consistency_results)} operations, all consistent, "
f"completed in {performance_timer.elapsed:.3f}s")