const test = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const Module = require('node:module') const crypto = require('node:crypto') const ts = require('typescript') const filename = path.resolve(__dirname, '../src/utils/zodiac.ts') const loaded = new Module(filename, module) loaded.filename = filename loaded.paths = Module._nodeModulePaths(path.dirname(filename)) loaded._compile(ts.transpileModule(fs.readFileSync(filename, 'utf8'), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017 } }).outputText, filename) const { calculateZodiac, zodiacNames } = loaded.exports test('春节前一天与正月初一换生肖,不按1月1日或立春换年', () => { for (const [before, after, previous, next] of [ ['2023-01-21', '2023-01-22', '虎', '兔'], ['2024-02-09', '2024-02-10', '兔', '龙'], ['2025-01-28', '2025-01-29', '龙', '蛇'], ['2026-02-16', '2026-02-17', '蛇', '马'] ]) { assert.equal(calculateZodiac(before), previous) assert.equal(calculateZodiac(after), next) } assert.equal(calculateZodiac('2024-01-01'), '兔') assert.equal(calculateZodiac('2024-02-05'), '兔') // 立春后,春节前仍属兔。 }) test('十二生肖全部覆盖,年份跨越周期仍正确', () => { zodiacNames.forEach((name, index) => { assert.equal(calculateZodiac(`${2008 + index}-06-15`), name) assert.equal(calculateZodiac(`${2020 + index}-06-15`), name) }) }) test('无效或缺失生日不默认显示某种生肖,闰年有效日期可计算', () => { for (const birthday of ['', '2025-02-29', '2024-02-30', '2024-00-10', '2024-13-01', '2024-01-00', '2024-1-1', '1899-12-31', '2101-01-01']) { assert.equal(calculateZodiac(birthday), null) } assert.equal(calculateZodiac('2024-02-29'), '龙') }) test('修改生日跨春节后返回不同生肖,重复计算稳定', () => { const first = calculateZodiac('1999-02-15') const changed = calculateZodiac('1999-02-16') assert.equal(first, '虎') assert.equal(changed, '兔') assert.equal(calculateZodiac('1999-02-15'), first) }) test('十二个图标均为独立的256像素透明PNG,非重复占位图', () => { const files = ['rat', 'ox', 'tiger', 'rabbit', 'dragon', 'snake', 'horse', 'goat', 'monkey', 'rooster', 'dog', 'pig'] const hashes = files.map((name) => { const data = fs.readFileSync(path.resolve(__dirname, `../src/assets/zodiac/${name}.png`)) assert.equal(data.subarray(1, 4).toString(), 'PNG') assert.equal(data.readUInt32BE(16), 256) assert.equal(data.readUInt32BE(20), 256) assert.equal(data[25], 6) // RGBA,保留透明背景。 return crypto.createHash('sha256').update(data).digest('hex') }) assert.equal(new Set(hashes).size, 12) })