"""Integration tests for report generation. Tests the full report generation pipeline from test run to PDF output. """ import json import tempfile from datetime import datetime from pathlib import Path from uuid import UUID, uuid4 import pytest from py_dvt_ate.data.models import Measurement, TestStatus from py_dvt_ate.data.repository import SQLiteRepository class TestReportGenerationIntegration: """Integration tests for the report generation pipeline.""" @pytest.fixture def temp_dir(self) -> Path: """Create a temporary directory for test files.""" with tempfile.TemporaryDirectory() as tmpdir: yield Path(tmpdir) @pytest.fixture def repository(self, temp_dir: Path) -> SQLiteRepository: """Create a test repository with sample data.""" db_path = temp_dir / "test.db" measurements_dir = temp_dir / "measurements" repo = SQLiteRepository(db_path, measurements_dir) # Create a test run test_config = { "temperatures": [-40.0, 25.0, 85.0], "input_voltage": 5.0, "load_current": 0.1, } run_id = repo.create_run( test_name="tempco", config=test_config, operator="test_operator", description="Integration test run", ) # Add some results repo.save_result( run_id=run_id, parameter="tempco", value=48.5, unit="ppm/C", lower_limit=None, upper_limit=100.0, ) repo.save_result( run_id=run_id, parameter="output_voltage_m40c", value=3.2965, unit="V", lower_limit=3.2, upper_limit=3.4, ) repo.save_result( run_id=run_id, parameter="output_voltage_25c", value=3.3000, unit="V", lower_limit=3.2, upper_limit=3.4, ) repo.save_result( run_id=run_id, parameter="output_voltage_85c", value=3.2901, unit="V", lower_limit=3.2, upper_limit=3.4, ) # Add measurements measurements = [] temperatures = [-40.0, 0.0, 25.0, 50.0, 85.0] voltages = [3.2965, 3.2985, 3.3000, 3.2960, 3.2901] for i, (temp, voltage) in enumerate(zip(temperatures, voltages, strict=False)): measurements.append( Measurement( timestamp=float(i * 60), parameter="output_voltage", value=voltage, unit="V", temperature=temp, input_voltage=5.0, load_current=0.1, ) ) repo.save_measurements(run_id, measurements) # Complete the run repo.complete_run(run_id, TestStatus.PASSED) return repo @pytest.fixture def run_id(self, repository: SQLiteRepository) -> UUID: """Get the test run ID from the repository.""" runs = repository.get_all_runs() return UUID(runs[0].id) def test_full_report_generation( self, repository: SQLiteRepository, run_id: UUID, temp_dir: Path ) -> None: """Test complete report generation pipeline.""" from py_dvt_ate.reporting import ReportConfig, ReportGenerator # Create report config config = ReportConfig( company_name="Test Company Ltd", include_charts=True, chart_dpi=100, # Lower for faster tests ) # Create generator reports_dir = temp_dir / "reports" generator = ReportGenerator( repository=repository, config=config, reports_dir=reports_dir, ) # Generate report pdf_path = generator.generate(run_id) # Verify PDF was created assert pdf_path.exists() assert pdf_path.suffix == ".pdf" assert pdf_path.stat().st_size > 1000 # Should be non-trivial size # Verify it's in the reports directory assert pdf_path.parent == reports_dir def test_report_generation_custom_path( self, repository: SQLiteRepository, run_id: UUID, temp_dir: Path ) -> None: """Test report generation with custom output path.""" from py_dvt_ate.reporting import ReportConfig, ReportGenerator config = ReportConfig(include_charts=False) # No charts for faster test generator = ReportGenerator( repository=repository, config=config, ) # Generate to custom path custom_path = temp_dir / "custom_report.pdf" pdf_path = generator.generate(run_id, output_path=custom_path) assert pdf_path == custom_path assert pdf_path.exists() def test_report_generation_as_bytes( self, repository: SQLiteRepository, run_id: UUID ) -> None: """Test generating report as bytes.""" from py_dvt_ate.reporting import ReportConfig, ReportGenerator config = ReportConfig(include_charts=False) generator = ReportGenerator( repository=repository, config=config, ) # Generate as bytes pdf_bytes = generator.generate_bytes(run_id) # Verify it's a valid PDF assert isinstance(pdf_bytes, bytes) assert pdf_bytes.startswith(b"%PDF") # PDF magic bytes assert len(pdf_bytes) > 1000 def test_report_includes_all_data( self, repository: SQLiteRepository, run_id: UUID, temp_dir: Path ) -> None: """Test that generated report includes all expected data.""" from py_dvt_ate.reporting import ReportConfig, ReportGenerator from py_dvt_ate.reporting.renderers.html import HTMLRenderer config = ReportConfig( company_name="Test Company", include_charts=False, ) generator = ReportGenerator( repository=repository, config=config, ) # Get HTML (intermediate step) to check content data = generator._gather_data(run_id) html_renderer = HTMLRenderer() html = html_renderer.render(data) # Check for expected content assert "tempco" in html assert "Test Company" in html assert "48.500000" in html # tempco value assert "3.300000" in html # output voltage assert "test_operator" in html assert "Integration test run" in html assert "PASS" in html def test_report_with_failed_results( self, temp_dir: Path ) -> None: """Test report generation with failed test results.""" from py_dvt_ate.reporting import ReportConfig, ReportGenerator # Create repository with a failed test db_path = temp_dir / "failed_test.db" repo = SQLiteRepository(db_path, temp_dir / "measurements") run_id = repo.create_run( test_name="failed_test", config={}, operator="test", ) # Add failing result repo.save_result( run_id=run_id, parameter="test_param", value=150.0, # Exceeds limit unit="X", lower_limit=0.0, upper_limit=100.0, ) repo.complete_run(run_id, TestStatus.FAILED) # Generate report config = ReportConfig(include_charts=False) generator = ReportGenerator(repository=repo, config=config) pdf_bytes = generator.generate_bytes(run_id) # Should still generate assert pdf_bytes.startswith(b"%PDF") def test_report_generation_invalid_run_id( self, repository: SQLiteRepository, temp_dir: Path ) -> None: """Test that invalid run ID raises appropriate error.""" from py_dvt_ate.reporting import ReportConfig, ReportGenerationError, ReportGenerator config = ReportConfig() generator = ReportGenerator(repository=repository, config=config) invalid_id = uuid4() with pytest.raises(ReportGenerationError): generator.generate(invalid_id) def test_report_charts_generation( self, repository: SQLiteRepository, run_id: UUID, temp_dir: Path ) -> None: """Test that charts are generated when enabled.""" from py_dvt_ate.reporting import ReportConfig, ReportGenerator config = ReportConfig(include_charts=True, chart_dpi=72) generator = ReportGenerator( repository=repository, config=config, reports_dir=temp_dir / "reports", ) # Gather data and check charts data = generator._gather_data(run_id) # Should have at least one chart (results bar chart) assert len(data.charts) >= 1 # Voltage vs temperature chart should be present (we have voltage measurements) assert "Voltage vs Temperature" in data.charts or "Results Summary" in data.charts