"""Unit tests for review queue.""" from __future__ import annotations import uuid from unittest.mock import AsyncMock, MagicMock import pytest from libs.review.queue import create_review_item, list_review_items, resolve_review_item def _make_mock_session() -> AsyncMock: session = AsyncMock() session.add = MagicMock() session.flush = AsyncMock() return session @pytest.mark.unit class TestCreateReviewItem: @pytest.mark.asyncio async def test_create_new_review_item(self) -> None: """Creating a review item for a new entity adds it to session.""" session = _make_mock_session() # No existing open item mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = None session.execute = AsyncMock(return_value=mock_result) await create_review_item( session=session, entity_type="parser_event", entity_id="EVT::test::001", priority="P1", reason_codes=["low_confidence"], snapshot_refs={"parse_id": 42}, ) session.add.assert_called_once() session.flush.assert_called_once() @pytest.mark.asyncio async def test_deduplicate_open_items(self) -> None: """Creating a review item for an already-open entity updates instead of creating.""" # Simulate existing open item existing_item = MagicMock() existing_item.status = "open" existing_item.priority = "P2" existing_item.reason_codes = ["low_confidence"] existing_item.suggested_overrides = None session = _make_mock_session() mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = existing_item session.execute = AsyncMock(return_value=mock_result) await create_review_item( session=session, entity_type="parser_event", entity_id="EVT::test::001", priority="P0", # escalate from P2 reason_codes=["rule_llm_conflict"], snapshot_refs={}, ) # Should NOT call session.add (update existing instead) session.add.assert_not_called() # Priority should be escalated assert existing_item.priority == "P0" # reason_codes merged assert "rule_llm_conflict" in existing_item.reason_codes @pytest.mark.asyncio async def test_resolve_review_item(self) -> None: """Resolving an open item updates status, reviewer, resolution fields.""" existing_item = MagicMock() existing_item.status = "open" review_id = uuid.uuid4() session = _make_mock_session() mock_result = MagicMock() mock_result.scalar_one_or_none.return_value = existing_item session.execute = AsyncMock(return_value=mock_result) await resolve_review_item( session=session, review_id=review_id, reviewer_id="analyst_01", resolution_type="accepted", root_cause="data looks correct", notes="verified against source", ) assert existing_item.status == "resolved" assert existing_item.reviewer_id == "analyst_01" assert existing_item.resolution_type == "accepted" @pytest.mark.asyncio async def test_list_review_items_with_status_filter(self) -> None: """list_review_items executes query with status filter and returns results.""" session = _make_mock_session() mock_result = MagicMock() mock_scalars = MagicMock() mock_scalars.all.return_value = [] mock_result.scalars.return_value = mock_scalars session.execute = AsyncMock(return_value=mock_result) items = await list_review_items(session, status="open") assert items == [] session.execute.assert_called_once()