feat: add reconciliation and quality reporting
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import { ReportsService } from './reports.service';
|
||||
|
||||
describe('ReportsService', () => {
|
||||
const tx = {
|
||||
dailyReconciliationReport: { deleteMany: jest.fn() },
|
||||
dailyProfitReport: { deleteMany: jest.fn() },
|
||||
dailyQualityReport: { deleteMany: jest.fn() },
|
||||
$executeRaw: jest.fn(),
|
||||
};
|
||||
const prisma = {
|
||||
dailyReconciliationReport: { findMany: jest.fn(), count: jest.fn() },
|
||||
dailyProfitReport: { findMany: jest.fn(), count: jest.fn() },
|
||||
dailyQualityReport: { findMany: jest.fn(), count: jest.fn() },
|
||||
$transaction: jest.fn((callback: (client: typeof tx) => unknown) => callback(tx)),
|
||||
};
|
||||
let service: ReportsService;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
tx.dailyReconciliationReport.deleteMany.mockResolvedValue({ count: 0 });
|
||||
tx.dailyProfitReport.deleteMany.mockResolvedValue({ count: 0 });
|
||||
tx.dailyQualityReport.deleteMany.mockResolvedValue({ count: 0 });
|
||||
tx.$executeRaw.mockResolvedValue(0);
|
||||
prisma.dailyReconciliationReport.findMany.mockResolvedValue([{ id: 'recon-1' }]);
|
||||
prisma.dailyReconciliationReport.count.mockResolvedValue(1);
|
||||
prisma.dailyProfitReport.findMany.mockResolvedValue([{ id: 'profit-1' }]);
|
||||
prisma.dailyProfitReport.count.mockResolvedValue(1);
|
||||
prisma.dailyQualityReport.findMany.mockResolvedValue([{ id: 'quality-1' }]);
|
||||
prisma.dailyQualityReport.count.mockResolvedValue(1);
|
||||
service = new ReportsService(prisma as never);
|
||||
});
|
||||
|
||||
it('rebuilds exactly T-4 through T-1 in independent transactions', async () => {
|
||||
await expect(service.refreshRollingWindow(new Date('2026-07-15T05:30:00.000Z'))).resolves.toEqual({
|
||||
refreshedDates: ['2026-07-11', '2026-07-12', '2026-07-13', '2026-07-14'],
|
||||
});
|
||||
expect(prisma.$transaction).toHaveBeenCalledTimes(4);
|
||||
expect(tx.dailyReconciliationReport.deleteMany).toHaveBeenCalledTimes(4);
|
||||
expect(tx.dailyProfitReport.deleteMany).toHaveBeenCalledTimes(4);
|
||||
expect(tx.dailyQualityReport.deleteMany).toHaveBeenCalledTimes(4);
|
||||
expect(tx.$executeRaw).toHaveBeenCalledTimes(28);
|
||||
});
|
||||
|
||||
it('queries reconciliation reports with server-side filters and bounded pagination', async () => {
|
||||
await expect(service.listReconciliation({
|
||||
dateFrom: '2026-07-01',
|
||||
dateTo: '2026-07-14',
|
||||
tenantId: 'tenant-1',
|
||||
applicationId: 'app-1',
|
||||
page: 2,
|
||||
pageSize: 500,
|
||||
})).resolves.toEqual({ items: [{ id: 'recon-1' }], total: 1, page: 2, pageSize: 100 });
|
||||
expect(prisma.dailyReconciliationReport.findMany).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: expect.objectContaining({ tenantId: 'tenant-1', applicationId: 'app-1' }),
|
||||
skip: 100,
|
||||
take: 100,
|
||||
}));
|
||||
});
|
||||
|
||||
it('keeps application and channel profit filters separate', async () => {
|
||||
await service.listProfit({ dimensionType: 'channel', tenantId: 'tenant-1', applicationId: 'app-1', channelId: 'channel-1' });
|
||||
expect(prisma.dailyProfitReport.findMany).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: expect.objectContaining({
|
||||
dimensionType: 'channel',
|
||||
tenantId: undefined,
|
||||
applicationId: undefined,
|
||||
channelId: 'channel-1',
|
||||
}),
|
||||
}));
|
||||
});
|
||||
|
||||
it('sorts quality reports by send volume and keeps the selected dimension', async () => {
|
||||
await expect(service.listQuality({ dimensionType: 'drainage', tenantId: 'tenant-1', page: 1, pageSize: 20 })).resolves.toEqual({
|
||||
items: [{ id: 'quality-1' }], total: 1, page: 1, pageSize: 20, dimensionType: 'drainage',
|
||||
});
|
||||
expect(prisma.dailyQualityReport.findMany).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: expect.objectContaining({ dimensionType: 'drainage', tenantId: 'tenant-1' }),
|
||||
orderBy: [{ sentUnits: 'desc' }, { reportDate: 'desc' }, { dimensionName: 'asc' }],
|
||||
}));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user