文件
hyb-oa/server/tests/unit/routes/reimbursement.submit-side-effects.test.js

166 行
5.5 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',
loadSummary: vi.fn().mockResolvedValue({
id: 'reimbursement-1',
form_number: 'BX-20260324-001',
total_amount: '1288.00',
owner: {
id: 'employee-1',
name: '王小明',
},
}),
resolveRecipients: vi.fn().mockResolvedValue(['manager-1']),
createAppNotifications: vi.fn().mockResolvedValue([
{
id: 'notification-1',
recipient_employee_id: 'manager-1',
},
]),
dispatchNotifications: vi.fn().mockResolvedValue({
deliveredChannels: [{ channelId: 'channel-1', status: 'success' }],
skippedReason: null,
}),
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 dispatchNotifications = vi.fn();
const sendEmailSafe = vi.fn();
const result = await finalizeReimbursementSubmitSideEffects(
createParams({
logger,
resolveRecipients: vi.fn().mockRejectedValue(new Error('resolve failed')),
createAppNotifications,
dispatchNotifications,
sendEmailSafe,
}),
);
expect(logger.error).toHaveBeenCalledTimes(1);
expect(createAppNotifications).not.toHaveBeenCalled();
expect(dispatchNotifications).not.toHaveBeenCalled();
expect(sendEmailSafe).not.toHaveBeenCalled();
expect(result).toEqual(
expect.objectContaining({
recipientEmployeeIds: [],
notification: expect.objectContaining({ skipped: true, reason: 'recipient_resolution_failed' }),
dispatch: expect.objectContaining({ 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 dispatchNotifications = vi.fn().mockRejectedValue(new Error('dispatch 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')),
dispatchNotifications,
sendEmailSafe,
}),
);
expect(logger.error).toHaveBeenCalledTimes(2);
expect(dispatchNotifications).toHaveBeenCalledTimes(1);
expect(sendEmailSafe).toHaveBeenCalledTimes(1);
expect(result).toEqual(
expect.objectContaining({
recipientEmployeeIds: ['manager-1'],
notification: expect.objectContaining({ skipped: true, reason: 'notification_failed' }),
dispatch: expect.objectContaining({ skippedReason: 'dispatch_failed' }),
email: expect.objectContaining({ skipped: true, sent: 0, failed: 0 }),
}),
);
});
it('builds structured reimbursement copy for app notification, IM and email', async () => {
const createAppNotifications = vi.fn().mockResolvedValue([
{
id: 'notification-1',
recipient_employee_id: 'manager-1',
},
]);
const dispatchNotifications = vi.fn().mockResolvedValue({
deliveredChannels: [{ channelId: 'channel-1', status: 'success' }],
skippedReason: null,
});
const sendEmailSafe = vi.fn().mockResolvedValue({
email: { skipped: true, sent: 0, failed: 0 },
});
await finalizeReimbursementSubmitSideEffects(
createParams({
createAppNotifications,
dispatchNotifications,
sendEmailSafe,
}),
);
expect(createAppNotifications).toHaveBeenCalledWith(
{},
expect.objectContaining({
title: '【待办】报销单 BX-20260324-001 待审批',
body: '申请人:王小明\n金额:1288.00\n单号:BX-20260324-001\n动作:请及时处理审批任务。',
}),
);
expect(dispatchNotifications).toHaveBeenCalledWith(
expect.objectContaining({
prisma: {},
title: '【待办】报销单 BX-20260324-001 待审批',
body: '申请人:王小明\n金额:1288.00\n单号:BX-20260324-001\n动作:请及时处理审批任务。',
notifications: [
expect.objectContaining({
id: 'notification-1',
}),
],
}),
);
expect(sendEmailSafe).toHaveBeenCalledWith(
expect.objectContaining({
subject: '【待办】报销单 BX-20260324-001 待审批',
body: '申请人:王小明\n金额:1288.00\n单号:BX-20260324-001\n动作:请及时处理审批任务。',
}),
);
});
});