fix: harden real backend admin workflows and ui
This commit is contained in:
@@ -27,6 +27,14 @@ function createPrismaMock() {
|
||||
smsChannel: {
|
||||
create: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'channel-copy', ...data })),
|
||||
},
|
||||
smsChannelGroup: {
|
||||
update: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'group-1', ...data })),
|
||||
findUnique: jest.fn().mockResolvedValue({ id: 'group-1', code: 'G-MOBILE', name: '移动组', carrier: 'mobile', items: [] }),
|
||||
},
|
||||
smsChannelGroupItem: {
|
||||
deleteMany: jest.fn(),
|
||||
createMany: jest.fn(),
|
||||
},
|
||||
signatureReportMaterial: {
|
||||
findMany: jest.fn().mockResolvedValue([{ signatureId: 'sig-1', fieldCode: 'license', fieldValue: '营业执照', fileObjectId: 'file-1' }]),
|
||||
createMany: jest.fn(),
|
||||
@@ -44,10 +52,12 @@ function createPrismaMock() {
|
||||
channelHealthMetric: { findMany: jest.fn() },
|
||||
smsChannelGroup: {
|
||||
findMany: jest.fn(),
|
||||
findUnique: jest.fn().mockResolvedValue({ id: 'group-1', code: 'G-MOBILE', name: '移动组', carrier: 'mobile', status: 'active' }),
|
||||
findUnique: jest.fn().mockResolvedValue({ id: 'group-1', code: 'G-MOBILE', name: '移动组', carrier: 'mobile', status: 'active', retryEnabled: true, retryTimeLimitHours: 72 }),
|
||||
create: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'group-1', ...data })),
|
||||
},
|
||||
smsChannelGroupItem: {
|
||||
deleteMany: jest.fn(),
|
||||
createMany: jest.fn(),
|
||||
findFirst: jest.fn().mockResolvedValue(null),
|
||||
create: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'group-item-1', ...data })),
|
||||
},
|
||||
@@ -144,6 +154,52 @@ describe('ChannelsService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('updates CMPP channel configuration without requiring password changes', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new ChannelsService(prisma as never);
|
||||
|
||||
await expect(service.updateChannel('channel-1', {
|
||||
name: '主通道-编辑',
|
||||
gatewayHost: '10.0.0.1',
|
||||
gatewayPort: 27890,
|
||||
carrier: 'all',
|
||||
sendRegion: '全国',
|
||||
account: 'sp-new',
|
||||
srcId: '10690001',
|
||||
unitPrice: 4,
|
||||
})).resolves.toEqual(expect.objectContaining({
|
||||
id: 'channel-1',
|
||||
name: '主通道-编辑',
|
||||
gatewayHost: '10.0.0.1',
|
||||
}));
|
||||
|
||||
expect(prisma.smsChannel.update).toHaveBeenCalledWith({
|
||||
where: { id: 'channel-1' },
|
||||
data: expect.objectContaining({
|
||||
name: '主通道-编辑',
|
||||
gatewayHost: '10.0.0.1',
|
||||
gatewayPort: 27890,
|
||||
carrier: 'all',
|
||||
passwordCipher: undefined,
|
||||
}),
|
||||
});
|
||||
expect(prisma.operationLog.create).toHaveBeenCalledWith({
|
||||
data: expect.objectContaining({
|
||||
action: 'sms_channel.update',
|
||||
resource: 'sms_channel',
|
||||
resourceId: 'channel-1',
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects invalid channel update ports', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new ChannelsService(prisma as never);
|
||||
|
||||
await expect(service.updateChannel('channel-1', { gatewayPort: 70000 })).rejects.toThrow('gatewayPort must be an integer between 1 and 65535');
|
||||
expect(prisma.smsChannel.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects direct single-channel route rules', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new ChannelsService(prisma as never);
|
||||
@@ -214,6 +270,36 @@ describe('ChannelsService', () => {
|
||||
.rejects.toThrow('同一通道组内全国通道优先级不能重复');
|
||||
});
|
||||
|
||||
it('updates channel groups and replaces items with backend validation', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new ChannelsService(prisma as never);
|
||||
prisma.smsChannel.findMany.mockResolvedValue([
|
||||
{ id: 'channel-sd', carrier: 'mobile', sendRegion: '山东' },
|
||||
{ id: 'channel-national', carrier: 'all', sendRegion: '全国' },
|
||||
]);
|
||||
|
||||
await service.updateGroup('group-1', {
|
||||
name: '移动组更新',
|
||||
carrier: 'mobile',
|
||||
retryEnabled: true,
|
||||
retryTimeLimitHours: 24,
|
||||
items: [
|
||||
{ channelId: 'channel-sd', carrier: 'mobile', province: '山东', priority: 10 },
|
||||
{ channelId: 'channel-national', carrier: 'mobile', priority: 1 },
|
||||
],
|
||||
});
|
||||
|
||||
expect(prisma.$transaction).toHaveBeenCalled();
|
||||
|
||||
await expect(service.updateGroup('group-1', {
|
||||
carrier: 'mobile',
|
||||
items: [
|
||||
{ channelId: 'channel-sd', carrier: 'mobile', priority: 1 },
|
||||
{ channelId: 'channel-national', carrier: 'mobile', priority: 1 },
|
||||
],
|
||||
})).rejects.toThrow('同一通道组内全国通道优先级不能重复');
|
||||
});
|
||||
|
||||
it('requires route rule carrier to match the channel group carrier', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new ChannelsService(prisma as never);
|
||||
@@ -265,11 +351,42 @@ describe('ChannelsService', () => {
|
||||
});
|
||||
expect(prisma.channelSignatureReportTask.update).toHaveBeenCalledWith({
|
||||
where: { id: 'report-task-1' },
|
||||
data: { status: 'rejected', reason: 'one rejected' },
|
||||
data: { status: 'partial', reason: 'one rejected' },
|
||||
});
|
||||
expect(prisma.smsSignature.update).toHaveBeenCalledWith({
|
||||
where: { id: 'sig-1' },
|
||||
data: { reportStatus: 'rejected' },
|
||||
data: { reportStatus: 'partial' },
|
||||
});
|
||||
});
|
||||
|
||||
it('parses text receipt imports and derives report task status', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new ChannelsService(prisma as never);
|
||||
|
||||
await service.importReportReceipt('report-task-1', {
|
||||
fileObjectId: 'file-1',
|
||||
fileName: 'receipt.csv',
|
||||
fileContent: 'phone,status\n13800138000,success\n13900139000,failed\n13700137000,通过',
|
||||
reason: 'carrier receipt',
|
||||
});
|
||||
|
||||
expect(prisma.reportReceiptImport.create).toHaveBeenCalledWith({
|
||||
data: expect.objectContaining({
|
||||
fileObjectId: 'file-1',
|
||||
fileName: 'receipt.csv',
|
||||
rowCount: 3,
|
||||
successCount: 2,
|
||||
failedCount: 1,
|
||||
result: expect.objectContaining({ hasHeader: true, rows: expect.any(Array) }),
|
||||
}),
|
||||
});
|
||||
expect(prisma.channelSignatureReportTask.update).toHaveBeenCalledWith({
|
||||
where: { id: 'report-task-1' },
|
||||
data: { status: 'partial', reason: 'carrier receipt' },
|
||||
});
|
||||
expect(prisma.smsSignature.update).toHaveBeenCalledWith({
|
||||
where: { id: 'sig-1' },
|
||||
data: { reportStatus: 'partial' },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user