"""The operator's half of the tuning surface. WHY THIS EXISTS The model moves these dials, so the browser's job is not "set the number". It is to show what was set, by whom, and on what argument, and to let the operator disagree. The load-bearing guard here is the last one. `/api/settings` is a generic key-value endpoint that accepts any key at all, and every retrieval floor IS an ordinary settings key. A floor written straight through it would land correctly and record nothing — a tuning history with holes in it, which is worse than no history because it reads as complete. So the generic endpoint routes those keys through `set_dial`, and that routing is asserted rather than remembered. """ import inspect import pytest def test_retrieval_blueprint_registered(): from grimoire.routes.retrieval import retrieval_bp assert retrieval_bp.name == "retrieval" assert retrieval_bp.url_prefix == "/api/retrieval" def test_retrieval_blueprint_registered_in_app(): from grimoire.app import create_app app = create_app() assert "retrieval" in app.blueprints def test_every_endpoint_is_reachable_on_the_app(): """Handlers existing is not the same as them being routed.""" from grimoire.app import create_app app = create_app() rules = { str(r.rule) for r in app.url_map.iter_rules() if r.endpoint.startswith("retrieval.") } assert rules == { "/api/retrieval/surfaces", "/api/retrieval/surfaces/", "/api/retrieval/tuning-history", "/api/retrieval/moments", "/api/retrieval/moments/mappings", "/api/retrieval/moments/proposals", "/api/retrieval/moments/proposals/judge", "/api/retrieval/reply-shapes", } def test_the_browser_and_the_agent_call_the_same_service(): """Parity. Two callers, one service — or the two surfaces drift and the guard that a reason is required exists on only one of them.""" from grimoire.mcp.tools import retrieval_tuning as tool from grimoire.routes import retrieval as routes from grimoire.services import retrieval_tuning as svc assert routes.set_dial is svc.set_dial assert tool.tuning_svc is svc def test_the_route_does_not_take_the_actor_from_the_caller(): """`actor` is the one field a reviewer leans on to answer "did I do this, or did the session?". A payload-supplied actor would let a model claim to be the operator, which turns the column into decoration.""" from grimoire.routes import retrieval as routes src = inspect.getsource(routes.tune_surface_route) assert 'actor="human"' in src assert 'data.get("actor"' not in src and 'data["actor"]' not in src # ── the hole the generic settings endpoint would otherwise leave ──────────── def test_every_registry_key_is_recognised_as_a_dial(): """Derived from the registry, both directions. A seventh surface added without a row here is a floor that can be written silently again.""" from grimoire.services.retrieval_surfaces import ( SURFACES, dial_for_key, sharing_floor, ) for name, surface in SURFACES.items(): # A shared floor resolves to one of the surfaces that read it, and # `set_dial` records the move on all of them. owner, dial = dial_for_key(surface.floor_key) assert dial == "floor" and name in sharing_floor(owner) assert dial_for_key(surface.budget_key) == (name, "budget") def test_an_ordinary_setting_is_not_mistaken_for_a_dial(): """The interception must be narrow. A false positive here would send an unrelated setting through a service that parses it as a float and rejects the save.""" from grimoire.services.retrieval_surfaces import dial_for_key for key in ("smtp_password", "kb_autoinject_enabled", "theme", "kb_planmatch_threshold", ""): assert dial_for_key(key) is None, key def test_the_settings_endpoint_routes_a_dial_through_the_recorder(): """THE GUARD. Asserted on the source because the alternative is a full request-context round trip for a branch whose whole content is "which function gets called" — and because what must not regress is precisely that this module reaches for `set_dial` at all.""" from grimoire.routes import settings as routes src = inspect.getsource(routes.update_settings_route) assert "dial_for_key" in src, ( "the generic settings endpoint no longer recognises retrieval dials — " "a floor written through it now moves with no event recorded, and the " "tuning history will say nothing happened" ) assert "set_dial" in src assert 'actor="human"' in src @pytest.mark.asyncio async def test_setting_a_dial_to_the_value_it_already_has_records_nothing(): """The Settings form re-sends every field on every save. Without this, one press of Save writes six rows saying the operator set six dials to the numbers they were already on — and a history nobody can skim is one nobody reads.""" from unittest.mock import AsyncMock, MagicMock, patch from grimoire.services import retrieval_tuning as rt from tests.helpers import make_mock_session session = make_mock_session() with patch.object(rt, "async_session", MagicMock(return_value=session)), \ patch.object(rt, "set_setting", AsyncMock()) as setter, \ patch.object(rt, "floor_for", AsyncMock(return_value=0.72)), \ patch.object(rt, "budget_for", AsyncMock(return_value=3)): out = await rt.set_dial(1, "prompt_rule", "floor", 0.72, reason="re-saved the settings form untouched") assert out["unchanged"] is True session.add.assert_not_called() # And the setting is left alone too: rewriting the same value would bump # whatever timestamp the row carries for no reason. setter.assert_not_called() # ── the moments view ──────────────────────────────────────────────────── async def _moments_call(handler_name, path, method="GET", **kw): from types import SimpleNamespace from quart import Quart, g from grimoire.routes import retrieval as routes app = Quart(__name__) async with app.test_request_context(path, method=method, **kw): g.user = SimpleNamespace(id=7) resp = await getattr(routes, handler_name).__wrapped__() if isinstance(resp, tuple): return await resp[0].get_json(), resp[1] return await resp.get_json(), 200 async def test_the_view_gets_the_catalog_with_counts_beside_it(): from unittest.mock import AsyncMock, patch from grimoire.routes import retrieval as routes usage = AsyncMock(return_value={"by_moment": {"work.finish": {"delivered": 2}}}) with patch.object(routes.moment_actions_svc, "actions_by_moment", AsyncMock(return_value={"actions": {}, "removed_defaults": []})), \ patch.object(routes.rulebooks_svc, "mount_counts", AsyncMock(return_value={"work.finish": 1})), \ patch.object(routes, "moment_usage", usage): body, status = await _moments_call("moments_route", "/api/retrieval/moments", query_string={"days": "7"}) assert status == 200 assert body["mounted"] == {"work.finish": 1} assert body["usage"]["days"] == 7 assert body["usage"]["by_moment"]["work.finish"]["delivered"] == 2 assert {m["name"] for m in body["moments"]} >= {"work.finish", "reply.report"} async def test_a_failed_mount_count_is_flagged_not_shown_as_nothing(): from unittest.mock import AsyncMock, patch from grimoire.routes import retrieval as routes with patch.object(routes.moment_actions_svc, "actions_by_moment", AsyncMock(return_value={"actions": {}, "removed_defaults": []})), \ patch.object(routes.rulebooks_svc, "mount_counts", AsyncMock(side_effect=RuntimeError("down"))), \ patch.object(routes, "moment_usage", AsyncMock(return_value={"by_moment": {}})): body, status = await _moments_call("moments_route", "/api/retrieval/moments") assert status == 200 assert body["mounted"] == {} and body["mounted_failed"] is True assert body["usage"]["days"] == 30 async def test_a_mapping_removal_can_come_as_query_parameters(): """The browser's DELETE carries no body (api/client.apiDelete), so the mapping it removes arrives in the query string.""" from unittest.mock import AsyncMock, patch from grimoire.routes import retrieval as routes unmap = AsyncMock(return_value={"change": "removed this install's mapping"}) with patch.object(routes.moment_actions_svc, "unmap_action", unmap): body, status = await _moments_call( "unmap_action_route", "/api/retrieval/moments/mappings", method="DELETE", query_string={"tool": "Bash", "match": "make ship", "moment": "work.deliver"}, ) assert status == 200 assert unmap.await_args.args == (7, "Bash", "make ship", "work.deliver") assert unmap.await_args.kwargs["actor"] == "human" async def test_a_mapping_with_neither_body_nor_parameters_is_refused(): body, status = await _moments_call( "unmap_action_route", "/api/retrieval/moments/mappings", method="DELETE", ) assert status == 400