90 lines
3.9 KiB
TypeScript
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();
|
|
}
|
|
});
|
|
});
|