release: prepare RealeseV2.3

This commit is contained in:
hectorzhao
2026-08-06 10:48:36 +08:00
parent 57b58f1c40
commit 8ad8e61793
37 changed files with 997 additions and 64 deletions
@@ -96,6 +96,26 @@ function createPrismaMock() {
groupBy: jest.fn().mockResolvedValue([{ status: 'pending', _count: { _all: 1 } }]),
findFirst: jest.fn().mockResolvedValue({ createdAt: new Date('2026-07-08T12:00:00.000Z') }),
},
smsReceiptAnomaly: {
findMany: jest.fn().mockResolvedValue([{
id: 'receipt-anomaly-1',
anomalyKey: 'aggregate-receipt-conflict:record-1:SUB-1',
anomalyType: 'aggregate_success_then_failure',
status: 'pending',
occurrenceCount: 1,
firstOccurredAt: new Date('2026-08-06T01:00:00.000Z'),
lastOccurredAt: new Date('2026-08-06T01:00:00.000Z'),
tenant: { name: '租户A' },
application: { name: '应用A' },
channel: { name: '通道A' },
messageRecord: { messageId: 'MSG-1', phoneNumber: '13800000001', status: 'delivered' },
submitRecord: { submitId: 'SUB-1', submitStatus: 'accepted' },
receiptRecord: { gatewayMessageId: 'GW-1', receiptStatus: 'undelivered', rawStatus: 'UNDELIV' },
}]),
count: jest.fn().mockResolvedValue(1),
groupBy: jest.fn().mockResolvedValue([{ status: 'pending', _count: { _all: 1 } }]),
findFirst: jest.fn().mockResolvedValue({ firstOccurredAt: new Date('2026-08-06T01:00:00.000Z') }),
},
gatewayDownstreamRecoveryStatus: {
findMany: jest.fn().mockResolvedValue([{
id: 'recover-1',
@@ -612,6 +632,27 @@ describe('OperationsService', () => {
expect(prisma.$queryRaw).toHaveBeenCalledTimes(4);
});
it('returns pending audit counts without running the full dashboard aggregation', async () => {
const prisma = createPrismaMock();
const service = new OperationsService(prisma as never);
await expect(service.pendingAudits('tenant-1')).resolves.toEqual({
enterpriseCertifications: 1,
smsAudits: 2,
templates: 1,
signatures: 1,
drainageInfos: 0,
total: 5,
});
expect(prisma.smsTemplate.count).toHaveBeenCalledWith({ where: { tenantId: 'tenant-1', auditStatus: 'pending' } });
expect(prisma.smsSignature.count).toHaveBeenCalledWith({ where: { tenantId: 'tenant-1', auditStatus: 'pending' } });
expect(prisma.smsDrainageInfo.count).toHaveBeenCalledWith({ where: { tenantId: 'tenant-1', auditStatus: 'pending' } });
expect(prisma.enterpriseCertification.count).toHaveBeenCalledWith({ where: { tenantId: 'tenant-1', status: 'pending' } });
expect(prisma.smsSendTask.count).toHaveBeenCalledWith({ where: { tenantId: 'tenant-1', status: 'pending_review' } });
expect(prisma.smsMessageRecord.groupBy).not.toHaveBeenCalled();
expect(prisma.$queryRaw).not.toHaveBeenCalled();
});
it('rejects invalid send quality dates', async () => {
const service = new OperationsService(createPrismaMock() as never);
await expect(service.sendQuality('2026-02-31')).rejects.toThrow('统计日期无效');
@@ -884,6 +925,49 @@ describe('OperationsService', () => {
});
});
it('returns paginated receipt anomalies with status summary', async () => {
const prisma = createPrismaMock();
const service = new OperationsService(prisma as never);
await expect(service.listReceiptAnomalies({
tenantId: 'tenant-1',
channelId: 'channel-1',
status: 'pending',
anomalyType: 'aggregate_success_then_failure',
keyword: 'MSG-1',
page: 1,
pageSize: 10,
})).resolves.toEqual(expect.objectContaining({
total: 1,
page: 1,
pageSize: 10,
summary: {
pending: 1,
resolved: 0,
ignored: 0,
oldestPendingAt: new Date('2026-08-06T01:00:00.000Z'),
},
}));
expect(prisma.smsReceiptAnomaly.findMany).toHaveBeenCalledWith(expect.objectContaining({
where: expect.objectContaining({
tenantId: 'tenant-1',
channelId: 'channel-1',
status: 'pending',
anomalyType: 'aggregate_success_then_failure',
}),
orderBy: [{ lastOccurredAt: 'desc' }, { id: 'desc' }],
take: 10,
include: {
tenant: { select: { id: true, name: true } },
application: { select: { id: true, name: true } },
channel: { select: { id: true, code: true, name: true, status: true } },
messageRecord: { select: { messageId: true, phoneNumber: true, status: true } },
submitRecord: { select: { submitId: true, submitStatus: true } },
receiptRecord: { select: { gatewayMessageId: true, receiptStatus: true, rawStatus: true, deliveredAt: true } },
},
}));
});
it('returns paginated downstream deliveries', async () => {
const prisma = createPrismaMock();
const service = new OperationsService(prisma as never);