diff --git a/tests/e2e/management/test_config_misc_endpoints_e2e.py b/tests/e2e/management/test_config_misc_endpoints_e2e.py index 6c4de621271..959c82085e7 100644 --- a/tests/e2e/management/test_config_misc_endpoints_e2e.py +++ b/tests/e2e/management/test_config_misc_endpoints_e2e.py @@ -131,6 +131,7 @@ class CacheSettingsValue(BaseModel): type: str host: str = "" port: str = "" + namespace: str = "" class CacheSettingsUpdateBody(BaseModel): @@ -141,6 +142,7 @@ class CacheCurrentValues(BaseModel): type: str | None = None host: str | None = None port: str | None = None + namespace: str | None = None class CacheGetResponse(BaseModel): @@ -374,40 +376,51 @@ class TestCacheSettings: def test_update_persists_cache_backend_to_get( self, client: ManagementClient, resources: ResourceManager ) -> None: - """Exercise the update route without changing global state: capture the live - cache backend and write exactly that back, so the config the proxy ends on is - byte-for-byte the one it started with. A teardown restore of the same captured - settings is the safety net if the body fails partway. The update route is only - meaningful against a configured cache, so an unconfigured proxy fails loudly - here rather than being silently switched to redis.""" + """Prove the update route actually persists by round-tripping a genuinely new + value. The cache namespace is a benign key prefix that does not change which + backend the proxy talks to, so writing a fresh one and reading it back proves a + no-op write route would be caught, without repointing the shared cache. The + original settings are restored on teardown. The update route is only meaningful + against a configured cache, so an unconfigured proxy fails loudly here rather + than being silently switched to redis.""" before = self._read_settings(client) assert before.type is not None, ( "GET /cache/settings reported no cache type; refusing to invent one and mutate the shared proxy" ) - captured = CacheSettingsValue(type=before.type, host=before.host or "", port=before.port or "") + captured = CacheSettingsValue( + type=before.type, + host=before.host or "", + port=before.port or "", + namespace=before.namespace or "", + ) resources.defer(lambda: self._write_settings(client, captured)) + target_namespace = f"e2e-cache-ns-{unique_marker()}" + target = captured.model_copy(update={"namespace": target_namespace}) updated = unwrap( client.proxy.transport.post( "/cache/settings", headers=client.proxy.transport.master, - json=CacheSettingsUpdateBody(cache_settings=captured), + json=CacheSettingsUpdateBody(cache_settings=target), response_type=CacheUpdateResponse, ) ) assert updated.status == "success", f"/cache/settings update status {updated.status!r}, expected 'success'" - assert updated.settings.type == captured.type, ( - f"/cache/settings echoed type {updated.settings.type!r}, wrote {captured.type!r}" + assert updated.settings.namespace == target_namespace, ( + f"/cache/settings echoed namespace {updated.settings.namespace!r}, wrote {target_namespace!r}" ) def reflected() -> CacheCurrentValues | None: current = self._read_settings(client) - return current if current.type == captured.type else None + return current if current.namespace == target_namespace else None - after = _poll(client, reflected, f"/cache/settings never reported type {captured.type!r} after the update") - assert after.host == captured.host and after.port == captured.port, ( - f"/cache/settings persisted host/port {after.host!r}/{after.port!r}, " - f"wrote {captured.host!r}/{captured.port!r}" + after = _poll( + client, reflected, f"/cache/settings never reported namespace {target_namespace!r} after the update" + ) + assert after.type == captured.type and after.host == captured.host and after.port == captured.port, ( + f"/cache/settings changed the backend while updating the namespace: type/host/port now " + f"{after.type!r}/{after.host!r}/{after.port!r}, expected " + f"{captured.type!r}/{captured.host!r}/{captured.port!r}" ) @staticmethod