test: add notifications integration behavior guard

This commit is contained in:
CREDO23 2026-06-03 21:53:06 +02:00
parent 339ec31cad
commit 3f770203ca
8 changed files with 938 additions and 0 deletions

View file

@ -0,0 +1,164 @@
"""Behavior guard for the shared find/upsert/update logic (BaseNotificationHandler).
Uses the connector-indexing handler instance to drive the base methods against
real Postgres, pinning upsert dedup, search-space scoping, and status stamping.
"""
from __future__ import annotations
import pytest
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.db import SearchSpace, User
from app.notifications.persistence import Notification
from app.notifications.service import NotificationService
pytestmark = pytest.mark.integration
handler = NotificationService.connector_indexing
async def test_find_or_create_creates_with_progress_metadata(
db_session: AsyncSession,
db_user: User,
db_search_space: SearchSpace,
):
"""Creating a notification seeds operation id, in-progress status, and start time."""
notification = await handler.find_or_create_notification(
session=db_session,
user_id=db_user.id,
operation_id="op-create",
title="Title",
message="Message",
search_space_id=db_search_space.id,
)
assert notification.notification_metadata["operation_id"] == "op-create"
assert notification.notification_metadata["status"] == "in_progress"
assert "started_at" in notification.notification_metadata
async def test_find_or_create_upserts_same_operation(
db_session: AsyncSession,
db_user: User,
db_search_space: SearchSpace,
):
"""Reusing an operation id updates the same row instead of creating a duplicate."""
first = await handler.find_or_create_notification(
session=db_session,
user_id=db_user.id,
operation_id="op-upsert",
title="First",
message="First message",
search_space_id=db_search_space.id,
)
second = await handler.find_or_create_notification(
session=db_session,
user_id=db_user.id,
operation_id="op-upsert",
title="Second",
message="Second message",
search_space_id=db_search_space.id,
)
assert second.id == first.id
assert second.title == "Second"
assert second.message == "Second message"
count = await db_session.scalar(
select(func.count(Notification.id)).where(
Notification.user_id == db_user.id,
Notification.notification_metadata["operation_id"].astext == "op-upsert",
)
)
assert count == 1
async def test_find_by_operation_is_scoped_to_search_space(
db_session: AsyncSession,
db_user: User,
db_search_space: SearchSpace,
):
"""Operation-id lookup is scoped per search space, so other spaces don't match."""
await handler.find_or_create_notification(
session=db_session,
user_id=db_user.id,
operation_id="op-scoped",
title="Title",
message="Message",
search_space_id=db_search_space.id,
)
other_space = SearchSpace(name="Other Space", user_id=db_user.id)
db_session.add(other_space)
await db_session.flush()
found_other = await handler.find_notification_by_operation(
session=db_session,
user_id=db_user.id,
operation_id="op-scoped",
search_space_id=other_space.id,
)
assert found_other is None
found_same = await handler.find_notification_by_operation(
session=db_session,
user_id=db_user.id,
operation_id="op-scoped",
search_space_id=db_search_space.id,
)
assert found_same is not None
async def test_update_notification_completed_stamps_completed_at(
db_session: AsyncSession,
db_user: User,
db_search_space: SearchSpace,
):
"""Completing a notification stamps completed_at and merges metadata updates."""
notification = await handler.find_or_create_notification(
session=db_session,
user_id=db_user.id,
operation_id="op-complete",
title="Title",
message="Message",
search_space_id=db_search_space.id,
)
updated = await handler.update_notification(
session=db_session,
notification=notification,
status="completed",
metadata_updates={"indexed_count": 7},
)
assert updated.notification_metadata["status"] == "completed"
assert "completed_at" in updated.notification_metadata
assert updated.notification_metadata["indexed_count"] == 7
async def test_update_notification_failed_stamps_completed_at(
db_session: AsyncSession,
db_user: User,
db_search_space: SearchSpace,
):
"""Failing a notification also stamps completed_at for the terminal state."""
notification = await handler.find_or_create_notification(
session=db_session,
user_id=db_user.id,
operation_id="op-fail",
title="Title",
message="Message",
search_space_id=db_search_space.id,
)
updated = await handler.update_notification(
session=db_session,
notification=notification,
status="failed",
)
assert updated.notification_metadata["status"] == "failed"
assert "completed_at" in updated.notification_metadata