103 行
3.3 KiB
JavaScript
103 行
3.3 KiB
JavaScript
const reimbursementRoutes = require('../../../src/routes/reimbursements');
|
|
|
|
describe('reimbursement submit side effects helper', () => {
|
|
const { finalizeReimbursementSubmitSideEffects } = reimbursementRoutes.__testables;
|
|
|
|
function createParams(overrides = {}) {
|
|
return {
|
|
prisma: {},
|
|
config: {
|
|
imNotifierEnabled: true,
|
|
emailEnabled: false,
|
|
},
|
|
logger: {
|
|
error: vi.fn(),
|
|
warn: vi.fn(),
|
|
},
|
|
requestId: 'req-1',
|
|
reimbursementId: 'reimbursement-1',
|
|
formNumber: 'BX-20260324-001',
|
|
workflowTaskId: 'task-1',
|
|
assigneeId: 'manager-1',
|
|
assigneeRole: null,
|
|
actorId: 'employee-1',
|
|
resolveRecipients: vi.fn().mockResolvedValue(['manager-1']),
|
|
createAppNotifications: vi.fn().mockResolvedValue({ count: 1 }),
|
|
enqueueIm: vi.fn().mockResolvedValue({
|
|
queued: 1,
|
|
duplicated: 0,
|
|
matchedChannels: [{ id: 'channel-1' }],
|
|
}),
|
|
sendEmailSafe: vi.fn().mockResolvedValue({
|
|
email: { skipped: true, sent: 0, failed: 0 },
|
|
}),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
it('does not throw when resolving recipients fails', async () => {
|
|
const logger = {
|
|
error: vi.fn(),
|
|
warn: vi.fn(),
|
|
};
|
|
const createAppNotifications = vi.fn();
|
|
const enqueueIm = vi.fn();
|
|
const sendEmailSafe = vi.fn();
|
|
|
|
const result = await finalizeReimbursementSubmitSideEffects(
|
|
createParams({
|
|
logger,
|
|
resolveRecipients: vi.fn().mockRejectedValue(new Error('resolve failed')),
|
|
createAppNotifications,
|
|
enqueueIm,
|
|
sendEmailSafe,
|
|
}),
|
|
);
|
|
|
|
expect(logger.error).toHaveBeenCalledTimes(1);
|
|
expect(createAppNotifications).not.toHaveBeenCalled();
|
|
expect(enqueueIm).not.toHaveBeenCalled();
|
|
expect(sendEmailSafe).not.toHaveBeenCalled();
|
|
expect(result).toEqual(
|
|
expect.objectContaining({
|
|
recipientEmployeeIds: [],
|
|
notification: expect.objectContaining({ skipped: true, reason: 'recipient_resolution_failed' }),
|
|
im: expect.objectContaining({ queued: 0, skippedReason: 'recipient_resolution_failed' }),
|
|
email: expect.objectContaining({ skipped: true, reason: 'recipient_resolution_failed' }),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('logs notification and IM failures and still continues to email', async () => {
|
|
const logger = {
|
|
error: vi.fn(),
|
|
warn: vi.fn(),
|
|
};
|
|
const enqueueIm = vi.fn().mockRejectedValue(new Error('im failed'));
|
|
const sendEmailSafe = vi.fn().mockResolvedValue({
|
|
email: { skipped: true, sent: 0, failed: 0 },
|
|
});
|
|
|
|
const result = await finalizeReimbursementSubmitSideEffects(
|
|
createParams({
|
|
logger,
|
|
createAppNotifications: vi.fn().mockRejectedValue(new Error('notifications failed')),
|
|
enqueueIm,
|
|
sendEmailSafe,
|
|
}),
|
|
);
|
|
|
|
expect(logger.error).toHaveBeenCalledTimes(2);
|
|
expect(enqueueIm).toHaveBeenCalledTimes(1);
|
|
expect(sendEmailSafe).toHaveBeenCalledTimes(1);
|
|
expect(result).toEqual(
|
|
expect.objectContaining({
|
|
recipientEmployeeIds: ['manager-1'],
|
|
notification: expect.objectContaining({ skipped: true, reason: 'notification_failed' }),
|
|
im: expect.objectContaining({ queued: 0, skippedReason: 'enqueue_im_failed' }),
|
|
email: expect.objectContaining({ skipped: true, sent: 0, failed: 0 }),
|
|
}),
|
|
);
|
|
});
|
|
});
|