diff --git a/src/__tests__/notification-delivery-identity.test.ts b/src/__tests__/notification-delivery-identity.test.ts index c143bcbc5..572c0c28a 100644 --- a/src/__tests__/notification-delivery-identity.test.ts +++ b/src/__tests__/notification-delivery-identity.test.ts @@ -60,12 +60,9 @@ const recipientUserId = "guardian-recipient"; beforeEach(() => { vi.resetAllMocks(); - vi.mocked(prisma.user.findUnique).mockImplementation(async (args) => { - const id = (args as { where: { id: string } }).where.id; - return id === recordUserId - ? ({ managedProfileAt: new Date() } as never) - : (null as never); - }); + vi.mocked(prisma.user.findUnique).mockResolvedValue({ + managedProfileAt: new Date(), + } as never); vi.mocked(prisma.notificationChannel.findMany).mockResolvedValue([ { id: "recipient-channel", diff --git a/src/app/api/admin/notifications/diagnostic/route.ts b/src/app/api/admin/notifications/diagnostic/route.ts index 9c80e4df9..1e2be30b7 100644 --- a/src/app/api/admin/notifications/diagnostic/route.ts +++ b/src/app/api/admin/notifications/diagnostic/route.ts @@ -59,6 +59,8 @@ interface DiagnosticChannel { } interface DiagnosticPushAttempt { + recordUserId: string; + recipientUserId: string; eventType: string; channel: string; result: string; @@ -170,18 +172,23 @@ export const GET = apiHandler(async () => { where: { userId: user.id }, select: { type: true, enabled: true, config: true }, }), - // v1.4.49 — trailing 20 push attempts for the calling admin's own - // account. The `(user_id, created_at DESC)` index on `push_attempts` - // turns the orderBy + take into a single index scan, so the cost is - // bounded regardless of the user's lifetime push volume. + // Trailing delivery attempts for the calling account as recipient. The + // recipient index keeps the bounded diagnostic read independent of the + // account's lifetime notification volume. prisma.pushAttempt.findMany({ // The reminder tick's per-slot dedup anchors live in this table under // a sentinel channel. They are bookkeeping, not delivery attempts, // and would push the real sends out of the trailing window. - where: { userId: user.id, channel: { not: REMINDER_DEDUP_CHANNEL } }, + where: { + recipientUserId: user.id, + channel: { not: REMINDER_DEDUP_CHANNEL }, + }, orderBy: { createdAt: "desc" }, take: 20, select: { + userId: true, + recordUserId: true, + recipientUserId: true, eventType: true, channel: true, result: true, @@ -215,6 +222,8 @@ export const GET = apiHandler(async () => { // `DiagnosticPushAttempt` interface above (the existing consumer // contract uses `at`, not `createdAt`). const recentPushAttempts: DiagnosticPushAttempt[] = pushAttempts.map((a) => ({ + recordUserId: a.recordUserId ?? a.userId, + recipientUserId: a.recipientUserId ?? a.userId, eventType: a.eventType, channel: a.channel, result: a.result,