"""Integration tests for chat-mode settings extraction. Replaces three earlier tests that only asserted on locally-constructed dict literals (no production-code import) with checks that actually exercise the chat route's settings helper and reproduce the snapshot field-extraction the research pipeline performs. These do NOT spin up a full DB — ``_load_settings`` is patched to return a controlled snapshot and the extraction is asserted against the same field names the production research path reads. """ from unittest.mock import MagicMock, patch class TestLoadSettingsHelper: """Tests for the ``chat/routes.py::_load_settings`` helper.""" def test_load_settings_bypasses_cache(self): """``_load_settings`` must call ``get_all_settings(bypass_cache=True)`` so a UI setting change just before the chat send takes effect on the very next research run (matches the behaviour of ``research_routes.start_research``).""" from src.local_deep_research.chat import routes as chat_routes with ( patch.object(chat_routes, "get_user_db_session") as mock_get_db, patch.object(chat_routes, "SettingsManager") as mock_manager_cls, ): mock_db = MagicMock() mock_get_db.return_value.__enter__.return_value = mock_db mock_manager = MagicMock() mock_manager.get_all_settings.return_value = { "llm.provider": {"value": "ollama"} } mock_manager_cls.return_value = mock_manager snapshot = chat_routes._load_settings("alice") mock_manager_cls.assert_called_once_with(db_session=mock_db) mock_manager.get_all_settings.assert_called_once_with(bypass_cache=True) assert snapshot == {"llm.provider": {"value": "ollama"}} def test_load_settings_returns_independent_snapshot_per_call(self): """Each ``_load_settings`` call returns the dict from the manager — independent calls don't share state by reference. This backs the snapshot semantics relied on by the research pipeline, where the snapshot is supposed to survive global setting changes that happen during the research run.""" from src.local_deep_research.chat import routes as chat_routes snapshots: list[dict] = [ {"llm.model": {"value": "first"}}, {"llm.model": {"value": "second"}}, ] results = [] def _make_manager(*args, **kwargs): m = MagicMock() m.get_all_settings.return_value = snapshots[len(results)] return m with ( patch.object(chat_routes, "get_user_db_session") as mock_get_db, patch.object( chat_routes, "SettingsManager", side_effect=_make_manager, ), ): mock_get_db.return_value.__enter__.return_value = MagicMock() results.append(chat_routes._load_settings("alice")) results.append(chat_routes._load_settings("alice")) # Two distinct dicts — modifying one doesn't bleed into the other. results[0]["llm.model"]["value"] = "mutated" assert results[1]["llm.model"]["value"] == "second" class TestSettingsExtractionMatchesProduction: """The extraction pattern used by ``send_message`` to pull settings out of the snapshot before invoking the research pipeline. If the schema for one of these keys changes, this test fails before the routes silently fall back to a default the user didn't ask for.""" EXTRACTION_KEYS = ( "llm.provider", "llm.model", "search.tool", "search.iterations", "search.questions_per_iteration", "search.search_strategy", ) def test_full_snapshot_round_trip(self): """Every key the chat route reads from the snapshot is present in the default settings shipped with the project (``defaults/default_settings.json`` loaded at import time by the SettingsManager). If any key is renamed or removed without updating the route, this fails.""" import json from pathlib import Path import local_deep_research defaults_path = ( Path(local_deep_research.__file__).parent / "defaults" / "default_settings.json" ) with defaults_path.open() as fh: defaults = json.load(fh) for key in self.EXTRACTION_KEYS: assert key in defaults, ( f"chat route reads {key!r} from settings_snapshot, but it " "is missing from default_settings.json — a rename will " "silently fall back to None at runtime." )