fix: close documented platform polish gaps
This commit is contained in:
@@ -279,7 +279,7 @@ describe('ChannelsService', () => {
|
||||
};
|
||||
|
||||
await expect(service.createChannel({ ...channel, rateLimitPerSecond: 2001 })).rejects.toThrow('rateLimitPerSecond must be between 1 and 2000');
|
||||
await expect(service.createChannel({ ...channel, config: { extensionDigits: 3 } })).rejects.toThrow('extensionDigits must be one of 0, 2, 4, or 6');
|
||||
await expect(service.createChannel({ ...channel, config: { extensionDigits: 21 } })).rejects.toThrow('extensionDigits must be an integer between 0 and 20');
|
||||
});
|
||||
|
||||
it('updates CMPP channel configuration without requiring password changes', async () => {
|
||||
@@ -326,6 +326,17 @@ describe('ChannelsService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('persists an arbitrary integer extension digit count within the supported range', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new ChannelsService(prisma as never);
|
||||
|
||||
await service.updateChannel('channel-1', { config: { extensionDigits: 15 } });
|
||||
|
||||
expect(prisma.smsChannel.update).toHaveBeenCalledWith(expect.objectContaining({
|
||||
data: expect.objectContaining({ config: expect.objectContaining({ extensionDigits: 15 }) }),
|
||||
}));
|
||||
});
|
||||
|
||||
it('rejects invalid channel update ports', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new ChannelsService(prisma as never);
|
||||
|
||||
@@ -1417,8 +1417,8 @@ function normalizeExtensionDigits(value: unknown) {
|
||||
return 0;
|
||||
}
|
||||
const normalized = Number(value);
|
||||
if (![0, 2, 4, 6].includes(normalized)) {
|
||||
throw new BadRequestException('extensionDigits must be one of 0, 2, 4, or 6');
|
||||
if (!Number.isInteger(normalized) || normalized < 0 || normalized > 20) {
|
||||
throw new BadRequestException('extensionDigits must be an integer between 0 and 20');
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ export class DictionariesController {
|
||||
@Get('phone-segments')
|
||||
listPhoneSegments(
|
||||
@Query('keyword') keyword?: string,
|
||||
@Query('cursor') cursor?: string,
|
||||
@Query('page') page?: string,
|
||||
@Query('pageSize') pageSize?: string,
|
||||
) {
|
||||
return this.dictionaries.listPhoneSegments({ keyword, cursor, pageSize: Number(pageSize) || undefined });
|
||||
return this.dictionaries.listPhoneSegments({ keyword, page: Number(page) || undefined, pageSize: Number(pageSize) || undefined });
|
||||
}
|
||||
|
||||
@Post('phone-segments')
|
||||
|
||||
@@ -4,6 +4,7 @@ function createPrismaMock() {
|
||||
return {
|
||||
phoneSegment: {
|
||||
findMany: jest.fn(),
|
||||
count: jest.fn().mockResolvedValue(3),
|
||||
},
|
||||
phoneCarrierRule: {
|
||||
findMany: jest.fn().mockResolvedValue([]),
|
||||
@@ -34,7 +35,7 @@ function createPrismaMock() {
|
||||
}
|
||||
|
||||
describe('DictionariesService', () => {
|
||||
it('paginates phone segments without counting the full table', async () => {
|
||||
it('paginates phone segments with a real database count', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
prisma.phoneSegment.findMany.mockResolvedValue([
|
||||
{ id: 'segment-1', prefix: '1300001', carrier: '中国联通', province: '江苏', city: '常州' },
|
||||
@@ -43,25 +44,24 @@ describe('DictionariesService', () => {
|
||||
]);
|
||||
const service = new DictionariesService(prisma as never);
|
||||
|
||||
await expect(service.listPhoneSegments({ keyword: '中国联通', cursor: '1300000', pageSize: 2 })).resolves.toEqual({
|
||||
await expect(service.listPhoneSegments({ keyword: '中国联通', page: 2, pageSize: 2 })).resolves.toEqual({
|
||||
items: expect.arrayContaining([
|
||||
expect.objectContaining({ prefix: '1300001' }),
|
||||
expect.objectContaining({ prefix: '1300002' }),
|
||||
]),
|
||||
pageSize: 2,
|
||||
hasMore: true,
|
||||
nextCursor: '1300002',
|
||||
page: 2,
|
||||
total: 3,
|
||||
});
|
||||
expect(prisma.phoneSegment.findMany).toHaveBeenCalledWith({
|
||||
where: {
|
||||
AND: [
|
||||
{ prefix: { gt: '1300000' } },
|
||||
{ OR: expect.any(Array) },
|
||||
],
|
||||
OR: expect.any(Array),
|
||||
},
|
||||
orderBy: { prefix: 'asc' },
|
||||
take: 3,
|
||||
skip: 2,
|
||||
take: 2,
|
||||
});
|
||||
expect(prisma.phoneSegment.count).toHaveBeenCalledWith({ where: { OR: expect.any(Array) } });
|
||||
});
|
||||
|
||||
it('searches security control dictionaries with keyword and status filters', async () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface CreatePhoneSegmentDto {
|
||||
|
||||
export interface PhoneSegmentListQuery {
|
||||
keyword?: string;
|
||||
cursor?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
@@ -71,33 +71,22 @@ export class DictionariesService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async listPhoneSegments(query: PhoneSegmentListQuery = {}) {
|
||||
const pageSize = Math.min(100, Math.max(1, Number(query.pageSize ?? 20)));
|
||||
const page = Math.max(1, Number(query.page ?? 1));
|
||||
const pageSize = Math.min(100, Math.max(1, Number(query.pageSize ?? 25)));
|
||||
const keyword = query.keyword?.trim();
|
||||
const items = await this.prisma.phoneSegment.findMany({
|
||||
where: {
|
||||
AND: [
|
||||
query.cursor ? { prefix: { gt: query.cursor } } : {},
|
||||
keyword ? {
|
||||
OR: [
|
||||
{ prefix: { startsWith: keyword } },
|
||||
{ carrier: { contains: keyword } },
|
||||
{ province: { contains: keyword } },
|
||||
{ city: { contains: keyword } },
|
||||
],
|
||||
} : {},
|
||||
],
|
||||
},
|
||||
orderBy: { prefix: 'asc' },
|
||||
take: pageSize + 1,
|
||||
});
|
||||
const hasMore = items.length > pageSize;
|
||||
const pageItems = hasMore ? items.slice(0, pageSize) : items;
|
||||
return {
|
||||
items: pageItems,
|
||||
pageSize,
|
||||
hasMore,
|
||||
nextCursor: hasMore ? pageItems.at(-1)?.prefix ?? null : null,
|
||||
};
|
||||
const where = keyword ? {
|
||||
OR: [
|
||||
{ prefix: { startsWith: keyword } },
|
||||
{ carrier: { contains: keyword } },
|
||||
{ province: { contains: keyword } },
|
||||
{ city: { contains: keyword } },
|
||||
],
|
||||
} : undefined;
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.phoneSegment.findMany({ where, orderBy: { prefix: 'asc' }, skip: (page - 1) * pageSize, take: pageSize }),
|
||||
this.prisma.phoneSegment.count({ where }),
|
||||
]);
|
||||
return { items, total, page, pageSize };
|
||||
}
|
||||
|
||||
createPhoneSegment(data: CreatePhoneSegmentDto) {
|
||||
|
||||
@@ -248,6 +248,13 @@ describe('OperationsService', () => {
|
||||
taskCount: 3,
|
||||
uplinkCount: 1,
|
||||
pendingAuditCount: 5,
|
||||
pendingAudits: {
|
||||
enterpriseCertifications: 1,
|
||||
smsAudits: 2,
|
||||
signatures: 1,
|
||||
templates: 1,
|
||||
total: 5,
|
||||
},
|
||||
gatewayConnections: [{ status: 'connected', _count: { _all: 1 }, _sum: { currentConnections: 2, desiredConnections: 2 } }],
|
||||
downstreamDeliverySummary: expect.objectContaining({
|
||||
pending: 3,
|
||||
|
||||
@@ -153,7 +153,7 @@ export class OperationsService {
|
||||
billingAggregate,
|
||||
transactionAggregate,
|
||||
connectionGroups,
|
||||
pendingAuditCount,
|
||||
pendingAudits,
|
||||
tenantAccounts,
|
||||
recentTasks,
|
||||
recentRecharges,
|
||||
@@ -257,7 +257,8 @@ export class OperationsService {
|
||||
billing: billingAggregate,
|
||||
transactions: transactionAggregate,
|
||||
gatewayConnections: connectionGroups,
|
||||
pendingAuditCount,
|
||||
pendingAuditCount: pendingAudits.total,
|
||||
pendingAudits,
|
||||
downstreamDeliverySummary: {
|
||||
pending: downstreamPendingCount,
|
||||
failed: downstreamFailedCount,
|
||||
@@ -736,7 +737,13 @@ export class OperationsService {
|
||||
this.prisma.smsSignature.count({ where: { tenantId, auditStatus: 'pending' } }),
|
||||
this.prisma.enterpriseCertification.count({ where: { tenantId, status: 'pending' } }),
|
||||
this.prisma.smsSendTask.count({ where: { tenantId, status: 'pending_review' } }),
|
||||
]).then((counts) => counts.reduce((sum, value) => sum + value, 0));
|
||||
]).then(([templates, signatures, enterpriseCertifications, smsAudits]) => ({
|
||||
templates,
|
||||
signatures,
|
||||
enterpriseCertifications,
|
||||
smsAudits,
|
||||
total: templates + signatures + enterpriseCertifications + smsAudits,
|
||||
}));
|
||||
}
|
||||
|
||||
private gatewayDownstreamRecoveryStatusDelegate() {
|
||||
|
||||
Reference in New Issue
Block a user