|
| 1 | +"""OPENRAG_SKIP_OS_SECURITY_SETUP gates the startup_orchestrator call to |
| 2 | +setup_opensearch_security. |
| 3 | +
|
| 4 | +Two cases: |
| 5 | + * Flag false (default): setup_opensearch_security is invoked. |
| 6 | + * Flag true: it is NOT invoked, skip log line is emitted. |
| 7 | +""" |
| 8 | + |
| 9 | +import sys |
| 10 | +from pathlib import Path |
| 11 | +from unittest.mock import AsyncMock, MagicMock, patch |
| 12 | + |
| 13 | +import pytest |
| 14 | + |
| 15 | +ROOT = Path(__file__).resolve().parent.parent.parent.parent |
| 16 | +SRC = ROOT / "src" |
| 17 | +if str(SRC) not in sys.path: |
| 18 | + sys.path.insert(0, str(SRC)) |
| 19 | + |
| 20 | + |
| 21 | +def _services_stub(): |
| 22 | + """Minimal services dict — only models_service is touched before the |
| 23 | + OpenSearch security block, and we want its call to fail loudly so the |
| 24 | + test fails fast if it changes.""" |
| 25 | + models = MagicMock() |
| 26 | + models.update_model_registry = AsyncMock() |
| 27 | + return {"models_service": models} |
| 28 | + |
| 29 | + |
| 30 | +@pytest.mark.asyncio |
| 31 | +async def test_setup_runs_when_flag_false(monkeypatch): |
| 32 | + """Default (flag false): setup_opensearch_security is called.""" |
| 33 | + import services.startup_orchestrator as orchestrator |
| 34 | + |
| 35 | + monkeypatch.setattr(orchestrator, "OPENRAG_SKIP_OS_SECURITY_SETUP", False) |
| 36 | + monkeypatch.setattr(orchestrator, "DISABLE_INGEST_WITH_LANGFLOW", False) |
| 37 | + # IBM_AUTH_ENABLED is imported lazily inside startup_tasks(). |
| 38 | + monkeypatch.setattr("config.settings.IBM_AUTH_ENABLED", False, raising=False) |
| 39 | + |
| 40 | + setup_mock = AsyncMock() |
| 41 | + with patch( |
| 42 | + "utils.opensearch_utils.setup_opensearch_security", setup_mock |
| 43 | + ), patch.object(orchestrator, "wait_for_opensearch", AsyncMock()), patch.object( |
| 44 | + orchestrator, "init_index", AsyncMock() |
| 45 | + ), patch.object( |
| 46 | + orchestrator, "configure_alerting_security", AsyncMock() |
| 47 | + ), patch.object( |
| 48 | + orchestrator, "_reingest_default_docs_on_upgrade_if_needed", |
| 49 | + AsyncMock(return_value=False), |
| 50 | + ), patch.object( |
| 51 | + orchestrator, "_update_mcp_server_urls", AsyncMock() |
| 52 | + ): |
| 53 | + # Force the post-security work to exit early — config.edited=False |
| 54 | + # short-circuits both the recovery init_index and the flow check. |
| 55 | + with patch.object( |
| 56 | + orchestrator, "get_openrag_config", |
| 57 | + MagicMock(return_value=MagicMock(edited=False, knowledge=MagicMock(embedding_model=None))), |
| 58 | + ): |
| 59 | + services = _services_stub() |
| 60 | + services["task_service"] = MagicMock() |
| 61 | + services["document_service"] = MagicMock() |
| 62 | + services["langflow_file_service"] = MagicMock() |
| 63 | + services["session_manager"] = MagicMock() |
| 64 | + services["langflow_mcp_service"] = MagicMock() |
| 65 | + services["flows_service"] = MagicMock( |
| 66 | + ensure_flows_exist=AsyncMock(return_value=set()) |
| 67 | + ) |
| 68 | + await orchestrator.startup_tasks(services) |
| 69 | + |
| 70 | + assert setup_mock.await_count == 1, ( |
| 71 | + "setup_opensearch_security must run when OPENRAG_SKIP_OS_SECURITY_SETUP is false" |
| 72 | + ) |
| 73 | + |
| 74 | + |
| 75 | +@pytest.mark.asyncio |
| 76 | +async def test_setup_skipped_when_flag_true(monkeypatch): |
| 77 | + """Flag true: setup_opensearch_security is NOT called.""" |
| 78 | + import services.startup_orchestrator as orchestrator |
| 79 | + |
| 80 | + monkeypatch.setattr(orchestrator, "OPENRAG_SKIP_OS_SECURITY_SETUP", True) |
| 81 | + monkeypatch.setattr(orchestrator, "DISABLE_INGEST_WITH_LANGFLOW", False) |
| 82 | + monkeypatch.setattr("config.settings.IBM_AUTH_ENABLED", False, raising=False) |
| 83 | + |
| 84 | + setup_mock = AsyncMock() |
| 85 | + # Spy on the orchestrator's bound logger so we can assert the skip line |
| 86 | + # was emitted without depending on caplog propagation through the |
| 87 | + # project's custom logger wrapper. |
| 88 | + logger_spy = MagicMock() |
| 89 | + monkeypatch.setattr(orchestrator, "logger", logger_spy) |
| 90 | + |
| 91 | + with patch( |
| 92 | + "utils.opensearch_utils.setup_opensearch_security", setup_mock |
| 93 | + ), patch.object(orchestrator, "wait_for_opensearch", AsyncMock()), patch.object( |
| 94 | + orchestrator, "init_index", AsyncMock() |
| 95 | + ), patch.object( |
| 96 | + orchestrator, "configure_alerting_security", AsyncMock() |
| 97 | + ), patch.object( |
| 98 | + orchestrator, "_reingest_default_docs_on_upgrade_if_needed", |
| 99 | + AsyncMock(return_value=False), |
| 100 | + ), patch.object( |
| 101 | + orchestrator, "_update_mcp_server_urls", AsyncMock() |
| 102 | + ): |
| 103 | + with patch.object( |
| 104 | + orchestrator, "get_openrag_config", |
| 105 | + MagicMock(return_value=MagicMock(edited=False, knowledge=MagicMock(embedding_model=None))), |
| 106 | + ): |
| 107 | + services = _services_stub() |
| 108 | + services["task_service"] = MagicMock() |
| 109 | + services["document_service"] = MagicMock() |
| 110 | + services["langflow_file_service"] = MagicMock() |
| 111 | + services["session_manager"] = MagicMock() |
| 112 | + services["langflow_mcp_service"] = MagicMock() |
| 113 | + services["flows_service"] = MagicMock( |
| 114 | + ensure_flows_exist=AsyncMock(return_value=set()) |
| 115 | + ) |
| 116 | + await orchestrator.startup_tasks(services) |
| 117 | + |
| 118 | + assert setup_mock.await_count == 0, ( |
| 119 | + "setup_opensearch_security must NOT run when OPENRAG_SKIP_OS_SECURITY_SETUP is true" |
| 120 | + ) |
| 121 | + info_messages = [call.args[0] for call in logger_spy.info.call_args_list if call.args] |
| 122 | + assert any( |
| 123 | + "Skipping OpenSearch security setup at startup" in msg |
| 124 | + for msg in info_messages |
| 125 | + ), f"expected skip log line not emitted; got: {info_messages}" |
0 commit comments