补充发送侧分组与错误分支的测试覆盖,保留43101原始错误信息的修复
- reminder.js:43101 跳过日志改为记录微信原始错误信息,便于和「额度耗尽」的 兜底文案区分排查(前一实施者已验证,此次原样带入提交) - reminder.test.js:新增 runOnce 按 openid 分组结算用例,验证多用户额度互不 串号;将「非额度类错误」用例扩到 2 条数据,证明该分支不会误中止后续发送 - 新建 wx.test.js:验证 sendSubscribeMessage 在微信返回非 0 errcode 时, 抛出的 Error 对象确实带有正确的 errcode 属性 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -103,21 +103,29 @@ test('遇到 43101 立即归零并中止本用户剩余发送', async () => {
|
||||
assert.strictEqual(r.skipped, 3, '含失败那条在内全部计为 skipped')
|
||||
})
|
||||
|
||||
test('非额度类错误只记 failed,不动余额', async () => {
|
||||
test('非额度类错误只记 failed,不中止后续发送,也不动余额', async () => {
|
||||
resetLogs()
|
||||
quota.grant('uE', 4)
|
||||
let called = 0
|
||||
const restore = wx.sendSubscribeMessage
|
||||
wx.sendSubscribeMessage = async () => {
|
||||
called++
|
||||
const e = new Error('网络炸了')
|
||||
e.errcode = 40003
|
||||
throw e
|
||||
}
|
||||
|
||||
const items = [makeAnniv('E1', 'uE', '甲', 'high', 0, 3)]
|
||||
// 至少 2 条数据:如果有人误把 halted = true 加进非额度错误分支,
|
||||
// 第二条就不会被尝试,called 会停在 1,这条断言就能抓到
|
||||
const items = [
|
||||
makeAnniv('E1', 'uE', '甲', 'high', 0, 3),
|
||||
makeAnniv('E2', 'uE', '乙', 'high', 0, 3)
|
||||
]
|
||||
const r = await reminder.runForUser('uE', items, TODAY)
|
||||
wx.sendSubscribeMessage = restore
|
||||
|
||||
assert.strictEqual(r.fail, 1)
|
||||
assert.strictEqual(called, 2, '第二条也应被尝试,证明非额度错误不会误中止后续发送')
|
||||
assert.strictEqual(r.fail, 2)
|
||||
assert.strictEqual(quota.getBalance('uE'), 4, '配置/网络错误不该动余额')
|
||||
})
|
||||
|
||||
@@ -129,3 +137,42 @@ test('skipped 会写入 remind_logs 便于排查', async () => {
|
||||
assert.ok(row, '应写入 skipped 日志')
|
||||
assert.strictEqual(row.error, 'quota_exhausted')
|
||||
})
|
||||
|
||||
// runOnce 是从数据库读数据的(SELECT * FROM anniversaries WHERE remindEnabled = 1),
|
||||
// 不能像 runForUser 那样直接传数组,必须真的往临时库插行
|
||||
function insertAnniv(id, openid, personName, offsetDays, remindDays) {
|
||||
const d = inDays(offsetDays)
|
||||
db.prepare(`
|
||||
INSERT INTO anniversaries
|
||||
(id, openid, personId, personName, type, isLunar, solarYear, solarMonth, solarDay,
|
||||
importance, remindEnabled, remindDays, createTime, updateTime)
|
||||
VALUES (?, ?, 'p', ?, 'birthday', 0, ?, ?, ?, 'high', 1, ?, 0, 0)
|
||||
`).run(id, openid, personName, d.getFullYear(), d.getMonth() + 1, d.getDate(), remindDays)
|
||||
}
|
||||
|
||||
test('runOnce 按 openid 分组结算,各用户额度互不影响', async () => {
|
||||
resetLogs()
|
||||
// 会读到库里所有 remindEnabled=1 的行,先清空避免受其它用例残留数据干扰
|
||||
db.prepare('DELETE FROM anniversaries').run()
|
||||
|
||||
quota.grant('uG', 5) // uG 有额度
|
||||
// uH 不 grant,余额保持 0
|
||||
|
||||
insertAnniv('G1', 'uG', '甲', 0, 3) // 今天到期
|
||||
insertAnniv('H1', 'uH', '乙', 0, 3) // 今天到期
|
||||
|
||||
let called = 0
|
||||
const restore = wx.sendSubscribeMessage
|
||||
wx.sendSubscribeMessage = async () => { called++; return { errcode: 0 } }
|
||||
|
||||
const r = await reminder.runOnce()
|
||||
wx.sendSubscribeMessage = restore
|
||||
|
||||
assert.strictEqual(r.total, 2)
|
||||
assert.strictEqual(r.ok, 1, '有额度的 uG 应正常发出')
|
||||
assert.strictEqual(r.skipped, 1, '没额度的 uH 应被跳过')
|
||||
assert.strictEqual(r.fail, 0)
|
||||
assert.strictEqual(called, 1, '没额度的用户不该真的发起请求,两用户不能互相借额度')
|
||||
assert.strictEqual(quota.getBalance('uG'), 4, 'uG 消费后余额正确减少')
|
||||
assert.strictEqual(quota.getBalance('uH'), 0, 'uH 的余额不受 uG 影响')
|
||||
})
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
const test = require('node:test')
|
||||
const assert = require('node:assert')
|
||||
const { useTempDb } = require('./helper')
|
||||
|
||||
// wx.js 本身不连库,但按项目约定统一在 require 业务模块前处理好临时库,
|
||||
// 避免以后 wx.js 引入了连库逻辑时这份测试悄悄读写到真实数据库。
|
||||
useTempDb()
|
||||
|
||||
const axios = require('axios')
|
||||
const wx = require('../src/wx')
|
||||
|
||||
test('微信返回非 0 errcode 时,抛出的 Error 对象应带上原始 errcode', async () => {
|
||||
const restoreGet = axios.get
|
||||
const restorePost = axios.post
|
||||
|
||||
// getAccessToken 走 axios.get,sendSubscribeMessage 走 axios.post,两个都要 mock
|
||||
axios.get = async () => ({ data: { access_token: 'fake_token', expires_in: 7200 } })
|
||||
axios.post = async () => ({ data: { errcode: 43101, errmsg: 'user refuse to accept the msg' } })
|
||||
|
||||
await assert.rejects(
|
||||
() => wx.sendSubscribeMessage({
|
||||
touser: 'oTest',
|
||||
page: 'pages/index/index',
|
||||
templateId: 'tplTest',
|
||||
data: {}
|
||||
}),
|
||||
(err) => {
|
||||
assert.strictEqual(err.errcode, 43101, 'Error 对象应带上微信返回的原始 errcode,而不是靠字符串匹配')
|
||||
return true
|
||||
}
|
||||
)
|
||||
|
||||
axios.get = restoreGet
|
||||
axios.post = restorePost
|
||||
})
|
||||
Reference in New Issue
Block a user