""" Integration tests for the complete text generation pipeline. Tests how the core modules work together. """ import pytest from unittest.mock import Mock, patch, MagicMock import sys import os # Add src to path for importing modules sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src')) from fixtures.sample_data import * class TestTextGenerationPipeline: """Integration tests for the complete text generation workflow""" @pytest.mark.integration @patch('src.core.text_processor.process_multiline_text') @patch('src.core.text_fitting.find_optimal_font_size') @patch('src.core.text_fitting.calculate_text_position') def test_basic_text_generation_workflow(self, mock_calc_position, mock_find_size, mock_process_text): """Test the basic workflow from text input to positioned output""" # Setup mocks mock_process_text.return_value = { 'lines': ['Hello', 'World'], 'total_height': 40, 'line_count': 2 } mock_find_size.return_value = 24 mock_calc_position.return_value = { 'x': 50, 'y': 50, 'text_width': 100, 'text_height': 40 } # Simulate a basic workflow text = "Hello\nWorld" font = Mock() canvas_width, canvas_height = 300, 200 # Step 1: Process multiline text from src.core.text_processor import process_multiline_text processed = process_multiline_text(text, font, canvas_width, canvas_height) # Step 2: Find optimal font size from src.core.text_fitting import find_optimal_font_size optimal_size = find_optimal_font_size(text, "/fake/font.ttf", 200, 100) # Step 3: Calculate position from src.core.text_fitting import calculate_text_position available_area = {'x': 0, 'y': 0, 'width': canvas_width, 'height': canvas_height} position = calculate_text_position(text, font, available_area, 'center') # Verify workflow completed assert processed['line_count'] == 2 assert optimal_size == 24 assert position['x'] == 50 and position['y'] == 50 # Verify functions were called in sequence mock_process_text.assert_called_once() mock_find_size.assert_called_once() mock_calc_position.assert_called_once() @pytest.mark.integration @patch('src.core.text_fitting.check_text_overlap') @patch('src.core.text_fitting.resolve_text_conflicts') def test_overlap_detection_and_resolution_workflow(self, mock_resolve, mock_check): """Test the workflow for detecting and resolving text overlaps""" # Setup mocks mock_check.return_value = [(0, 1)] # Positions 0 and 1 overlap mock_resolve.return_value = [ {'x': 0, 'y': 0, 'width': 100, 'height': 50}, {'x': 0, 'y': 60, 'width': 100, 'height': 50} # Moved down ] # Initial overlapping positions positions = [ {'x': 0, 'y': 0, 'width': 100, 'height': 50}, {'x': 50, 'y': 25, 'width': 100, 'height': 50} # Overlaps ] # Step 1: Check for overlaps from src.core.text_fitting import check_text_overlap overlaps = check_text_overlap(positions) # Step 2: Resolve conflicts from src.core.text_fitting import resolve_text_conflicts resolved = resolve_text_conflicts(positions, 500, 400) # Verify workflow assert len(overlaps) == 1 assert len(resolved) == 2 assert resolved[1]['y'] == 60 # Second position moved down mock_check.assert_called_once_with(positions) mock_resolve.assert_called_once() @pytest.mark.integration def test_text_metrics_and_fitting_integration(self): """Test integration between text metrics calculation and fitting""" with patch('src.core.text_processor.calculate_text_metrics') as mock_metrics, \ patch('src.core.text_fitting.calculate_text_scaling') as mock_scaling: # Setup mocks mock_metrics.return_value = { 'width': 200, 'height': 50, 'ascent': 40, 'descent': 10 } mock_scaling.return_value = (1.5, 2.0, 1.5) # width_scale, height_scale, uniform_scale # Test workflow font = Mock() text = "Sample Text" from src.core.text_processor import calculate_text_metrics from src.core.text_fitting import calculate_text_scaling # Get text metrics metrics = calculate_text_metrics(text, font) # Calculate scaling based on metrics scaling = calculate_text_scaling(text, font, 300, 100) # Verify integration assert metrics['width'] == 200 assert metrics['height'] == 50 assert scaling[2] == 1.5 # uniform_scale mock_metrics.assert_called_once_with(text, font) mock_scaling.assert_called_once() @pytest.mark.integration @patch('src.core.text_fitting.calculate_available_text_area') @patch('src.core.text_processor.optimize_text_layout') def test_area_calculation_and_layout_optimization(self, mock_optimize, mock_calc_area): """Test integration between area calculation and layout optimization""" # Setup mocks mock_calc_area.return_value = { 'x': 10, 'y': 10, 'width': 300, 'height': 200, 'area': 60000 } mock_optimize.return_value = [ {'x': 10, 'y': 10, 'width': 100, 'height': 50, 'text': 'Block 1'}, {'x': 150, 'y': 10, 'width': 80, 'height': 40, 'text': 'Block 2'} ] # Test data canvas_width, canvas_height = 400, 300 overlays = [{'x': 350, 'y': 250, 'width': 50, 'height': 50}] text_blocks = [ {'x': 50, 'y': 50, 'width': 100, 'height': 50, 'text': 'Block 1'}, {'x': 75, 'y': 75, 'width': 80, 'height': 40, 'text': 'Block 2'} # Overlaps ] # Test workflow from src.core.text_fitting import calculate_available_text_area from src.core.text_processor import optimize_text_layout # Step 1: Calculate available area available_area = calculate_available_text_area(canvas_width, canvas_height, overlays) # Step 2: Optimize layout optimized_blocks = optimize_text_layout(text_blocks, canvas_width, canvas_height) # Verify integration assert available_area['width'] == 300 assert available_area['area'] == 60000 assert len(optimized_blocks) == 2 assert optimized_blocks[0]['x'] == 10 # First block positioned in available area mock_calc_area.assert_called_once_with(canvas_width, canvas_height, overlays) mock_optimize.assert_called_once_with(text_blocks, canvas_width, canvas_height) class TestErrorHandlingIntegration: """Integration tests for error handling across modules""" @pytest.mark.integration def test_graceful_degradation_on_font_errors(self): """Test that the system degrades gracefully when font operations fail""" with patch('PIL.ImageFont.truetype') as mock_truetype: mock_truetype.side_effect = OSError("Font not found") # Test that font size finding falls back gracefully from src.core.text_fitting import find_optimal_font_size result = find_optimal_font_size("Test", "/fake/font.ttf", 200, 100) assert result == 8 # Should fall back to minimum size @pytest.mark.integration def test_error_handling_in_text_processing_chain(self): """Test error handling when text processing components fail""" with patch('src.core.text_processor.wrap_text_to_width') as mock_wrap: mock_wrap.side_effect = Exception("Text wrapping failed") # Test that multiline processing handles wrap failures from src.core.text_processor import process_multiline_text result = process_multiline_text("Test text", Mock(), 200, 100) # Should return fallback result instead of crashing assert 'lines' in result assert 'total_height' in result assert 'line_count' in result