Files
markitect-main/tests/test_issue_65_template_integration.py
tegwick bcbe78d04f feat: Complete Issue #65 Template Engine Foundation + Fix CLI Regression
## Issue #65 - Template Engine Foundation (COMPLETED)
- Implement complete TDD8 methodology with 30 comprehensive tests (100% passing)
- Add template variable parser with Unicode and dot notation support
- Add template rendering engine with strict/lenient modes
- Add business document generation (invoices, reports)
- Add CLI integration with `markitect template-render` command
- Add performance optimization (1000+ variables in <0.1s)

## Critical CLI Regression Fix
- Fix broken `markitect --help` due to import path issues in markitect/issues/base.py
- Add proper path resolution for domain module accessibility
- Add 12 comprehensive CLI integration tests to prevent future regressions
- Restore full CLI functionality with 35+ working commands

## Template Engine Architecture
- markitect/template/parser.py - Variable parsing with comprehensive validation
- markitect/template/engine.py - Template rendering with business logic
- markitect/template/__init__.py - Structured package exports
- Comprehensive exception hierarchy for robust error handling

## Test Coverage Excellence
- 30 Issue #65 tests: parser (9), substitution (14), integration (7)
- 12 CLI integration tests for regression prevention
- Business scenario validation with real invoice/report generation
- Performance benchmarking and error handling validation

## CLI Professional Enhancement
- Add template-render command with comprehensive options
- Fix import path issues preventing CLI access
- Add validation, data checking, output options
- Support JSON/YAML data formats with auto-detection

## Business Impact
- Transform MarkiTect from document analysis to business automation platform
- Enable professional invoice and report generation
- Provide robust CLI interface for document workflows
- Establish foundation for Epic #64 advanced template features

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-02 15:33:32 +02:00

504 lines
16 KiB
Python

"""
Test for Issue #65: Template Engine Foundation - Integration Tests
This test module validates complete template engine integration scenarios
for business document generation, implementing TDD8 Cycle 3.
Tests focus on:
- Real business document template rendering (invoices, reports)
- End-to-end template processing workflows
- Performance with realistic data volumes
- Integration with MarkdownMatters metadata structure
"""
import pytest
from typing import Dict, Any
class TestTemplateEngineIntegration:
"""Test suite for template engine integration scenarios."""
def setup_method(self):
"""Set up test environment for each test."""
try:
from markitect.template.engine import TemplateEngine
self.engine = TemplateEngine()
except ImportError:
self.engine = None
def test_render_complete_invoice_template(self):
"""Test rendering a complete business invoice template.
Reference: Issue #65 - Template Engine Foundation
TDD Phase: Integration test for business use case
"""
# Arrange - Complete invoice template from examples/invoice_template.md
invoice_template = """---
title: "Invoice {{invoice_number}}"
date: "{{date}}"
due_date: "{{due_date}}"
customer_id: "{{customer.id}}"
---
{{company.name}}
{{company.address}}
{{company.city}}, {{company.state}} {{company.zip}}
{{company.email}} | {{company.phone}}
# Invoice {{invoice_number}}
**Bill To:**
{{customer.name}}
{{customer.address}}
{{customer.city}}, {{customer.state}} {{customer.zip}}
**Invoice Date:** {{date}}
**Due Date:** {{due_date}}
**Customer ID:** {{customer.id}}
## Summary
**Subtotal:** {{subtotal}}
**Tax ({{tax_rate}}%):** {{tax_amount}}
**Total:** {{total}} {{currency}}
## Payment Information
Please remit payment to {{company.name}} within {{payment_terms}} days.
---
{{!contentmatter}}
invoice_number: "{{invoice_number}}"
customer: "{{customer.name}}"
total_amount: {{total}}
currency: "{{currency}}"
status: "generated"
{{!/contentmatter}}
"""
# Test data representing realistic invoice data
invoice_data = {
"invoice_number": "INV-2025-001",
"date": "2025-01-15",
"due_date": "2025-02-14",
"company": {
"name": "MarkiTect Solutions",
"address": "123 Business Park",
"city": "Tech City",
"state": "CA",
"zip": "90210",
"email": "billing@markitect.com",
"phone": "(555) 123-4567"
},
"customer": {
"id": "CUST-001",
"name": "Acme Corporation",
"address": "456 Industry Blvd",
"city": "Enterprise",
"state": "NY",
"zip": "10001"
},
"subtotal": 1500.00,
"tax_rate": 8.5,
"tax_amount": 127.50,
"total": 1627.50,
"currency": "USD",
"payment_terms": "30"
}
# Act & Assert
if self.engine is None:
pytest.skip("TemplateEngine not implemented yet - TDD integration phase")
result = self.engine.render(invoice_template, invoice_data)
# Verify critical invoice elements are rendered correctly
assert "Invoice INV-2025-001" in result
assert "MarkiTect Solutions" in result
assert "Acme Corporation" in result
assert "123 Business Park" in result
assert "Tech City, CA 90210" in result
assert "Invoice Date:** 2025-01-15" in result
assert "Due Date:** 2025-02-14" in result
assert "Customer ID:** CUST-001" in result
assert "**Total:** 1627.5 USD" in result
assert "Tax (8.5%):** 127.5" in result
assert "within 30 days" in result
# Verify frontmatter is rendered
assert 'title: "Invoice INV-2025-001"' in result
assert 'customer_id: "CUST-001"' in result
# Verify contentmatter placeholders are rendered
assert 'invoice_number: "INV-2025-001"' in result
assert 'customer: "Acme Corporation"' in result
assert 'total_amount: 1627.5' in result
def test_render_business_report_template(self):
"""Test rendering a business report template with calculations.
Reference: Issue #65 - Template Engine Foundation
"""
# Arrange
report_template = """---
title: "{{report_type}} Report - {{period}}"
generated: "{{generated_date}}"
department: "{{department.name}}"
---
# {{report_type}} Report
**Period:** {{period}}
**Department:** {{department.name}}
**Generated:** {{generated_date}}
## Summary
- Total Revenue: {{metrics.revenue}} {{currency}}
- Total Expenses: {{metrics.expenses}} {{currency}}
- Net Profit: {{metrics.profit}} {{currency}}
- Profit Margin: {{metrics.profit_margin}}%
## Department Performance
**Manager:** {{department.manager}}
**Team Size:** {{department.team_size}}
**Budget Utilization:** {{department.budget_utilization}}%
Contact: {{department.contact.email}}
"""
report_data = {
"report_type": "Monthly Financial",
"period": "January 2025",
"generated_date": "2025-02-01",
"currency": "USD",
"department": {
"name": "Sales",
"manager": "Sarah Johnson",
"team_size": 12,
"budget_utilization": 85.5,
"contact": {
"email": "sales@company.com"
}
},
"metrics": {
"revenue": 125000.00,
"expenses": 87500.00,
"profit": 37500.00,
"profit_margin": 30.0
}
}
# Act & Assert
if self.engine is None:
pytest.skip("TemplateEngine not implemented yet")
result = self.engine.render(report_template, report_data)
# Verify report structure and data
assert "Monthly Financial Report" in result
assert "Period:** January 2025" in result
assert "Department:** Sales" in result
assert "Total Revenue: 125000.0 USD" in result
assert "Net Profit: 37500.0 USD" in result
assert "Profit Margin: 30.0%" in result
assert "Manager:** Sarah Johnson" in result
assert "Budget Utilization:** 85.5%" in result
assert "sales@company.com" in result
def test_error_handling_missing_nested_data(self):
"""Test comprehensive error handling with detailed context.
Reference: Issue #65 - Template Engine Foundation
"""
# Arrange
template = "Customer: {{customer.profile.details.name}}, Order: {{order.items.first.description}}"
incomplete_data = {
"customer": {
"profile": {
# Missing 'details' key
}
},
"order": {
# Missing 'items' key
"id": "ORD-001"
}
}
# Act & Assert
if self.engine is None:
pytest.skip("TemplateEngine not implemented yet")
# Test strict mode error with context
with pytest.raises(Exception) as exc_info:
self.engine.render(template, incomplete_data, strict=True)
error_message = str(exc_info.value)
# Should provide helpful context about what was available
assert ("details" in error_message.lower() or
"customer.profile.details.name" in error_message)
# Test lenient mode preserves placeholders
result = self.engine.render(template, incomplete_data, strict=False)
assert "{{customer.profile.details.name}}" in result
assert "{{order.items.first.description}}" in result
def test_performance_large_business_document(self):
"""Test performance with realistic large business document.
Reference: Issue #65 - Performance Requirements
"""
# Arrange - Large template with many variables
large_template = """# Annual Report {{year}}
## Executive Summary
Company: {{company.name}}
CEO: {{company.ceo}}
Revenue: {{financials.revenue}} {{currency}}
## Department Reports
"""
# Add many department sections
for i in range(50):
dept_prefix = f"departments.dept_{i}"
large_template += f"""
### Department {{{{{dept_prefix}.name}}}}
Manager: {{{{{dept_prefix}.manager}}}}
Budget: {{{{{dept_prefix}.budget}}}} {{{{currency}}}}
Team Size: {{{{{dept_prefix}.team_size}}}}
"""
# Generate corresponding data
departments_data = {}
for i in range(50):
departments_data[f"dept_{i}"] = {
"name": f"Department {i+1}",
"manager": f"Manager {i+1}",
"budget": (i+1) * 10000,
"team_size": (i % 20) + 5
}
large_data = {
"year": "2025",
"currency": "USD",
"company": {
"name": "Enterprise Corp",
"ceo": "John CEO"
},
"financials": {
"revenue": 50000000
},
"departments": departments_data
}
# Act & Assert
if self.engine is None:
pytest.skip("TemplateEngine not implemented yet")
import time
start_time = time.time()
result = self.engine.render(large_template, large_data)
render_time = time.time() - start_time
# Performance requirement: <100ms for large documents
assert render_time < 0.1
# Verify content was rendered
assert "Annual Report 2025" in result
assert "Enterprise Corp" in result
assert "50000000 USD" in result
assert "Department 1" in result
assert "Department 50" in result
def test_markdown_structure_preservation(self):
"""Test that complex markdown structure is preserved during rendering.
Reference: Issue #65 - Template Engine Foundation
"""
# Arrange - Complex markdown with various elements
complex_template = """---
title: "{{document.title}}"
author: "{{document.author}}"
---
# {{document.title}}
## Table of Contents
- [Introduction](#introduction)
- [Analysis](#analysis-{{section.id}})
- [Conclusion](#conclusion)
## Introduction
Welcome to **{{document.title}}** by *{{document.author}}*.
> This document provides {{description.type}} analysis for {{client.name}}.
### Code Example
```python
def process_{{operation.name}}():
return "{{operation.result}}"
```
## Analysis {{section.id}}
| Metric | Value | Target |
|--------|-------|--------|
| {{metrics.primary.name}} | {{metrics.primary.value}} | {{metrics.primary.target}} |
| {{metrics.secondary.name}} | {{metrics.secondary.value}} | {{metrics.secondary.target}} |
### Subsection
1. First point about {{analysis.point1}}
2. Second point about {{analysis.point2}}
3. Third point with [link]({{external.url}})
---
*Generated on {{generation.date}} by {{generation.system}}*
"""
template_data = {
"document": {
"title": "Business Analysis Report",
"author": "Analytics Team"
},
"description": {
"type": "comprehensive"
},
"client": {
"name": "Global Enterprises"
},
"section": {
"id": "Q1-2025"
},
"operation": {
"name": "quarterly_analysis",
"result": "success"
},
"metrics": {
"primary": {
"name": "Revenue",
"value": "$125K",
"target": "$120K"
},
"secondary": {
"name": "Growth",
"value": "12%",
"target": "10%"
}
},
"analysis": {
"point1": "market expansion",
"point2": "customer acquisition"
},
"external": {
"url": "https://example.com/data"
},
"generation": {
"date": "2025-01-15",
"system": "MarkiTect"
}
}
# Act & Assert
if self.engine is None:
pytest.skip("TemplateEngine not implemented yet")
result = self.engine.render(complex_template, template_data)
# Verify markdown structure preservation
assert "---" in result # Frontmatter
assert "# Business Analysis Report" in result # H1
assert "## Table of Contents" in result # H2
assert "- [Introduction](#introduction)" in result # List
assert "> This document provides" in result # Blockquote
assert "```python" in result # Code block
assert "def process_quarterly_analysis():" in result # Rendered in code
assert "| Revenue | $125K | $120K |" in result # Table
assert "1. First point about market expansion" in result # Numbered list
assert "[link](https://example.com/data)" in result # Link
assert "*Generated on 2025-01-15 by MarkiTect*" in result # Emphasis
# Verify frontmatter variables were rendered
assert 'title: "Business Analysis Report"' in result
assert 'author: "Analytics Team"' in result
class TestTemplateEngineWorkflows:
"""Test complete template processing workflows."""
def setup_method(self):
"""Set up test environment."""
try:
from markitect.template.engine import TemplateEngine
from markitect.template.parser import TemplateParser
self.engine = TemplateEngine()
self.parser = TemplateParser()
except ImportError:
self.engine = None
self.parser = None
def test_template_validation_workflow(self):
"""Test complete template validation before rendering workflow.
Reference: Issue #65 - Template Engine Foundation
"""
# Arrange
template_with_errors = "Valid: {{name}}, Invalid: {{broken, Incomplete: {missing}"
valid_template = "Hello {{name}}, welcome to {{company}}!"
test_data = {"name": "Alice", "company": "MarkiTect"}
# Act & Assert
if self.engine is None or self.parser is None:
pytest.skip("Template components not implemented yet")
# Test validation of problematic template
errors = self.engine.validate_template(template_with_errors)
assert len(errors) > 0
# Test validation of good template
errors = self.engine.validate_template(valid_template)
assert len(errors) == 0
# Test rendering after validation
result = self.engine.render(valid_template, test_data)
assert result == "Hello Alice, welcome to MarkiTect!"
def test_data_completeness_analysis_workflow(self):
"""Test data completeness analysis before rendering.
Reference: Issue #65 - Template Engine Foundation
"""
# Arrange
template = "Invoice {{invoice_number}} for {{customer.name}} - Total: {{total}} {{currency}}"
complete_data = {
"invoice_number": "INV-001",
"customer": {"name": "Acme Corp"},
"total": 1500.00,
"currency": "USD"
}
incomplete_data = {
"invoice_number": "INV-001",
"customer": {"name": "Acme Corp"}
# Missing 'total' and 'currency'
}
# Act & Assert
if self.engine is None:
pytest.skip("TemplateEngine not implemented yet")
# Test with complete data
completeness = self.engine.check_data_completeness(template, complete_data)
assert completeness['completeness'] == 1.0
assert len(completeness['missing']) == 0
assert len(completeness['available']) == 4
# Test with incomplete data
completeness = self.engine.check_data_completeness(template, incomplete_data)
assert completeness['completeness'] < 1.0
assert 'total' in completeness['missing']
assert 'currency' in completeness['missing']
assert 'invoice_number' in completeness['available']
assert 'customer.name' in completeness['available']
if __name__ == '__main__':
pytest.main([__file__, '-v'])