"""WP-230E: the session-store production decision is enforced, not just documented. See docs/02-security/SESSION_STORE_DECISION.md. Two things are tested: 1. scripts/compose_release_check.py actually fails the release gate if control-api is ever scaled beyond one replica (the concrete enforcement mechanism the decision doc points to), not just that the doc says so. 2. The documented single-instance consequence -- a restart invalidates every session -- is a real, observable property of SessionStore, not an assumption. """ from __future__ import annotations import tempfile import unittest from datetime import UTC, datetime from pathlib import Path import yaml # type: ignore[import-untyped] from scripts.compose_release_check import check_compose from services.control_api.auth.bff import SessionStore from services.control_api.auth.errors import SessionExpired from services.control_api.auth.models import ( AuthenticatedPrincipal, ManagementRole, PrincipalKind, TokenSet, ) ROOT = Path(__file__).resolve().parents[1] REAL_COMPOSE = ROOT / "infrastructure" / "compose" / "docker-compose.yml" def _minimal_compose(extra_control_api: dict[str, object] | None = None) -> dict[str, object]: control_api: dict[str, object] = {"image": "local/itworx-mcp-hub-control:dev"} if extra_control_api: control_api.update(extra_control_api) return { "name": "itworx-mcp-hub", "services": { "control-api": control_api, "docker-read-broker": { "image": "local/itworx-mcp-hub-broker:dev", "volumes": ["/var/run/docker.sock:/var/run/docker.sock:ro"], }, }, } class ComposeSingleInstanceEnforcementTests(unittest.TestCase): def _check(self, document: dict[str, object]) -> dict[str, object]: with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "docker-compose.yml" path.write_text(yaml.safe_dump(document), encoding="utf-8") return check_compose(path) def test_control_api_scaled_to_two_replicas_fails_the_gate(self) -> None: result = self._check(_minimal_compose({"deploy": {"replicas": 2}})) self.assertIn("control-api", result["singleInstanceViolations"]) self.assertFalse(result["pass"]) def test_control_api_with_no_deploy_block_passes(self) -> None: result = self._check(_minimal_compose()) self.assertEqual(result["singleInstanceViolations"], []) self.assertTrue(result["pass"]) def test_control_api_explicitly_pinned_to_one_replica_passes(self) -> None: result = self._check(_minimal_compose({"deploy": {"replicas": 1}})) self.assertEqual(result["singleInstanceViolations"], []) self.assertTrue(result["pass"]) def test_real_compose_file_does_not_scale_control_api(self) -> None: result = check_compose(REAL_COMPOSE) self.assertEqual(result["singleInstanceViolations"], []) class SessionStoreRestartBehaviorTests(unittest.TestCase): """Proves the documented consequence of the in-memory decision is real: the store has no persistence, so a process restart (modeled here as constructing a fresh SessionStore -- the same thing a real restart does, a brand-new empty dict) invalidates every session that existed before it, rather than silently reusing stale state.""" def _principal(self) -> AuthenticatedPrincipal: return AuthenticatedPrincipal( kind=PrincipalKind.MANAGEMENT_USER, subject="user-1", issuer="https://fake.oidc.test", audience=("itworx-mcp-hub",), client_id=None, roles=frozenset({ManagementRole.VIEWER}), issued_at=datetime(2026, 1, 1, tzinfo=UTC), expires_at=datetime(2030, 1, 1, tzinfo=UTC), ) def _store(self) -> SessionStore: return SessionStore( ttl_seconds=3600, cookie_name="hub_session", same_site="lax", secure=True ) def test_session_does_not_survive_a_simulated_restart(self) -> None: first_process = self._store() cookie = first_process.create( self._principal(), TokenSet(access_token="access", id_token=None, refresh_token=None), # noqa: S106 ) second_process = self._store() with self.assertRaises(SessionExpired): second_process.get(cookie.value) def test_the_same_process_still_recognizes_its_own_session(self) -> None: store = self._store() cookie = store.create( self._principal(), TokenSet(access_token="access", id_token=None, refresh_token=None), # noqa: S106 ) record = store.get(cookie.value) self.assertEqual(record.csrf_token, cookie.csrf_token) if __name__ == "__main__": unittest.main()