文件
OmniCard/backend/test/modules/crm/push.test.ts

120 行
3.5 KiB
TypeScript

import {
CrmRadarPushService,
RadarPushPayload,
isHotRadarPush,
} from '../../../src/modules/crm/service/push';
import { RadarTrack } from '../../../src/comm/types/omni';
function makeTrack(overrides: Partial<RadarTrack> = {}): RadarTrack {
return {
id: 1,
tenantId: 10,
cardId: 101,
visitorId: 'vis_abc_123',
ip: '8.8.8.8',
country: 'US',
city: 'Mountain View',
touchType: 'nfc',
stayDurationMs: 12000,
scrollDepth: 40,
viewedBlocks: ['hero'],
createdAt: '2026-09-20T10:00:00.000Z',
...overrides,
};
}
describe('Task 35: 雷达 Push 通知内存渠道 (CrmRadarPushService)', () => {
const payloadKeys: Array<keyof RadarPushPayload> = [
'event',
'cardId',
'visitorId',
'country',
'city',
'touchType',
'stayDurationMs',
];
let service: CrmRadarPushService;
beforeEach(() => {
service = new CrmRadarPushService();
});
it('buildPayload 字段齐全且 event 为 radar.visitor', () => {
const track = makeTrack();
const payload = service.buildPayload(track);
for (const key of payloadKeys) {
expect(payload).toHaveProperty(key);
}
expect(Object.keys(payload).sort()).toEqual([...payloadKeys].sort());
expect(payload.event).toBe('radar.visitor');
expect(payload.cardId).toBe(track.cardId);
expect(payload.visitorId).toBe(track.visitorId);
expect(payload.country).toBe(track.country);
expect(payload.city).toBe(track.city);
expect(payload.touchType).toBe(track.touchType);
expect(payload.stayDurationMs).toBe(track.stayDurationMs);
});
it('subscribe 后 publish 能收到对应名片的载荷', () => {
const received: RadarPushPayload[] = [];
const track = makeTrack({ cardId: 202, visitorId: 'vis_sub' });
service.subscribe(202, payload => {
received.push(payload);
});
service.publish(track);
expect(received).toHaveLength(1);
expect(received[0].event).toBe('radar.visitor');
expect(received[0].cardId).toBe(202);
expect(received[0].visitorId).toBe('vis_sub');
});
it('不同 cardId 的订阅互不串扰', () => {
const cardA: RadarPushPayload[] = [];
const cardB: RadarPushPayload[] = [];
service.subscribe(301, payload => {
cardA.push(payload);
});
service.subscribe('302', payload => {
cardB.push(payload);
});
service.publish(makeTrack({ cardId: 301, visitorId: 'vis_a' }));
service.publish(makeTrack({ cardId: 302, visitorId: 'vis_b' }));
expect(cardA).toHaveLength(1);
expect(cardA[0].visitorId).toBe('vis_a');
expect(cardB).toHaveLength(1);
expect(cardB[0].visitorId).toBe('vis_b');
});
it('停留满 3 分钟或浏览 3 个模块视为高意向推送', () => {
expect(isHotRadarPush({ stayDurationMs: 179999, viewedBlocks: ['hero'] })).toBe(
false
);
expect(isHotRadarPush({ stayDurationMs: 180000, viewedBlocks: [] })).toBe(true);
expect(
isHotRadarPush({ stayDurationMs: 1000, viewedBlocks: ['hero', 'case', 'article'] })
).toBe(true);
});
it('unsubscribe 后不再收到后续 publish', () => {
const received: RadarPushPayload[] = [];
const listener = (payload: RadarPushPayload) => {
received.push(payload);
};
service.subscribe(401, listener);
service.publish(makeTrack({ cardId: 401, visitorId: 'vis_before' }));
service.unsubscribe(401, listener);
service.publish(makeTrack({ cardId: 401, visitorId: 'vis_after' }));
expect(received).toHaveLength(1);
expect(received[0].visitorId).toBe('vis_before');
});
});