from __future__ import annotations from types import SimpleNamespace import unittest from unittest.mock import patch from services.openai_responses.protocol import ResponsesAPIError from services.openai_responses.state import ( IDEMPOTENCY_CONSTRAINT, _integrity_constraint_name, _load_chain, delete_response, get_idempotent_response, get_response, ) from sqlalchemy.exc import IntegrityError class _ScalarResult: def __init__(self, value): self.value = value def scalar_one_or_none(self): return self.value class _FakeSession: def __init__(self, rows, *, selected=None): self.rows = rows self.selected = selected self.commits = 0 async def __aenter__(self): return self async def __aexit__(self, *_args): return None async def get(self, _model, row_id, **_kwargs): return self.rows.get(row_id) async def execute(self, _statement): return _ScalarResult(self.selected) async def commit(self): self.commits += 1 async def refresh(self, _row): return None def _row( row_id, *, api_key_id="key_a", previous_response_id=None, deleted_at=None, status="completed", ): return SimpleNamespace( id=row_id, api_key_id=api_key_id, previous_response_id=previous_response_id, deleted_at=deleted_at, status=status, idempotency_key=None, request_hash=None, ) class ResponseTombstoneTests(unittest.IsolatedAsyncioTestCase): async def test_deleted_ancestor_remains_available_to_existing_child_chain(self): root = _row("resp_root", deleted_at=object()) child = _row("resp_child", previous_response_id=root.id) rows = {root.id: root, child.id: child} with patch( "services.openai_responses.state.SessionLocal", side_effect=lambda: _FakeSession(rows), ): chain = await _load_chain(child.id, api_key_id="key_a") self.assertEqual([row.id for row in chain], [root.id, child.id]) with self.assertRaises(ResponsesAPIError) as raised: await _load_chain(root.id, api_key_id="key_a") self.assertEqual(raised.exception.status_code, 404) async def test_deleted_and_cross_key_resources_are_hidden(self): rows = { "resp_deleted": _row("resp_deleted", deleted_at=object()), "resp_other": _row("resp_other", api_key_id="key_b"), } with patch( "services.openai_responses.state.SessionLocal", side_effect=lambda: _FakeSession(rows), ): for response_id in rows: with self.assertRaises(ResponsesAPIError) as raised: await get_response(response_id, api_key_id="key_a") self.assertEqual(raised.exception.status_code, 404) async def test_delete_rejects_in_progress_and_tombstones_terminal_response(self): running = _row("resp_running", status="in_progress") completed = _row("resp_completed") rows = {running.id: running, completed.id: completed} with patch( "services.openai_responses.state.SessionLocal", side_effect=lambda: _FakeSession(rows), ): with self.assertRaises(ResponsesAPIError) as raised: await delete_response(running.id, api_key_id="key_a") self.assertEqual(raised.exception.status_code, 409) deleted = await delete_response(completed.id, api_key_id="key_a") self.assertIsNotNone(deleted.deleted_at) class ResponseIdempotencyTests(unittest.IsolatedAsyncioTestCase): def test_only_named_unique_constraint_is_classified_as_idempotency(self): original = RuntimeError("unique violation") original.constraint_name = IDEMPOTENCY_CONSTRAINT error = IntegrityError("insert", {}, original) self.assertEqual( _integrity_constraint_name(error), IDEMPOTENCY_CONSTRAINT, ) self.assertIsNone( _integrity_constraint_name( IntegrityError("insert", {}, RuntimeError("other failure")) ) ) driver_error = RuntimeError("asyncpg unique violation") driver_error.constraint_name = IDEMPOTENCY_CONSTRAINT adapter_error = RuntimeError("SQLAlchemy asyncpg adapter") adapter_error.__cause__ = driver_error self.assertEqual( _integrity_constraint_name( IntegrityError("insert", {}, adapter_error) ), IDEMPOTENCY_CONSTRAINT, ) async def test_same_request_replays_and_different_request_conflicts(self): stored = _row("resp_idempotent") stored.idempotency_key = "request-1" stored.request_hash = "hash-a" with patch( "services.openai_responses.state.SessionLocal", side_effect=lambda: _FakeSession({}, selected=stored), ): replay = await get_idempotent_response( api_key_id="key_a", idempotency_key="request-1", request_hash="hash-a", ) self.assertIs(replay, stored) with self.assertRaises(ResponsesAPIError) as raised: await get_idempotent_response( api_key_id="key_a", idempotency_key="request-1", request_hash="hash-b", ) self.assertEqual(raised.exception.code, "idempotency_key_conflict") self.assertEqual(raised.exception.status_code, 409) if __name__ == "__main__": unittest.main()