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 subscriptionRepository = { find: jest.fn(), save: jest.fn((v) => v) };
|
||||||
const teamRepository = { findOne: jest.fn() };
|
const teamRepository = { findOne: jest.fn() };
|
||||||
const mailService = { cashboxExport: jest.fn() };
|
const mailService = { cashboxExport: jest.fn() };
|
||||||
const logger = { info: jest.fn() };
|
const logger = { info: jest.fn(), error: jest.fn() };
|
||||||
let scheduler: CashboxExportScheduler;
|
let scheduler: CashboxExportScheduler;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -118,4 +118,29 @@ describe('CashboxExportScheduler', () => {
|
|||||||
|
|
||||||
expect(mailService.cashboxExport).toHaveBeenCalledTimes(2);
|
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) {
|
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_update'
|
||||||
| 'recurring_transaction_delete'
|
| 'recurring_transaction_delete'
|
||||||
| 'recurring_transaction_run'
|
| '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';
|
export type LOGLEVEL = 'FATAL' | 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE';
|
||||||
|
|||||||
Reference in New Issue
Block a user