fix: simplify balance billing and govern operation logs
This commit is contained in:
@@ -249,11 +249,13 @@ function createPrismaMock() {
|
||||
updateMany: jest.fn().mockResolvedValue({ count: 1 }),
|
||||
},
|
||||
gatewayDownstreamRecoveryStatus: {
|
||||
findUnique: jest.fn().mockResolvedValue(null),
|
||||
upsert: jest.fn().mockResolvedValue({
|
||||
id: 'recover-1',
|
||||
tenantId: 'tenant-1',
|
||||
applicationId: 'app-1',
|
||||
account: '100001',
|
||||
gatewayInstanceId: 'gateway-a',
|
||||
state: 'waiting_connection',
|
||||
lockOwner: 'gateway-a',
|
||||
lockExpiresAt: new Date('2026-07-08T12:00:30.000Z'),
|
||||
@@ -341,7 +343,7 @@ describe('SendChainService', () => {
|
||||
expect.objectContaining({ phoneNumber: '13800000002', status: 'queued', billingUnits: 1, amountCents: 3, queuePriority: 'normal' }),
|
||||
]),
|
||||
});
|
||||
expect(billing.freeze).toHaveBeenCalledWith(expect.objectContaining({ amountCents: 6, smsUnits: 2, relatedId: 'task-1' }));
|
||||
expect(billing.freeze).toHaveBeenCalledWith(expect.objectContaining({ amountCents: 6, relatedId: 'task-1' }));
|
||||
expect(service.enqueueBatchTask).toHaveBeenCalledWith('task-1');
|
||||
});
|
||||
|
||||
@@ -376,7 +378,7 @@ describe('SendChainService', () => {
|
||||
dispatched: 1,
|
||||
results: [{ taskId: 'task-1', status: 'queued', enqueued: 1 }],
|
||||
});
|
||||
expect(billing.freeze).toHaveBeenCalledWith(expect.objectContaining({ amountCents: 3, smsUnits: 1, relatedId: 'task-1' }));
|
||||
expect(billing.freeze).toHaveBeenCalledWith(expect.objectContaining({ amountCents: 3, relatedId: 'task-1' }));
|
||||
expect(prisma.smsMessageRecord.updateMany).toHaveBeenCalledWith({
|
||||
where: { batchTaskId: 'task-1', status: 'scheduled' },
|
||||
data: { status: 'queued' },
|
||||
@@ -854,8 +856,8 @@ describe('SendChainService', () => {
|
||||
where: { id: 'record-1' },
|
||||
data: expect.objectContaining({ gatewayMessageId: 'GW-1', status: 'submitted', submitStatus: 'accepted' }),
|
||||
});
|
||||
expect(billing.release).toHaveBeenCalledWith(expect.objectContaining({ amountCents: 3, smsUnits: 1, relatedId: 'task-1' }));
|
||||
expect(billing.charge).toHaveBeenCalledWith(expect.objectContaining({ amountCents: 3, smsUnits: 1, relatedId: 'MSG-1' }));
|
||||
expect(billing.release).toHaveBeenCalledWith(expect.objectContaining({ amountCents: 3, relatedId: 'task-1' }));
|
||||
expect(billing.charge).toHaveBeenCalledWith(expect.objectContaining({ amountCents: 3, relatedId: 'MSG-1' }));
|
||||
expect(prisma.smsBillingRecord.create).toHaveBeenCalledWith({
|
||||
data: expect.objectContaining({ messageId: 'MSG-1', amountCents: 3, billingStatus: 'charged', transactionId: 'tx-charge' }),
|
||||
});
|
||||
@@ -1412,12 +1414,39 @@ describe('SendChainService', () => {
|
||||
}));
|
||||
expect(prisma.operationLog.create).toHaveBeenCalledWith(expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
action: 'gateway.downstream_recovery_status_sync',
|
||||
action: 'gateway.downstream_recovery_status_changed',
|
||||
resource: 'gateway_downstream_recovery_status',
|
||||
}),
|
||||
}));
|
||||
});
|
||||
|
||||
it('does not append recovery audit logs when only periodic timestamps change', async () => {
|
||||
const { service, prisma } = createService();
|
||||
prisma.gatewayDownstreamRecoveryStatus.findUnique.mockResolvedValue({
|
||||
state: 'waiting_connection',
|
||||
gatewayInstanceId: 'gateway-a',
|
||||
lockOwner: 'gateway-a',
|
||||
failureCategory: 'client_disconnected',
|
||||
lastError: 'downstream client is not connected',
|
||||
lastSkipReason: null,
|
||||
});
|
||||
|
||||
await service.recordGatewayDownstreamRecoveryStatus({
|
||||
account: '100001',
|
||||
gatewayInstanceId: 'gateway-a',
|
||||
state: 'waiting_connection',
|
||||
lastAttemptAt: '2026-07-08T12:01:00.000Z',
|
||||
nextRetryAt: '2026-07-08T12:11:00.000Z',
|
||||
attemptCount: 3,
|
||||
lockOwner: 'gateway-a',
|
||||
lockExpiresAt: '2026-07-08T12:01:30.000Z',
|
||||
lastError: 'downstream client is not connected',
|
||||
});
|
||||
|
||||
expect(prisma.gatewayDownstreamRecoveryStatus.upsert).toHaveBeenCalled();
|
||||
expect(prisma.operationLog.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('marks downstream delivery as failed after reaching retry limit', async () => {
|
||||
const { service, prisma } = createService();
|
||||
const previous = process.env.CMPP_DOWNSTREAM_MAX_RETRIES;
|
||||
|
||||
Reference in New Issue
Block a user