fix notifications
Build & Push / Pipeline Tests (push) Failing after 1m8s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Successful in 53s
Test / API Unit Tests (push) Failing after 52s
Test / Homepage Unit Tests (push) Successful in 46s
Test / Carplace Unit Tests (push) Successful in 43s
Test / Admin Unit Tests (push) Successful in 43s
Test / Dashboard Unit Tests (push) Successful in 46s
Test / API Integration Tests (push) Successful in 1m7s

This commit is contained in:
root
2026-07-22 22:31:39 -04:00
parent bcabd17220
commit f6fcd7ce54
26 changed files with 1171 additions and 361 deletions
@@ -119,12 +119,12 @@ describe('operations validation API contracts', () => {
it('accepts valid company notification preferences and passes employee identity', async () => {
const res = await request(app).patch('/api/v1/notifications/company/preferences').send([
{ notificationType: 'BOOKING_CREATED', channel: 'EMAIL', enabled: true },
{ notificationType: 'NEW_BOOKING', channel: 'EMAIL', enabled: true },
])
expect(res.status).toBe(200)
expect(notificationService.setPreferences).toHaveBeenCalledWith('employee_1', [
{ notificationType: 'BOOKING_CREATED', channel: 'EMAIL', enabled: true },
{ notificationType: 'NEW_BOOKING', channel: 'EMAIL', enabled: true },
])
})
@@ -146,6 +146,7 @@ describe('operations API contracts', () => {
expect(res.status).toBe(200)
expect(res.body).toEqual({ data: { unread: 7 } })
expect(notificationService.countUnread).toHaveBeenCalledWith('employee_1')
})
it('GET /api/v1/notifications/history coerces limit and passes history filters to the service', async () => {
+59 -16
View File
@@ -182,35 +182,78 @@ export async function createCompanyNotification(
employeeId?: string,
overrides: Record<string, unknown> = {},
) {
return prisma.notification.create({
const notificationEvent = await prisma.notificationEvent.create({
data: {
companyId,
employeeId,
type: 'PAYMENT_RECEIVED',
title: 'Payment received',
body: 'A payment was recorded.',
channel: 'IN_APP',
status: 'PENDING',
...overrides,
type: (overrides.type ?? 'PAYMENT_RECEIVED') as any,
title: (overrides.title ?? 'Payment received') as string,
body: (overrides.body ?? 'A payment was recorded.') as string,
data: (overrides.data ?? {}) as any,
sourceType: (overrides.sourceType ?? 'test') as string,
sourceId: (overrides.sourceId ?? `source-${uid()}`) as string,
idempotencyKey: (overrides.idempotencyKey ?? `test-${uid()}`) as string,
} as any,
})
const recipient = await prisma.notificationRecipient.create({
data: {
notificationEventId: notificationEvent.id,
recipientType: 'EMPLOYEE',
employeeId,
readAt: (overrides.readAt ?? null) as Date | null,
} as any,
})
await prisma.notificationDelivery.create({
data: {
notificationRecipientId: recipient.id,
channel: (overrides.channel ?? 'IN_APP') as any,
status: (overrides.status ?? 'DELIVERED') === 'READ' ? 'DELIVERED' : (overrides.status ?? 'DELIVERED') as any,
deliveredAt: overrides.sentAt ? (overrides.sentAt as Date) : new Date(),
} as any,
})
return recipient
}
export async function createRenterNotification(
renterId: string,
overrides: Record<string, unknown> = {},
) {
return prisma.notification.create({
const companyId = (overrides.companyId as string | undefined)
?? (await prisma.company.findFirstOrThrow({ select: { id: true } })).id
const notificationEvent = await prisma.notificationEvent.create({
data: {
renterId,
type: 'BOOKING_CONFIRMED',
title: 'Booking confirmed',
body: 'Your booking is confirmed.',
channel: 'IN_APP',
status: 'PENDING',
...overrides,
companyId,
type: (overrides.type ?? 'BOOKING_CONFIRMED') as any,
title: (overrides.title ?? 'Booking confirmed') as string,
body: (overrides.body ?? 'Your booking is confirmed.') as string,
data: (overrides.data ?? {}) as any,
sourceType: (overrides.sourceType ?? 'test') as string,
sourceId: (overrides.sourceId ?? `source-${uid()}`) as string,
idempotencyKey: (overrides.idempotencyKey ?? `test-${uid()}`) as string,
} as any,
})
const recipient = await prisma.notificationRecipient.create({
data: {
notificationEventId: notificationEvent.id,
recipientType: 'RENTER',
renterId,
readAt: (overrides.readAt ?? null) as Date | null,
} as any,
})
await prisma.notificationDelivery.create({
data: {
notificationRecipientId: recipient.id,
channel: (overrides.channel ?? 'IN_APP') as any,
status: (overrides.status ?? 'DELIVERED') === 'READ' ? 'DELIVERED' : (overrides.status ?? 'DELIVERED') as any,
deliveredAt: overrides.sentAt ? (overrides.sentAt as Date) : new Date(),
} as any,
})
return recipient
}
export function signEmployeeToken(employeeId: string, _companyId: string, _role: string = 'OWNER') {
@@ -115,7 +115,7 @@ describe('Notifications API', () => {
it('updates renter notification preferences', async () => {
const payload = [
{ notificationType: 'BOOKING_CONFIRMED', channel: 'PUSH', enabled: true },
{ notificationType: 'BOOKING_CONFIRMED', channel: 'EMAIL', enabled: true },
]
const patchRes = await request(app)
@@ -136,7 +136,7 @@ describe('Notifications API', () => {
expect.objectContaining({
renterId,
notificationType: 'BOOKING_CONFIRMED',
channel: 'PUSH',
channel: 'EMAIL',
enabled: true,
}),
]),
@@ -82,7 +82,7 @@ describe('Operations API integration', () => {
expect(marked.status).toBe(200)
expect(marked.body.data).toEqual({ success: true })
const stored = await prisma.notification.findUniqueOrThrow({ where: { id: notification.id } })
const stored = await prisma.notificationRecipient.findUniqueOrThrow({ where: { id: notification.id } })
expect(stored.readAt).toBeInstanceOf(Date)
})