Files
digital-me-history/digital-me-backend/test/day.test.js
T
admin 0593ff7e62
nginx serve /uploads direct (audio speed) / patch-nginx (push) Failing after 1m34s
数字我 Digital Me:全量入库(大文件走 Git LFS)
2026-10-10 16:24:43 +08:00

75 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 「哪一天」的定义 —— 未成年人模式每日限额的地基(《拟人化互动办法》第十四条「使用时长限制」)。
//
// ⚠ 这组测试存在的理由:原实现用 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));
});