抽出日期计算纯函数模块,并搭建后端测试基建

- 新增 server/src/occurrence.js:startOfDay/getNextOccurrence/daysBetween,today 可注入
- reminder.js 改为调用新模块,删除内联的 getThisYearDate/daysBetween
- 引入 node:test 作为测试框架,加 npm test 脚本
- 新增 test/helper.js(临时库辅助,供后续任务用)和 test/occurrence.test.js(7 个用例,覆盖农历跨年等边界)
This commit is contained in:
yuming
2026-08-15 17:30:42 +08:00
parent c92df874d1
commit 68acbb3809
5 changed files with 111 additions and 37 deletions
+13
View File
@@ -0,0 +1,13 @@
const fs = require('fs')
const os = require('os')
const path = require('path')
// 把 DB_PATH 指向临时目录,避免测试碰到真实库。
// 必须在 require('../src/db') 之前调用,因为 db.js 在 require 时就读取 DB_PATH。
function useTempDb() {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bday-test-'))
process.env.DB_PATH = path.join(dir, 'test.db')
return dir
}
module.exports = { useTempDb }
+45
View File
@@ -0,0 +1,45 @@
const test = require('node:test')
const assert = require('node:assert')
const { getNextOccurrence, daysBetween, startOfDay } = require('../src/occurrence')
const fmt = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
test('公历纪念日:今年还没到就取今年', () => {
const anniv = { isLunar: false, solarMonth: 12, solarDay: 25 }
assert.strictEqual(fmt(getNextOccurrence(anniv, new Date(2026, 7, 15))), '2026-12-25')
})
test('公历纪念日:今年已过就取明年', () => {
const anniv = { isLunar: false, solarMonth: 5, solarDay: 20 }
assert.strictEqual(fmt(getNextOccurrence(anniv, new Date(2026, 7, 15))), '2027-05-20')
})
test('农历腊月十五:跨公历年不会算错一年', () => {
const anniv = { isLunar: true, lunarMonth: 12, lunarDay: 15, isLeapMonth: false }
// 2027-01-20 时,下一次应是 2 天后的 2027-01-22,而不是 2028 年
assert.strictEqual(fmt(getNextOccurrence(anniv, new Date(2027, 0, 20))), '2027-01-22')
// 过了之后应跳到下一个农历年
assert.strictEqual(fmt(getNextOccurrence(anniv, new Date(2027, 0, 23))), '2028-01-11')
})
test('农历四月十七', () => {
const anniv = { isLunar: true, lunarMonth: 4, lunarDay: 17, isLeapMonth: false }
assert.strictEqual(fmt(getNextOccurrence(anniv, new Date(2026, 7, 15))), '2027-05-22')
})
test('isLunar 但缺农历字段时降级按公历算,不抛错', () => {
const anniv = { isLunar: true, lunarMonth: null, lunarDay: null, solarMonth: 6, solarDay: 5 }
assert.strictEqual(fmt(getNextOccurrence(anniv, new Date(2026, 7, 15))), '2027-06-05')
})
test('daysBetween 忽略时分秒', () => {
const today = new Date(2026, 7, 15, 23, 59, 0)
const target = new Date(2026, 7, 17, 0, 1, 0)
assert.strictEqual(daysBetween(target, today), 2)
})
test('startOfDay 归零时分秒', () => {
const d = startOfDay(new Date(2026, 7, 15, 13, 45, 30))
assert.strictEqual(d.getHours(), 0)
assert.strictEqual(d.getMinutes(), 0)
})