feat: add number library routing and cdr location support

This commit is contained in:
hectorzhao
2026-06-24 11:39:32 +08:00
parent 5fa1bd35e9
commit 7057fd3c42
63 changed files with 6361 additions and 566 deletions
@@ -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' })
])
);
});
});