feat: add application interface controls

This commit is contained in:
hectorzhao
2026-07-09 18:32:26 +08:00
parent 767f0ca9aa
commit ff0756ff9c
14 changed files with 260 additions and 26 deletions
+70 -1
View File
@@ -63,7 +63,7 @@ function createPrismaMock() {
findUnique: jest.fn().mockResolvedValue({ id: 'tenant-1', status: 'active', certificationStatus: 'approved' }),
},
smsApplication: {
findUnique: jest.fn().mockResolvedValue({ id: 'app-1', tenantId: 'tenant-1', cmppAccount: '100001', status: 'active', customerUnitPrice: 3, queuePriority: 'normal' }),
findUnique: jest.fn().mockResolvedValue({ id: 'app-1', tenantId: 'tenant-1', cmppAccount: '100001', status: 'active', interfaceEnabled: true, customerUnitPrice: 3, queuePriority: 'normal' }),
findMany: jest.fn().mockResolvedValue([{ id: 'app-1', tenantId: 'tenant-1', name: '应用A' }]),
findFirst: jest.fn().mockResolvedValue({
id: 'app-1',
@@ -71,6 +71,7 @@ function createPrismaMock() {
cmppAccount: '100001',
secretHash: 'secret-hash',
status: 'active',
interfaceEnabled: true,
queuePriority: 'normal',
ipAllowlist: [{ ipCidr: '127.0.0.1/32' }],
tenant: { id: 'tenant-1', status: 'active', certificationStatus: 'approved' },
@@ -414,6 +415,74 @@ describe('SendChainService', () => {
).rejects.toThrow('企业认证未通过,不能发送短信');
});
it('blocks sending when application interface is disabled', async () => {
const { service, prisma } = createService();
prisma.smsApplication.findUnique.mockResolvedValue({
id: 'app-1',
tenantId: 'tenant-1',
status: 'active',
interfaceEnabled: false,
customerUnitPrice: 3,
queuePriority: 'normal',
});
await expect(
service.createBatchTask({
tenantId: 'tenant-1',
applicationId: 'app-1',
templateId: 'tpl-1',
content: 'hello',
phones: ['13800000001'],
}),
).rejects.toThrow('短信应用接口未开通,不能发送短信');
expect(prisma.smsBatchTask.create).not.toHaveBeenCalled();
});
it('rejects Gateway authentication when application interface is disabled', async () => {
const { service, prisma } = createService();
prisma.smsApplication.findFirst.mockResolvedValue({
id: 'app-1',
tenantId: 'tenant-1',
cmppAccount: '100001',
secretHash: 'secret-hash',
status: 'active',
interfaceEnabled: false,
ipAllowlist: [{ ipCidr: '127.0.0.1/32' }],
tenant: { id: 'tenant-1', status: 'active', certificationStatus: 'approved' },
});
await expect(service.authenticateInboundApplication({
account: '100001',
password: 'secret-hash',
remoteIp: '127.0.0.1',
})).rejects.toThrow('CMPP interface is disabled for this application');
});
it('rejects Gateway submit when application interface is disabled', async () => {
const { service, prisma } = createService();
prisma.smsApplication.findFirst.mockResolvedValue({
id: 'app-1',
tenantId: 'tenant-1',
cmppAccount: '100001',
secretHash: 'secret-hash',
status: 'active',
interfaceEnabled: false,
queuePriority: 'normal',
ipAllowlist: [{ ipCidr: '127.0.0.1/32' }],
tenant: { id: 'tenant-1', status: 'active', certificationStatus: 'approved' },
});
await expect(service.submitInboundMessage({
account: '100001',
phoneNumber: '13800000001',
content: 'hello',
remoteIp: '127.0.0.1',
})).rejects.toThrow('CMPP interface is disabled for this application');
expect(prisma.smsBatchTask.create).not.toHaveBeenCalled();
});
it('previews imported phone files with duplicate, invalid, blacklist, and variable errors', async () => {
const { service, prisma } = createService();
prisma.enterpriseBlacklist.findMany.mockResolvedValue([{ phoneNumber: '13800000003' }]);