208 lines
7.1 KiB
TypeScript
208 lines
7.1 KiB
TypeScript
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') }
|
|
]
|
|
},
|
|
businessPrefix: {
|
|
findMany: async () => [
|
|
{ id: 'bp_1', prefix: '671', name: '业务671', priority: 10, status: 'ENABLED' }
|
|
]
|
|
},
|
|
customerGateway: {
|
|
findMany: async () => [
|
|
{
|
|
id: 'cgw_1',
|
|
customerId: 'cus_1',
|
|
authMode: 'IP',
|
|
sourceIp: '100.93.185.30',
|
|
ips: [
|
|
{ sourceIp: '100.93.185.30' },
|
|
{ sourceIp: '100.93.185.31' }
|
|
],
|
|
sipUsername: null,
|
|
sipDomain: null,
|
|
sipHa1: null,
|
|
lineGroupId: 'lg_1',
|
|
billingCycleSec: 6,
|
|
cycleRate: new Prisma.Decimal('0.008'),
|
|
callerMatchMode: 'PREFIXES',
|
|
callerPrefixes: [{ prefix: '021' }],
|
|
calleeMatchMode: 'BUSINESS_PREFIXES',
|
|
businessPrefixes: [
|
|
{
|
|
businessPrefix: { id: 'bp_1', prefix: '671', name: '业务671', priority: 10, status: 'ENABLED' }
|
|
}
|
|
],
|
|
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'),
|
|
landingCalleePrefix: '86',
|
|
status: 'ENABLED',
|
|
forbiddenPeriods: [],
|
|
codecs: [],
|
|
prefixRules: [],
|
|
callerRewritePool: [
|
|
{ caller: '02160010001', weight: 70, status: 'ENABLED' },
|
|
{ caller: '02160010002', weight: 30, status: 'ENABLED' }
|
|
],
|
|
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({
|
|
schemaVersion: 2,
|
|
businessPrefixCount: 1,
|
|
customerGatewayIpCount: 2,
|
|
customerGatewayBusinessPrefixCount: 1,
|
|
customerGatewayCallerPrefixCount: 1,
|
|
callerRewriteCount: 2,
|
|
cityCount: 1,
|
|
phoneSegmentCount: 1,
|
|
areaCodeCount: 1,
|
|
carrierPrefixRuleCount: 1,
|
|
blockedRegionCount: 1
|
|
});
|
|
expect(redis.writes).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ command: 'set', key: `cfg:v:${version}:business_prefix:bp_1` }),
|
|
expect.objectContaining({ command: 'set', key: `cfg:v:${version}:business_prefix_value:671`, value: 'bp_1' }),
|
|
expect.objectContaining({ command: 'rpush', key: `cfg:v:${version}:business_prefixes` }),
|
|
expect.objectContaining({ command: 'set', key: `cfg:v:${version}:auth:ip:100.93.185.30`, value: 'cgw_1' }),
|
|
expect.objectContaining({ command: 'rpush', key: `cfg:v:${version}:auth:ip:100.93.185.31:gateways`, value: 'cgw_1' }),
|
|
expect.objectContaining({ command: 'rpush', key: `cfg:v:${version}:customer_gateway:cgw_1:caller_prefixes`, value: '021' }),
|
|
expect.objectContaining({ command: 'set', key: `cfg:v:${version}:customer_gateway:cgw_1:line_group`, value: 'lg_1' }),
|
|
expect.objectContaining({ command: 'rpush', key: `cfg:v:${version}:vendor_gateway:vgw_1:caller_rewrite_pool` }),
|
|
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' })
|
|
])
|
|
);
|
|
});
|
|
});
|