import { test } from 'node:test' import assert from 'node:assert/strict' import { normalizeRulePack } from '../rules' // 「个人+团体」是服务端新增的小项类型;规则包模板按 eventType 匹配它时必须原样保留, // 否则前端归一化会把 match.eventType 丢掉,模板会退化成「匹配全部项目」。 test('规则包模板保留 eventType: individual_team', () => { const pack = normalizeRulePack({ format: 'scoreboard-rules', formatVersion: 1, meta: { code: 'demo', name: '示例规则' }, profiles: [{ code: 'individual_top6', name: '个人项目前六名', awardRanks: 1, tieMode: 'skip', rules: [{ rank: 1, points: 10 }] }], channelTemplates: [ { match: { eventType: 'individual_team' }, channels: [ { scope: 'athlete', basis: 'result', profile: 'individual_top6' }, { scope: 'team', basis: 'manual', profile: 'individual_top6' }, ], }, ], }) assert.ok(pack !== null) assert.equal(pack.channelTemplates[0]?.match.eventType, 'individual_team') assert.equal(pack.channelTemplates[0]?.channels.length, 2) }) test('规则包模板仍接受原有三种类型', () => { for (const eventType of ['individual', 'team', 'match'] as const) { const pack = normalizeRulePack({ format: 'scoreboard-rules', formatVersion: 1, meta: { code: 'demo', name: '示例规则' }, profiles: [{ code: 'p', name: '模板', awardRanks: 1, tieMode: 'skip', rules: [{ rank: 1, points: 1 }] }], channelTemplates: [{ match: { eventType }, channels: [{ scope: 'athlete', basis: 'result', profile: 'p' }] }], }) assert.ok(pack !== null) assert.equal(pack.channelTemplates[0]?.match.eventType, eventType) } }) // 规则包里显式写的汇总口径要落到通道对象上(缺省 = 不写 = avg,导出页才能如实显示原文口径); // 认不出的取值不落到对象上,也不会把 avg / sum / worst 之外的东西带进写请求。 test('规则包通道保留 aggregateMode:avg / sum / worst 都认,缺省不写', () => { const build = (aggregateMode: unknown) => normalizeRulePack({ format: 'scoreboard-rules', formatVersion: 1, meta: { code: 'demo', name: '示例规则' }, profiles: [{ code: 'p', name: '模板', awardRanks: 1, tieMode: 'skip', rules: [{ rank: 1, points: 1 }] }], channelTemplates: [ { match: { eventType: 'team' }, channels: [{ scope: 'team', basis: 'aggregate', profile: 'p', aggregateMode }] }, ], }) for (const mode of ['avg', 'sum', 'worst'] as const) { const pack = build(mode) assert.ok(pack !== null) assert.equal(pack.channelTemplates[0]?.channels[0]?.aggregateMode, mode) } // 缺省(不写)与认不出的取值都不落到对象上:既不凭空写缺省值,也不把 min / max 当取值带过去。 for (const missing of [undefined, '', 'min', 'max', 'bogus']) { const pack = build(missing) assert.ok(pack !== null) assert.equal(pack.channelTemplates[0]?.channels[0]?.aggregateMode, undefined) } })