fix: add error handling for CashboxExportScheduler subscription processing
- Wrap runOne(subscription) in try/catch to ensure one subscription failure doesn't block remaining subscriptions - Log failed subscriptions with new 'cashbox_export_subscription_run_fail' event - Add new LOGEVENT type for subscription run failures - Add test to verify second subscription processes even when first fails (continues processing independently) - All 7 tests passing: 6 original + 1 new failure handling test Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ describe('CashboxExportScheduler', () => {
|
||||
const subscriptionRepository = { find: jest.fn(), save: jest.fn((v) => v) };
|
||||
const teamRepository = { findOne: jest.fn() };
|
||||
const mailService = { cashboxExport: jest.fn() };
|
||||
const logger = { info: jest.fn() };
|
||||
const logger = { info: jest.fn(), error: jest.fn() };
|
||||
let scheduler: CashboxExportScheduler;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -118,4 +118,29 @@ describe('CashboxExportScheduler', () => {
|
||||
|
||||
expect(mailService.cashboxExport).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('continues processing when one subscription fails', async () => {
|
||||
subscriptionRepository.find.mockResolvedValue([
|
||||
{ id: 1, team: { id: 5 }, recipients: ['a@example.com'], interval: RecurringTransactionIntervalEnum.monthly, nextRunDate: '2026-09-01T00:00:00.000Z', active: true },
|
||||
{ id: 2, team: { id: 6 }, recipients: ['b@example.com'], interval: RecurringTransactionIntervalEnum.monthly, nextRunDate: '2026-09-01T00:00:00.000Z', active: true },
|
||||
]);
|
||||
teamRepository.findOne.mockResolvedValue({
|
||||
id: 5,
|
||||
name: 'Team A',
|
||||
alias: 'team-a',
|
||||
transactions: [],
|
||||
players: [],
|
||||
});
|
||||
mailService.cashboxExport.mockRejectedValueOnce(new Error('smtp down'));
|
||||
|
||||
await scheduler.runDueSubscriptions();
|
||||
|
||||
expect(mailService.cashboxExport).toHaveBeenCalledTimes(2);
|
||||
expect(subscriptionRepository.save).toHaveBeenCalledTimes(1);
|
||||
expect(logger.error).toHaveBeenCalledWith({
|
||||
event: 'cashbox_export_subscription_run_fail',
|
||||
details: expect.stringContaining('subscriptionId=1 teamId=5'),
|
||||
userId: -1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,7 +35,16 @@ export class CashboxExportScheduler {
|
||||
});
|
||||
|
||||
for (const subscription of due) {
|
||||
await this.runOne(subscription);
|
||||
try {
|
||||
await this.runOne(subscription);
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
await this.logger.error({
|
||||
event: 'cashbox_export_subscription_run_fail',
|
||||
details: `recurring subscription failed: subscriptionId=${subscription.id} teamId=${subscription.team.id}: ${errorMessage}`,
|
||||
userId: -1,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ export type LOGEVENT =
|
||||
| 'recurring_transaction_update'
|
||||
| 'recurring_transaction_delete'
|
||||
| 'recurring_transaction_run'
|
||||
| 'cashbox_export_subscription_run';
|
||||
| 'cashbox_export_subscription_run'
|
||||
| 'cashbox_export_subscription_run_fail';
|
||||
|
||||
export type LOGLEVEL = 'FATAL' | 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE';
|
||||
|
||||
Reference in New Issue
Block a user