Initial LisgloSIPS V2 implementation
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { buildTrendBuckets, callMetrics, DashboardService, startOfShanghaiDayUtc } from './dashboard.service.js';
|
||||
import type { DashboardRepository } from './dashboard.repository.js';
|
||||
|
||||
describe('dashboard service', () => {
|
||||
it('uses Asia/Shanghai day boundary for today summary', async () => {
|
||||
const calls: Array<{ start: Date; end: Date }> = [];
|
||||
const repository = {
|
||||
summaryWindow: async (start: Date, end: Date) => {
|
||||
calls.push({ start, end });
|
||||
return {
|
||||
cdrs: [],
|
||||
ratedCdrs: [],
|
||||
failureCodes: [],
|
||||
abnormalGateways: [],
|
||||
activeCustomers: 0,
|
||||
activeCustomerGateways: 0,
|
||||
activeVendorGateways: 0,
|
||||
pendingQuality: 0
|
||||
};
|
||||
}
|
||||
} as unknown as DashboardRepository;
|
||||
const service = new DashboardService(repository);
|
||||
const now = new Date('2026-06-21T15:30:00.000Z');
|
||||
|
||||
await service.summary(now);
|
||||
|
||||
expect(calls[0]?.start.toISOString()).toBe('2026-06-20T16:00:00.000Z');
|
||||
expect(startOfShanghaiDayUtc(now).toISOString()).toBe('2026-06-20T16:00:00.000Z');
|
||||
});
|
||||
|
||||
it('computes call metrics and answer rate without floating point money exposure', () => {
|
||||
expect(
|
||||
callMetrics([
|
||||
{ startedAt: new Date('2026-06-21T00:00:00.000Z'), durationSec: 10, sipCode: 200 },
|
||||
{ startedAt: new Date('2026-06-21T00:01:00.000Z'), durationSec: 0, sipCode: 486 },
|
||||
{ startedAt: new Date('2026-06-21T00:02:00.000Z'), durationSec: 0, sipCode: 503 }
|
||||
])
|
||||
).toEqual({
|
||||
totalCalls: 3,
|
||||
answeredCalls: 1,
|
||||
failedCalls: 2,
|
||||
answerRate: '0.3333',
|
||||
totalDurationSec: 10
|
||||
});
|
||||
});
|
||||
|
||||
it('builds fixed trend buckets', () => {
|
||||
const buckets = buildTrendBuckets(
|
||||
new Date('2026-06-21T00:00:00.000Z'),
|
||||
new Date('2026-06-21T01:00:00.000Z'),
|
||||
30,
|
||||
[
|
||||
{ startedAt: new Date('2026-06-21T00:10:00.000Z'), durationSec: 30, sipCode: 200 },
|
||||
{ startedAt: new Date('2026-06-21T00:45:00.000Z'), durationSec: 0, sipCode: 480 }
|
||||
],
|
||||
[
|
||||
{ startedAt: new Date('2026-06-21T00:10:00.000Z'), customerFee: '0.100000', vendorCost: '0.030000', grossProfit: '0.070000' }
|
||||
]
|
||||
);
|
||||
|
||||
expect(buckets).toHaveLength(2);
|
||||
expect(buckets[0]).toMatchObject({
|
||||
calls: { totalCalls: 1, answeredCalls: 1, failedCalls: 0 },
|
||||
money: { customerFee: '0.100000', vendorCost: '0.030000', grossProfit: '0.070000' }
|
||||
});
|
||||
expect(buckets[1]).toMatchObject({
|
||||
calls: { totalCalls: 1, answeredCalls: 0, failedCalls: 1 }
|
||||
});
|
||||
});
|
||||
|
||||
it('validates trend query bounds', async () => {
|
||||
const service = new DashboardService({} as DashboardRepository);
|
||||
await expect(service.trends({ hours: '0' })).rejects.toThrow(BadRequestException);
|
||||
await expect(service.trends({ bucketMinutes: '10' })).rejects.toThrow(BadRequestException);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user