feat: restore credit limits and harden SMS sending
This commit is contained in:
@@ -1654,18 +1654,49 @@ describe('SendChainService', () => {
|
||||
await expect(service.batchRequeueDownstreamDeliveries([])).rejects.toThrow('请选择至少一条下游投递记录');
|
||||
});
|
||||
|
||||
it('marks 72 hour unknown receipts as timeout', async () => {
|
||||
const { service, prisma } = createService();
|
||||
it('marks submitted or unknown messages without a final receipt for 72 hours as timeout and refunds them', async () => {
|
||||
const { service, prisma, billing } = createService();
|
||||
prisma.smsMessageRecord.findMany.mockResolvedValue([
|
||||
{ id: 'record-1', batchTaskId: 'task-1' },
|
||||
{ id: 'record-2', batchTaskId: 'task-1' },
|
||||
{ id: 'record-1', tenantId: 'tenant-1', batchTaskId: 'task-1', messageId: 'MSG-1', amountCents: 3, billingUnits: 1 },
|
||||
{ id: 'record-2', tenantId: 'tenant-1', batchTaskId: 'task-1', messageId: 'MSG-2', amountCents: 3, billingUnits: 1 },
|
||||
]);
|
||||
prisma.smsBillingRecord.findFirst
|
||||
.mockResolvedValueOnce(null).mockResolvedValueOnce({ id: 'bill-1', billingStatus: 'charged' })
|
||||
.mockResolvedValueOnce(null).mockResolvedValueOnce({ id: 'bill-2', billingStatus: 'charged' });
|
||||
|
||||
await expect(service.markUnknownTimeout({ olderThanHours: 72 })).resolves.toEqual({ timeout: 2 });
|
||||
expect(prisma.smsMessageRecord.findMany).toHaveBeenCalledWith({
|
||||
where: {
|
||||
tenantId: { not: null },
|
||||
status: { in: ['submitted', 'unknown'] },
|
||||
submittedAt: { lte: expect.any(Date) },
|
||||
},
|
||||
select: { id: true, tenantId: true, batchTaskId: true, messageId: true, amountCents: true, billingUnits: true },
|
||||
take: 10000,
|
||||
});
|
||||
expect(prisma.smsMessageRecord.updateMany).toHaveBeenCalledWith({
|
||||
where: { id: { in: ['record-1', 'record-2'] } },
|
||||
where: { id: 'record-1', status: { in: ['submitted', 'unknown'] } },
|
||||
data: expect.objectContaining({ status: 'timeout', errorMessage: '72小时未收到明确回执,自动转超时' }),
|
||||
});
|
||||
expect(billing.refund).toHaveBeenCalledTimes(2);
|
||||
expect(prisma.smsBatchTask.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('starts the automatic receipt-timeout scan after application startup', async () => {
|
||||
jest.useFakeTimers();
|
||||
const previousEnabled = process.env.SMS_RECEIPT_TIMEOUT_SCAN_ENABLED;
|
||||
const { service } = createService();
|
||||
const scan = jest.spyOn(service, 'markUnknownTimeout').mockResolvedValue({ timeout: 0 });
|
||||
try {
|
||||
process.env.SMS_RECEIPT_TIMEOUT_SCAN_ENABLED = 'true';
|
||||
service.onModuleInit();
|
||||
await jest.advanceTimersByTimeAsync(60_000);
|
||||
expect(scan).toHaveBeenCalledWith({});
|
||||
await service.onModuleDestroy();
|
||||
} finally {
|
||||
if (previousEnabled === undefined) delete process.env.SMS_RECEIPT_TIMEOUT_SCAN_ENABLED;
|
||||
else process.env.SMS_RECEIPT_TIMEOUT_SCAN_ENABLED = previousEnabled;
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user