feat: add number library routing and cdr location support
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { Prisma, type PrismaClient } from '@prisma/client';
|
||||
import { CONFIG_ACTIVE_VERSION_KEY } from '@lisglosips/redis';
|
||||
import { publishPendingConfig } from './publisher.js';
|
||||
|
||||
class MemoryRedisMulti {
|
||||
constructor(private readonly writes: Array<{ command: string; key: string; value?: string }>) {}
|
||||
|
||||
set(key: string, value: string) {
|
||||
this.writes.push({ command: 'set', key, value });
|
||||
return this;
|
||||
}
|
||||
|
||||
rpush(key: string, value: string) {
|
||||
this.writes.push({ command: 'rpush', key, value });
|
||||
return this;
|
||||
}
|
||||
|
||||
sadd(key: string, value: string) {
|
||||
this.writes.push({ command: 'sadd', key, value });
|
||||
return this;
|
||||
}
|
||||
|
||||
async exec() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class MemoryRedis {
|
||||
writes: Array<{ command: string; key: string; value?: string }> = [];
|
||||
|
||||
async get(key: string) {
|
||||
return key === CONFIG_ACTIVE_VERSION_KEY ? null : null;
|
||||
}
|
||||
|
||||
multi() {
|
||||
return new MemoryRedisMulti(this.writes);
|
||||
}
|
||||
}
|
||||
|
||||
function prismaFixture(): PrismaClient {
|
||||
const now = new Date('2026-06-24T03:20:00.000Z');
|
||||
return {
|
||||
outboxEvent: {
|
||||
findMany: async () => [
|
||||
{
|
||||
id: 'out_number_library',
|
||||
aggregateType: 'number_library_config',
|
||||
status: 'PENDING',
|
||||
availableAt: now,
|
||||
createdAt: now
|
||||
}
|
||||
],
|
||||
updateMany: async () => ({ count: 1 })
|
||||
},
|
||||
$transaction: async (operations: Array<Promise<unknown>>) => Promise.all(operations),
|
||||
customer: {
|
||||
findMany: async () => [
|
||||
{ id: 'cus_1', status: 'ENABLED', balance: new Prisma.Decimal('10'), creditLimit: new Prisma.Decimal('0'), minBalance: new Prisma.Decimal('0') }
|
||||
]
|
||||
},
|
||||
customerGateway: {
|
||||
findMany: async () => [
|
||||
{ id: 'cgw_1', customerId: 'cus_1', authMode: 'IP', sourceIp: '100.93.185.30', sipUsername: null, sipDomain: null, sipHa1: null, status: 'ENABLED' }
|
||||
]
|
||||
},
|
||||
customerGatewayPolicy: {
|
||||
findMany: async () => [
|
||||
{
|
||||
id: 'cgp_1',
|
||||
customerId: 'cus_1',
|
||||
gatewayId: 'cgw_1',
|
||||
lineGroupId: 'lg_1',
|
||||
priority: 1,
|
||||
callerMode: 'ANY',
|
||||
callerValue: null,
|
||||
calleeMode: 'ANY',
|
||||
calleeValue: null,
|
||||
status: 'ENABLED'
|
||||
}
|
||||
]
|
||||
},
|
||||
vendorGateway: {
|
||||
findMany: async () => [
|
||||
{
|
||||
id: 'vgw_1',
|
||||
vendorId: 'ven_1',
|
||||
authMode: 'IP',
|
||||
host: '100.93.185.30',
|
||||
port: 50620,
|
||||
transport: 'udp',
|
||||
sipUsername: null,
|
||||
sipHa1: null,
|
||||
cpsLimit: 10,
|
||||
concurrencyLimit: 30,
|
||||
billingCycleSec: 6,
|
||||
cycleRate: new Prisma.Decimal('0.012'),
|
||||
status: 'ENABLED',
|
||||
forbiddenPeriods: [],
|
||||
codecs: [],
|
||||
prefixRules: [],
|
||||
blockedRegions: [{ regionScope: 'CITY', provinceCode: '340000', provinceName: '安徽省', cityCode: '340100', cityName: '合肥市' }]
|
||||
}
|
||||
]
|
||||
},
|
||||
landingLineGroup: {
|
||||
findMany: async () => [
|
||||
{ id: 'lg_1', name: '默认线路组', status: 'ENABLED', items: [{ id: 'lgi_1', vendorGatewayId: 'vgw_1', priority: 1, weight: 1, concurrencyCap: 30, status: 'ENABLED' }] }
|
||||
]
|
||||
},
|
||||
geoCity: {
|
||||
findMany: async () => [
|
||||
{ code: '340100', provinceCode: '340000', provinceName: '安徽省', cityCode: '340100', cityName: '合肥市', cityLevel: 'PREFECTURE', status: 'ENABLED' }
|
||||
]
|
||||
},
|
||||
phoneNumberSegment: {
|
||||
findMany: async () => [
|
||||
{ segment7: '1380013', cityCode: '340100', provinceName: '安徽省', cityName: '合肥市', carrier: 'MOBILE', city: { provinceCode: '340000' } }
|
||||
]
|
||||
},
|
||||
phoneAreaCode: {
|
||||
findMany: async () => [
|
||||
{ areaCode: '0551', cityCode: '340100', provinceName: '安徽省', cityName: '合肥市', city: { provinceCode: '340000' } }
|
||||
]
|
||||
},
|
||||
carrierPrefixRule: {
|
||||
findMany: async () => [
|
||||
{ prefix: '138', carrier: 'MOBILE', priority: 100 }
|
||||
]
|
||||
}
|
||||
} as unknown as PrismaClient;
|
||||
}
|
||||
|
||||
describe('S42 config publisher number library snapshot', () => {
|
||||
it('publishes number library lookup keys and gateway blocked-region sets', async () => {
|
||||
const redis = new MemoryRedis();
|
||||
const now = new Date('2026-06-24T03:20:00.000Z');
|
||||
|
||||
const result = await publishPendingConfig(prismaFixture(), redis as never, now);
|
||||
const version = `${now.getTime()}`;
|
||||
|
||||
expect(result.published).toBe(true);
|
||||
expect(result.manifest).toMatchObject({
|
||||
cityCount: 1,
|
||||
phoneSegmentCount: 1,
|
||||
areaCodeCount: 1,
|
||||
carrierPrefixRuleCount: 1,
|
||||
blockedRegionCount: 1
|
||||
});
|
||||
expect(redis.writes).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ command: 'set', key: `cfg:v:${version}:phone_segment:1380013` }),
|
||||
expect.objectContaining({ command: 'set', key: `cfg:v:${version}:area_code:0551` }),
|
||||
expect.objectContaining({ command: 'set', key: `cfg:v:${version}:carrier_prefix:138` }),
|
||||
expect.objectContaining({ command: 'sadd', key: `cfg:v:${version}:vendor_gateway:vgw_1:blocked_city_codes`, value: '340100' })
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user