Files
lislgosms/api/src/signature-retirement/daily-message.spec.ts
T

90 lines
3.9 KiB
TypeScript

import { SignatureRetirementService } from './signature-retirement.service';
describe('daily application messages and date formatting', () => {
function setup() {
const detections = [
{ id: 'a', tenantId: 't1', applicationId: 'app1', dimensionType: 'enterprise' },
{ id: 'b', tenantId: 't1', applicationId: 'app1', dimensionType: 'channel' },
{ id: 'c', tenantId: 't1', applicationId: 'app2', dimensionType: 'enterprise' },
{ id: 'd', tenantId: 't1', applicationId: null, dimensionType: 'enterprise' },
{ id: 'e', tenantId: 't2', applicationId: null, dimensionType: 'enterprise' },
].map((item) => ({
...item,
cycleId: `cycle-${item.id}`,
notificationTitle: '预警',
notificationContent: `冻结正文${item.id}`,
}));
const prisma = {
signatureAnalyticsRun: { findUnique: jest.fn().mockResolvedValue({ state: 'succeeded' }) },
signatureRetirementDetection: { findMany: jest.fn().mockResolvedValue(detections) },
smsApplication: {
findMany: jest.fn().mockResolvedValue([
{ id: 'app1', name: '应用一' },
{ id: 'app2', name: '应用二' },
]),
},
signatureRetirementMessage: {
findFirst: jest.fn().mockResolvedValue(null),
create: jest.fn().mockResolvedValue({}),
findMany: jest.fn().mockResolvedValue([]),
},
signatureRetirementWebhook: { findMany: jest.fn().mockResolvedValue([]) },
};
return { prisma, service: new SignatureRetirementService(prisma as never) };
}
it('creates one frozen message per application per day including all dimensions and separate unbound tenants', async () => {
const { prisma, service } = setup();
expect(await service.publishNotifications('2026-09-09')).toEqual({ notificationDate: '2026-09-09', created: 4 });
const data = prisma.signatureRetirementMessage.create.mock.calls.map(([arg]) => arg.data);
expect(data[0]).toEqual(
expect.objectContaining({
title: '应用一 · 签名清退预警',
detectionIds: ['a', 'b'],
content: '冻结正文a\n冻结正文b',
}),
);
expect(new Set(data.map((item) => item.dailyGroupKey)).size).toBe(4);
expect(data.every((item) => item.notificationDate.toISOString() === '2026-09-09T00:00:00.000Z')).toBe(true);
});
it('does not regenerate already-published or historical single-detection messages', async () => {
const { prisma, service } = setup();
prisma.signatureRetirementMessage.findFirst.mockResolvedValue({ id: 'existing' });
expect((await service.publishNotifications('2026-09-09')).created).toBe(0);
expect(prisma.signatureRetirementMessage.create).not.toHaveBeenCalled();
});
it.each([
['2024-03-01', '2024-02-29'],
['2026-01-01', '2025-12-31'],
['2026-09-01', '2026-08-31'],
])('keeps T-1 semantics for %s without constructing formatters per row', async (date, expected) => {
const prisma = {
signatureAnalyticsRun: { findUnique: jest.fn().mockResolvedValue({ state: 'succeeded' }) },
signatureRetirementDetection: {
findMany: jest.fn().mockResolvedValue(
Array.from({ length: 100 }, (_, i) => ({
id: String(i),
detectionDate: new Date(`${date}T00:00:00Z`),
signatureId: 's',
tenantId: 't',
})),
),
},
smsSignature: { findMany: jest.fn().mockResolvedValue([]) },
smsChannel: { findMany: jest.fn().mockResolvedValue([]) },
tenant: { findMany: jest.fn().mockResolvedValue([]) },
channelSignatureReportTask: { findMany: jest.fn().mockResolvedValue([]) },
};
const spy = jest.spyOn(Intl, 'DateTimeFormat');
try {
const result = await new SignatureRetirementService(prisma as never).heatmap(date);
expect(result.items.every((item) => item.activityDate === expected)).toBe(true);
expect(spy).not.toHaveBeenCalled();
} finally {
spy.mockRestore();
}
});
});