mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-06 20:15:17 +02:00
165 lines
5 KiB
Python
165 lines
5 KiB
Python
|
|
"""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
|