import assert from 'node:assert/strict'; import test from 'node:test'; import { createRequestIdKeeper, getSilenceErrorMessage, getSilencePaymentOptions, getSilenceReasonText, getSilenceStatusClass, isPaymentLink, isSilenceFeatureDisabled, isSilenceOrderFinal, pickSilenceReminder, silenceReminderKey, silenceReminderRepeats, isSilenceReminderBlocking, validateExtensionDays } from '../utils/silence.js'; test('延期支付方式只取 wechat、alipay 与资产允许方式的交集,支付宝优先', () => { assert.deepEqual(getSilencePaymentOptions(['wechat', 'wallet', 'alipay']).map((item) => item.value), ['alipay', 'wechat']); assert.deepEqual(getSilencePaymentOptions(['wallet']), []); assert.deepEqual(getSilencePaymentOptions(undefined), []); assert.deepEqual(getSilencePaymentOptions(['alipay', 'wechat'], true).map((item) => item.value), ['wechat', 'alipay']); }); test('延期天数校验:整数且在 min~max 内', () => { assert.equal(validateExtensionDays('30', 30, 180), ''); assert.equal(validateExtensionDays(180, 30, 180), ''); assert.equal(validateExtensionDays('29', 30, 180), '延期天数需在 30~180 天之间'); assert.equal(validateExtensionDays('181', 30, 180), '延期天数需在 30~180 天之间'); assert.equal(validateExtensionDays('12.5', 30, 180), '请输入整数天数'); assert.equal(validateExtensionDays('', 30, 180), '请输入整数天数'); }); test('request_id:同一天数与支付方式复用,任一变化或重置后生成新的', () => { let seq = 0; const keeper = createRequestIdKeeper(() => `id-${++seq}`); assert.equal(keeper.get(30, 'alipay'), 'id-1'); assert.equal(keeper.get(30, 'alipay'), 'id-1'); assert.equal(keeper.get(60, 'alipay'), 'id-2'); assert.equal(keeper.get(60, 'wechat'), 'id-3'); keeper.reset(); assert.equal(keeper.get(60, 'wechat'), 'id-4'); }); test('沉默期错误码转客户提示', () => { assert.equal(getSilenceErrorMessage({ code: 1266, data: { min_days: 30, max_days: 180 } }), '延期天数需在 30~180 天之间'); assert.equal(getSilenceErrorMessage({ code: 1265, data: { min_months: 1, max_months: 6 } }), '超过可延期期限,可延期 1~6 个月'); assert.equal(getSilenceErrorMessage({ code: 1263, data: { reason: 'not_expired' } }), '套餐沉默期尚未过期,暂不需要延期'); assert.equal(getSilenceErrorMessage({ code: 1267 }), '免费延期次数已用完'); assert.equal(getSilenceErrorMessage({ code: 1271 }), '已有未完成的延期订单,请先完成支付或等待其关闭'); assert.equal(getSilenceErrorMessage({ code: 1274 }), '延期功能暂未开放'); assert.equal(getSilenceErrorMessage({ code: 1001, msg: '参数错误' }), '参数错误'); assert.equal(getSilenceErrorMessage({ paymentResultUnknown: true }), '网络异常,请稍后在"我的延期订单"中查看结果'); assert.equal(getSilenceErrorMessage({}, '兜底'), '兜底'); assert.equal(isSilenceFeatureDisabled({ code: 1274 }), true); assert.equal(isSilenceFeatureDisabled({ code: 1266 }), false); }); test('资格原因、状态样式、订单终态与支付链接判断', () => { assert.equal(getSilenceReasonText('extension_disabled'), '该套餐不支持付费延期,请联系客服'); assert.equal(getSilenceReasonText('unknown'), '当前暂不能办理延期,请联系客服'); assert.equal(getSilenceStatusClass('expired'), 'silence-expired'); assert.equal(getSilenceStatusClass('other'), ''); assert.equal(isSilenceOrderFinal('completed'), true); assert.equal(isSilenceOrderFinal('pending_payment'), false); assert.equal(isPaymentLink('https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?x=1'), true); assert.equal(isPaymentLink('weixin://wxpay/bizpayurl?pr=abc'), false); assert.equal(isPaymentLink(''), false); }); test('首页沉默期提醒:优先已过期可延期,其次 7 天内到期的待实名,含加油包子节点', () => { const now = Date.parse('2026-11-07T10:00:00+08:00'); const node = (id, silence, children = []) => ({ package_usage_id: id, package_name: `套餐${id}`, silence, children }); assert.equal(pickSilenceReminder([], now), null); assert.equal(pickSilenceReminder([node(1, null)], now), null); assert.equal(pickSilenceReminder([node(1, { silence_status: 'expired', extendable: false })], now), null); const expired = pickSilenceReminder([ node(1, { silence_status: 'waiting', silence_deadline_at: '2026-11-09T23:59:59+08:00' }), node(2, null, [node(3, { silence_status: 'expired', extendable: true, silence_deadline_at: '2026-11-06T23:59:59+08:00' })]) ], now); assert.equal(expired.kind, 'expired'); assert.equal(expired.packageUsageId, 3); const waiting = pickSilenceReminder([ node(4, { silence_status: 'waiting', silence_deadline_at: '2026-11-20T23:59:59+08:00' }), node(5, { silence_status: 'waiting', silence_deadline_at: '2026-11-12T23:59:59+08:00' }), node(6, { silence_status: 'waiting', silence_deadline_at: '2026-11-09T23:59:59+08:00' }) ], now); assert.equal(waiting.kind, 'waiting'); assert.equal(waiting.packageUsageId, 6); assert.equal(waiting.remainingDays, 3); assert.equal(pickSilenceReminder([node(7, { silence_status: 'ended' })], now), null); const keyA = silenceReminderKey('A', { packageUsageId: 3, kind: 'expired' }, 'token-1'); assert.match(keyA, /^silence_reminder:A:3:expired:/); assert.equal(keyA, silenceReminderKey('A', { packageUsageId: 3, kind: 'expired' }, 'token-1')); assert.notEqual(keyA, silenceReminderKey('A', { packageUsageId: 3, kind: 'expired' }, 'token-2')); }); test('只有已过期未延期的提醒每次回首页都弹,待实名提醒每次登录一次', () => { assert.equal(silenceReminderRepeats({ kind: 'expired' }), true); assert.equal(silenceReminderRepeats({ kind: 'expired', pendingOrderNo: 'SE1' }), true); assert.equal(silenceReminderRepeats({ kind: 'waiting' }), false); assert.equal(silenceReminderRepeats(null), false); }); test('已过期的沉默期提醒为强制弹窗,临近截止的可关闭', () => { assert.equal(isSilenceReminderBlocking({ kind: 'expired' }), true); assert.equal(isSilenceReminderBlocking({ kind: 'expired', pendingOrderNo: 'SE1' }), true); assert.equal(isSilenceReminderBlocking({ kind: 'waiting' }), false); assert.equal(isSilenceReminderBlocking(null), false); });