75 lines
4.1 KiB
JavaScript
75 lines
4.1 KiB
JavaScript
// 「哪一天」的定义 —— 未成年人模式每日限额的地基(《拟人化互动办法》第十四条「使用时长限制」)。
|
||
//
|
||
// ⚠ 这组测试存在的理由:原实现用 now.getFullYear()/getMonth()/getDate() 读【进程本地时区】,
|
||
// 并在注释里写「部署时服务器 TZ 应为 Asia/Shanghai」—— 但注释不是保证。共享 ECS 若是 UTC,
|
||
// 「每日 60 分钟」会在北京时间早上 8 点重置而不是午夜:限额名不副实,
|
||
// 没有报错、没有告警、没有任何人会发现。合规义务不能挂在一条没人签字、没人复查的部署约定上。
|
||
//
|
||
// ⚠ 别用 `TZ=UTC node --test` 这种方式测:Windows 上 TZ 环境变量对 Node 不生效,
|
||
// 三个时区会全部返回同一个值 —— 那不是「代码锁死了时区」,那是【测试本身是坏的】。
|
||
// (我们真的被这个绿色骗过一次。)
|
||
// 下面的断言不依赖进程 TZ:给定一个【确切的 UTC 时刻】,断言它在北京时间的日历日 ——
|
||
// 服务器在哪个时区,这个答案都必须一样。
|
||
import test from 'node:test';
|
||
import assert from 'node:assert/strict';
|
||
import { dayKey, lastDayKeys, DAY_TZ } from '../src/lib/day.js';
|
||
|
||
test('切日时区恒为 Asia/Shanghai', () => {
|
||
assert.equal(DAY_TZ, 'Asia/Shanghai');
|
||
});
|
||
|
||
test('★ 跨日的那一刻:18:30Z 在北京已是次日凌晨 2:30 —— 必须算作次日', () => {
|
||
// 这一条是整组的核心。若 dayKey 读的是服务器本地时区:
|
||
// UTC 服务器 → '2026-07-16'(错,北京已经是 17 号了)
|
||
// 纽约服务器 → '2026-07-16'(错)
|
||
// 只有显式锁 Asia/Shanghai 才恒为 '2026-07-17'。
|
||
assert.equal(dayKey(new Date('2026-07-16T18:30:00Z')), '2026-07-17');
|
||
});
|
||
|
||
test('★ 北京的午夜前后:15:59Z 是当天,16:00Z 就跨日了', () => {
|
||
// 北京 = UTC+8,故 UTC 16:00 整 = 北京次日 00:00。
|
||
assert.equal(dayKey(new Date('2026-07-16T15:59:59Z')), '2026-07-16'); // 北京 23:59:59
|
||
assert.equal(dayKey(new Date('2026-07-16T16:00:00Z')), '2026-07-17'); // 北京 次日 00:00
|
||
// ↑ 这两条把「每日限额到底几点重置」钉死在北京的午夜。
|
||
// 若哪天有人把 DAY_TZ 改了、或把 Intl 换回 getDate(),这两条会立刻红。
|
||
});
|
||
|
||
test('UTC 白天的时刻不该被误判', () => {
|
||
assert.equal(dayKey(new Date('2026-07-16T04:00:00Z')), '2026-07-16'); // 北京 12:00
|
||
assert.equal(dayKey(new Date('2026-01-01T00:00:00Z')), '2026-01-01'); // 北京 08:00,跨年也不许错
|
||
});
|
||
|
||
test('跨年:12/31 的 16:00Z 在北京已是元旦', () => {
|
||
assert.equal(dayKey(new Date('2025-12-31T16:00:00Z')), '2026-01-01');
|
||
});
|
||
|
||
test('格式恒为 YYYY-MM-DD,月日补零', () => {
|
||
assert.match(dayKey(new Date('2026-03-05T04:00:00Z')), /^\d{4}-\d{2}-\d{2}$/);
|
||
assert.equal(dayKey(new Date('2026-03-05T04:00:00Z')), '2026-03-05');
|
||
});
|
||
|
||
test('lastDayKeys:从旧到新,末位是今天,且同样按北京时间切', () => {
|
||
// 起点选北京时间 7/17 02:30(= 7/16 18:30Z)—— 若这里退回按服务器本地日历日构造,
|
||
// 末位会变成 '2026-07-16',这条会红。
|
||
const keys = lastDayKeys(3, new Date('2026-07-16T18:30:00Z'));
|
||
assert.deepEqual(keys, ['2026-07-15', '2026-07-16', '2026-07-17']);
|
||
assert.equal(keys[keys.length - 1], dayKey(new Date('2026-07-16T18:30:00Z')), '末位必须是今天');
|
||
});
|
||
|
||
test('lastDayKeys:跨月不许错', () => {
|
||
assert.deepEqual(lastDayKeys(3, new Date('2026-03-01T04:00:00Z')), ['2026-02-27', '2026-02-28', '2026-03-01']);
|
||
});
|
||
|
||
test('lastDayKeys(1) 只给今天', () => {
|
||
assert.deepEqual(lastDayKeys(1, new Date('2026-07-16T04:00:00Z')), ['2026-07-16']);
|
||
});
|
||
|
||
test('记账与查账必须是同一个「今天」', () => {
|
||
// minor.js 按 dayKey 记账、guardian.js 按 lastDayKeys 查账。两者口径若分叉,
|
||
// 家长看到的「今天用了多久」会和后端真正拿来拦人的数字对不上 —— 而这种错不报任何错,
|
||
// 只会让家长觉得「这功能在骗我」。这条断言把两者钉在一起。
|
||
const now = new Date('2026-07-16T18:30:00Z');
|
||
const last = lastDayKeys(7, now);
|
||
assert.equal(last[last.length - 1], dayKey(now));
|
||
});
|