test(api): cover notification broadcast reference errors

This commit is contained in:
2026-03-31 21:57:02 +02:00
parent 4111b7b661
commit 7585a76c11
2 changed files with 115 additions and 0 deletions
@@ -452,6 +452,66 @@ describe("notification.createBroadcast", () => {
expect(outerCreateNotification).not.toHaveBeenCalled();
});
it("maps broadcast source reference loss during recipient fan-out to not found errors", async () => {
resolveRecipientsMock.mockResolvedValue(["user_a", "user_b"]);
const txCreateBroadcast = vi.fn().mockResolvedValue({
id: "broadcast_tx_source_missing",
title: "Ops update",
createdAt: new Date("2026-03-30T10:00:00Z"),
});
const txUpdateBroadcast = vi.fn();
const txCreateNotification = vi.fn().mockRejectedValue(
Object.assign(new Error("Foreign key constraint failed"), {
code: "P2003",
meta: { field_name: "Notification_sourceId_fkey" },
}),
);
const tx = {
notificationBroadcast: {
create: txCreateBroadcast,
update: txUpdateBroadcast,
},
notification: {
create: txCreateNotification,
},
};
const outerCreateBroadcast = vi.fn();
const outerUpdateBroadcast = vi.fn();
const outerCreateNotification = vi.fn();
const transaction = vi.fn(async (callback: (db: typeof tx) => Promise<unknown>) => callback(tx));
const db = {
$transaction: transaction,
notificationBroadcast: {
create: outerCreateBroadcast,
update: outerUpdateBroadcast,
},
notification: {
create: outerCreateNotification,
},
};
const caller = createManagerCaller(db);
await expect(caller.createBroadcast({
title: "Ops update",
targetType: "all",
})).rejects.toMatchObject({
code: "NOT_FOUND",
message: "Notification broadcast not found",
});
expect(transaction).toHaveBeenCalledTimes(1);
expect(txCreateBroadcast).toHaveBeenCalledTimes(1);
expect(txCreateNotification).toHaveBeenCalledTimes(1);
expect(txUpdateBroadcast).not.toHaveBeenCalled();
expect(outerCreateBroadcast).not.toHaveBeenCalled();
expect(outerUpdateBroadcast).not.toHaveBeenCalled();
expect(outerCreateNotification).not.toHaveBeenCalled();
expect(emitNotificationCreated).not.toHaveBeenCalled();
expect(emitTaskAssigned).not.toHaveBeenCalled();
});
it("emits recipient SSE only after an immediate broadcast commits", async () => {
resolveRecipientsMock.mockResolvedValue(["user_a", "user_b"]);
@@ -688,6 +748,43 @@ describe("notification.createBroadcast", () => {
});
});
it("maps scheduled broadcast sender persistence failures to not found errors", async () => {
resolveRecipientsMock.mockResolvedValue(["user_a", "user_b"]);
const create = vi.fn().mockRejectedValue(
Object.assign(new Error("Foreign key constraint failed"), {
code: "P2003",
meta: { field_name: "NotificationBroadcast_senderId_fkey" },
}),
);
const db = {
notificationBroadcast: {
create,
update: vi.fn(),
},
notification: {
create: vi.fn(),
},
};
const caller = createManagerCaller(db);
await expect(caller.createBroadcast({
title: "Scheduled ops update",
targetType: "all",
scheduledAt: FUTURE_SCHEDULED_AT,
})).rejects.toMatchObject({
code: "NOT_FOUND",
message: "Sender user not found",
});
expect(create).toHaveBeenCalledTimes(1);
expect(db.notificationBroadcast.update).not.toHaveBeenCalled();
expect(db.notification.create).not.toHaveBeenCalled();
expect(emitNotificationCreated).not.toHaveBeenCalled();
expect(emitTaskAssigned).not.toHaveBeenCalled();
});
it("rolls back an immediate broadcast when the final broadcast update fails", async () => {
resolveRecipientsMock.mockResolvedValue(["user_a", "user_b"]);