92dfbf5722
日报、周报、月报均不含发送当天发生额,避免盘中数据未闭合。
137 lines
4.3 KiB
TypeScript
137 lines
4.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import {
|
||
formatWecomReportMarkdown,
|
||
wecomReportCutoff,
|
||
wecomReportDueAt,
|
||
wecomReportPeriod,
|
||
wecomReportShouldFire,
|
||
} from './wecom-report';
|
||
|
||
const emptyStats = {
|
||
usersTotal: 10,
|
||
usersIncrement: 2,
|
||
partnersTotal: 3,
|
||
partnersIncrement: 1,
|
||
storesTotal: 4,
|
||
storesIncrement: 0,
|
||
ordersTotal: 20,
|
||
ordersIncrement: 5,
|
||
orderAmountTotal: 1000,
|
||
orderAmountIncrement: 80.5,
|
||
redeemsTotal: 8,
|
||
redeemsIncrement: 3,
|
||
redeemAmountTotal: 200,
|
||
redeemAmountIncrement: 40,
|
||
};
|
||
|
||
describe('wecomReportPeriod', () => {
|
||
it('daily is the previous Shanghai calendar day (closed at send-day 00:00)', () => {
|
||
const p = wecomReportPeriod('daily', new Date('2026-09-02T20:00:00+08:00'));
|
||
expect(p.periodKey).toBe('2026-09-01');
|
||
expect(p.incrementLabel).toBe('当日新增');
|
||
expect(p.start.toISOString()).toBe(new Date('2026-09-01T00:00:00+08:00').toISOString());
|
||
expect(p.endExclusive.toISOString()).toBe(new Date('2026-09-02T00:00:00+08:00').toISOString());
|
||
});
|
||
|
||
it('weekly is the previous natural week', () => {
|
||
const p = wecomReportPeriod('weekly', new Date('2026-09-02T09:00:00+08:00'));
|
||
expect(p.periodKey).toBe('2026-08-24');
|
||
expect(p.rangeLabel).toBe('2026-08-24 ~ 2026-08-30');
|
||
expect(p.incrementLabel).toBe('本期新增');
|
||
expect(p.endExclusive.toISOString()).toBe(new Date('2026-08-31T00:00:00+08:00').toISOString());
|
||
});
|
||
|
||
it('monthly is the previous natural month', () => {
|
||
const p = wecomReportPeriod('monthly', new Date('2026-09-01T09:00:00+08:00'));
|
||
expect(p.periodKey).toBe('2026-08');
|
||
expect(p.title).toBe('月报(2026年8月)');
|
||
expect(p.endExclusive.toISOString()).toBe(new Date('2026-09-01T00:00:00+08:00').toISOString());
|
||
});
|
||
});
|
||
|
||
describe('wecomReportCutoff', () => {
|
||
it('daily closes at send-day midnight, not send clock time', () => {
|
||
const now = new Date('2026-09-02T20:00:00+08:00');
|
||
const p = wecomReportPeriod('daily', now);
|
||
expect(wecomReportCutoff(p).getTime()).toBe(new Date('2026-09-02T00:00:00+08:00').getTime());
|
||
});
|
||
|
||
it('uses period end for a completed week', () => {
|
||
const now = new Date('2026-09-02T09:00:00+08:00');
|
||
const p = wecomReportPeriod('weekly', now);
|
||
expect(wecomReportCutoff(p).getTime()).toBe(p.endExclusive.getTime());
|
||
});
|
||
});
|
||
|
||
describe('wecomReportShouldFire', () => {
|
||
const base = {
|
||
enabled: true,
|
||
sendHour: 20,
|
||
sendMinute: 0,
|
||
sendWeekday: 1,
|
||
sendMonthDay: 1,
|
||
lastSentPeriod: null as string | null,
|
||
};
|
||
|
||
it('does not fire before due time', () => {
|
||
expect(
|
||
wecomReportShouldFire('daily', base, new Date('2026-09-02T19:59:00+08:00')),
|
||
).toBe(false);
|
||
});
|
||
|
||
it('fires after due time if this period not sent', () => {
|
||
expect(
|
||
wecomReportShouldFire('daily', base, new Date('2026-09-02T20:00:00+08:00')),
|
||
).toBe(true);
|
||
});
|
||
|
||
it('does not fire twice for the same period', () => {
|
||
expect(
|
||
wecomReportShouldFire(
|
||
'daily',
|
||
{ ...base, lastSentPeriod: '2026-09-01' },
|
||
new Date('2026-09-02T21:00:00+08:00'),
|
||
),
|
||
).toBe(false);
|
||
});
|
||
|
||
it('weekly fires Monday 09:00 for last week', () => {
|
||
expect(
|
||
wecomReportShouldFire(
|
||
'weekly',
|
||
{ ...base, sendHour: 9, sendMinute: 0, sendWeekday: 1 },
|
||
new Date('2026-09-07T09:00:00+08:00'),
|
||
),
|
||
).toBe(true);
|
||
});
|
||
});
|
||
|
||
describe('wecomReportDueAt', () => {
|
||
it('monthly clamps to last day of month', () => {
|
||
const due = wecomReportDueAt(
|
||
'monthly',
|
||
{
|
||
enabled: true,
|
||
sendHour: 9,
|
||
sendMinute: 0,
|
||
sendWeekday: 1,
|
||
sendMonthDay: 31,
|
||
lastSentPeriod: null,
|
||
},
|
||
new Date('2026-09-10T00:00:00+08:00'),
|
||
);
|
||
expect(due.toISOString()).toBe(new Date('2026-09-30T09:00:00+08:00').toISOString());
|
||
});
|
||
});
|
||
|
||
describe('formatWecomReportMarkdown', () => {
|
||
it('renders stock plus increment lines', () => {
|
||
const p = wecomReportPeriod('daily', new Date('2026-09-02T20:00:00+08:00'));
|
||
const md = formatWecomReportMarkdown(p, emptyStats);
|
||
expect(md).toContain('**杜康好客 · 日报(2026-09-01)**');
|
||
expect(md).toContain('用户数量:10(当日新增 2)');
|
||
expect(md).toContain('订单金额:1000.00(当日新增 80.50)');
|
||
expect(md).toContain('核销单数量:8(当日新增 3)');
|
||
});
|
||
});
|