数字我 Digital Me:全量入库(大文件走 Git LFS)
nginx serve /uploads direct (audio speed) / patch-nginx (push) Failing after 1m34s

This commit is contained in:
2026-10-10 16:24:43 +08:00
commit 0593ff7e62
1893 changed files with 361723 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
// 「哪一天」的定义 —— 未成年人模式每日限额的地基(《拟人化互动办法》第十四条「使用时长限制」)。
//
// ⚠ 这组测试存在的理由:原实现用 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));
});