You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

113 lines
3.8 KiB
Python

"""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()