""" Integration tests for engine/app.py - pipeline orchestration. Tests the main entry point and pipeline mode initialization. """ import sys from unittest.mock import Mock, patch import pytest from engine.app import main, run_pipeline_mode from engine.pipeline import get_preset class TestMain: """Test main() entry point.""" def test_main_calls_run_pipeline_mode_with_default_preset(self): """main() runs default preset (demo) when no args provided.""" with patch("engine.app.run_pipeline_mode") as mock_run: sys.argv = ["mainline.py"] main() mock_run.assert_called_once_with("demo") def test_main_calls_run_pipeline_mode_with_config_preset(self): """main() uses PRESET from config if set.""" with ( patch("engine.app.config") as mock_config, patch("engine.app.run_pipeline_mode") as mock_run, ): mock_config.PIPELINE_DIAGRAM = False mock_config.PRESET = "gallery-sources" mock_config.PIPELINE_MODE = False sys.argv = ["mainline.py"] main() mock_run.assert_called_once_with("gallery-sources") def test_main_exits_on_unknown_preset(self): """main() exits with error for unknown preset.""" with ( patch("engine.app.config") as mock_config, patch("engine.app.list_presets", return_value=["demo", "poetry"]), ): mock_config.PIPELINE_DIAGRAM = False mock_config.PRESET = "nonexistent" mock_config.PIPELINE_MODE = False sys.argv = ["mainline.py"] with pytest.raises(SystemExit) as exc_info: main() assert exc_info.value.code == 1 class TestRunPipelineMode: """Test run_pipeline_mode() initialization.""" def test_run_pipeline_mode_loads_valid_preset(self): """run_pipeline_mode() loads a valid preset.""" preset = get_preset("demo") assert preset is not None assert preset.name == "demo" assert preset.source == "headlines" def test_run_pipeline_mode_exits_on_invalid_preset(self): """run_pipeline_mode() exits if preset not found.""" with pytest.raises(SystemExit) as exc_info: run_pipeline_mode("invalid-preset-xyz") assert exc_info.value.code == 1 def test_run_pipeline_mode_exits_when_no_content_available(self): """run_pipeline_mode() exits if no content can be fetched.""" with ( patch("engine.app.load_cache", return_value=None), patch("engine.app.fetch_all", return_value=([], None, None)), patch("engine.app.effects_plugins"), pytest.raises(SystemExit) as exc_info, ): run_pipeline_mode("demo") assert exc_info.value.code == 1 def test_run_pipeline_mode_uses_cache_over_fetch(self): """run_pipeline_mode() uses cached content if available.""" cached = ["cached_item"] with ( patch("engine.app.load_cache", return_value=cached) as mock_load, patch("engine.app.fetch_all") as mock_fetch, patch("engine.app.DisplayRegistry.create") as mock_create, ): mock_display = Mock() mock_display.init = Mock() mock_display.get_dimensions = Mock(return_value=(80, 24)) mock_display.is_quit_requested = Mock(return_value=True) mock_display.clear_quit_request = Mock() mock_display.show = Mock() mock_display.cleanup = Mock() mock_create.return_value = mock_display try: run_pipeline_mode("demo") except (KeyboardInterrupt, SystemExit): pass # Verify fetch_all was NOT called (cache was used) mock_fetch.assert_not_called() mock_load.assert_called_once() def test_run_pipeline_mode_creates_display(self): """run_pipeline_mode() creates a display backend.""" with ( patch("engine.app.load_cache", return_value=["item"]), patch("engine.app.DisplayRegistry.create") as mock_create, ): mock_display = Mock() mock_display.init = Mock() mock_display.get_dimensions = Mock(return_value=(80, 24)) mock_display.is_quit_requested = Mock(return_value=True) mock_display.clear_quit_request = Mock() mock_display.show = Mock() mock_display.cleanup = Mock() mock_create.return_value = mock_display try: run_pipeline_mode("gallery-display-terminal") except (KeyboardInterrupt, SystemExit): pass # Verify display was created with 'terminal' (preset display) mock_create.assert_called_once_with("terminal") def test_run_pipeline_mode_respects_display_cli_flag(self): """run_pipeline_mode() uses --display CLI flag if provided.""" sys.argv = ["mainline.py", "--display", "websocket"] with ( patch("engine.app.load_cache", return_value=["item"]), patch("engine.app.DisplayRegistry.create") as mock_create, ): mock_display = Mock() mock_display.init = Mock() mock_display.get_dimensions = Mock(return_value=(80, 24)) mock_display.is_quit_requested = Mock(return_value=True) mock_display.clear_quit_request = Mock() mock_display.show = Mock() mock_display.cleanup = Mock() mock_create.return_value = mock_display try: run_pipeline_mode("demo") except (KeyboardInterrupt, SystemExit): pass # Verify display was created with CLI override mock_create.assert_called_once_with("websocket") def test_run_pipeline_mode_fetches_poetry_for_poetry_source(self): """run_pipeline_mode() fetches poetry for poetry preset.""" with ( patch("engine.app.load_cache", return_value=None), patch( "engine.app.fetch_poetry", return_value=(["poem"], None, None) ) as mock_fetch_poetry, patch("engine.app.fetch_all") as mock_fetch_all, patch("engine.app.DisplayRegistry.create") as mock_create, ): mock_display = Mock() mock_display.init = Mock() mock_display.get_dimensions = Mock(return_value=(80, 24)) mock_display.is_quit_requested = Mock(return_value=True) mock_display.clear_quit_request = Mock() mock_display.show = Mock() mock_display.cleanup = Mock() mock_create.return_value = mock_display try: run_pipeline_mode("poetry") except (KeyboardInterrupt, SystemExit): pass # Verify fetch_poetry was called, not fetch_all mock_fetch_poetry.assert_called_once() mock_fetch_all.assert_not_called() def test_run_pipeline_mode_discovers_effect_plugins(self): """run_pipeline_mode() discovers available effect plugins.""" with ( patch("engine.app.load_cache", return_value=["item"]), patch("engine.app.effects_plugins") as mock_effects, patch("engine.app.DisplayRegistry.create") as mock_create, ): mock_display = Mock() mock_display.init = Mock() mock_display.get_dimensions = Mock(return_value=(80, 24)) mock_display.is_quit_requested = Mock(return_value=True) mock_display.clear_quit_request = Mock() mock_display.show = Mock() mock_display.cleanup = Mock() mock_create.return_value = mock_display try: run_pipeline_mode("demo") except (KeyboardInterrupt, SystemExit): pass # Verify effects_plugins.discover_plugins was called mock_effects.discover_plugins.assert_called_once()