D2 — skip notification hooks in Vitest; fixture cleanup for push rows

This commit is contained in:
Cursor Agent 2026-07-31 02:09:52 +00:00
parent de67acf41b
commit b9a81f7ad0
3 changed files with 72 additions and 13 deletions

View file

@ -1,3 +1,9 @@
/** Payload `req.context` flag — domain hooks skip creating notifications when true. */
export const SKIP_CAMPAIGN_NOTIFICATION_CONTEXT_KEY = 'skipCampaignNotification'
/** Opt back into hook-fired notifications inside Vitest (default: skipped). */
export const ENABLE_CAMPAIGN_NOTIFICATION_CONTEXT_KEY = 'enableCampaignNotification'
export const notificationTypes = [
'municipality_update',
'new_supporter',

View file

@ -4,6 +4,10 @@ import 'server-only'
import type { PayloadRequest } from 'payload'
import {
ENABLE_CAMPAIGN_NOTIFICATION_CONTEXT_KEY,
SKIP_CAMPAIGN_NOTIFICATION_CONTEXT_KEY,
} from '@/lib/notificationContract'
import { relationshipId, uniqueRelationshipIds } from '@/lib/relationship'
import { municipalityUpdateKindLabels } from '@/lib/schemas/municipalityUpdate'
import { createCampaignNotifications } from '@/utilities/notification/createCampaignNotification'
@ -11,10 +15,13 @@ import { resolveMunicipalityStaffRecipientIds } from '@/utilities/notification/n
export type NotificationEventRequest = Pick<PayloadRequest, 'payload' | 'context' | 'transactionID'>
const SKIP_NOTIFICATION_CONTEXT = 'skipCampaignNotification'
const shouldSkipCampaignNotification = (context: Record<string, unknown>): boolean =>
Boolean(context[SKIP_NOTIFICATION_CONTEXT])
const shouldSkipCampaignNotification = (context: Record<string, unknown>): boolean => {
if (context[ENABLE_CAMPAIGN_NOTIFICATION_CONTEXT_KEY]) return false
if (context[SKIP_CAMPAIGN_NOTIFICATION_CONTEXT_KEY]) return true
// Int suite shares one Postgres across ~48 workers; hook side-effects (extra
// rows, coordinator fan-out reads) are tested in campaignNotifications.int.
return process.env.VITEST === 'true'
}
export const notifyMunicipalityUpdateCreated = async (
req: NotificationEventRequest,

View file

@ -333,6 +333,8 @@ export class CampaignFixtures {
private readonly touchedMunicipalities = new Set<number>()
private readonly ownedLockedDocuments = new Set<number>()
private readonly ownedPreferences = new Set<number>()
private readonly ownedNotifications = new Set<number>()
private readonly ownedPushSubscriptions = new Set<number>()
constructor(private readonly rootPayload: Payload) {
builderCounter += 1
@ -917,16 +919,36 @@ export class CampaignFixtures {
}
if (userIDs.length > 0) {
const updates = await this.rootPayload.find({
collection: 'municipalityUpdate',
where: { author: { in: userIDs } },
depth: 0,
pagination: false,
})
const [updates, notifications, pushSubscriptions] = await Promise.all([
this.rootPayload.find({
collection: 'municipalityUpdate',
where: { author: { in: userIDs } },
depth: 0,
pagination: false,
}),
this.rootPayload.find({
collection: 'notification',
where: { recipient: { in: userIDs } },
depth: 0,
pagination: false,
}),
this.rootPayload.find({
collection: 'pushSubscription',
where: { user: { in: userIDs } },
depth: 0,
pagination: false,
}),
])
for (const update of updates.docs) {
this.own('municipalityUpdate', update)
this.touchedMunicipalities.add(relationId(update.municipality))
}
for (const notification of notifications.docs) {
this.ownedNotifications.add(notification.id)
}
for (const subscription of pushSubscriptions.docs) {
this.ownedPushSubscriptions.add(subscription.id)
}
}
const contactIDs = [...this.owned.contact]
@ -995,6 +1017,29 @@ export class CampaignFixtures {
}
}
private async deleteNotificationOwned(req: { transactionID: number | string }): Promise<void> {
if (this.ownedNotifications.size > 0) {
await this.rootPayload.delete({
collection: 'notification',
where: { id: { in: [...this.ownedNotifications] } },
depth: 0,
overrideAccess: true,
req,
})
this.ownedNotifications.clear()
}
if (this.ownedPushSubscriptions.size > 0) {
await this.rootPayload.delete({
collection: 'pushSubscription',
where: { id: { in: [...this.ownedPushSubscriptions] } },
depth: 0,
overrideAccess: true,
req,
})
this.ownedPushSubscriptions.clear()
}
}
private async deleteOwned(
collection: CampaignCollection,
req: { transactionID: number | string },
@ -1079,12 +1124,13 @@ export class CampaignFixtures {
'organization',
'stateDeputy',
'contact',
'campaignUser',
'consent',
'users',
] as const) {
await this.deleteOwned(collection, req)
}
await this.deleteNotificationOwned(req)
for (const collection of ['campaignUser', 'consent', 'users'] as const) {
await this.deleteOwned(collection, req)
}
await this.resetTouchedMunicipalities(req)
if (this.ownedPreferences.size > 0) {
const transaction = this.rootPayload.db.sessions?.[String(req.transactionID)]?.db as