import { test } from 'node:test' import assert from 'node:assert/strict' import { applyHeatToRows, filterRowsByHeat, heatLabel, heatOptions, maxHeat, planHeatSplit, type HeatEntryRow, } from '../pages/entryHeat' type Row = HeatEntryRow & { name: string } function rows(spec: Array<[string, number | null]>): Row[] { return spec.map(([name, heat], index) => ({ entryId: index + 1, name, heat })) } test('heatLabel:未分批与第 N 批', () => { assert.equal(heatLabel(null), '未分批') assert.equal(heatLabel(1), '第 1 批') assert.equal(heatLabel(12), '第 12 批') }) test('heatOptions:全部 → 已有批次去重升序 → 未分批', () => { const options = heatOptions(rows([['甲', 2], ['乙', null], ['丙', 1], ['丁', 2]])) assert.deepEqual(options, [ { value: 'all', label: '全部' }, { value: 1, label: '第 1 批' }, { value: 2, label: '第 2 批' }, { value: 'none', label: '未分批' }, ]) }) test('heatOptions:没有未分批的行时不给「未分批」选项,避免选中得到空表', () => { assert.deepEqual(heatOptions(rows([['甲', 1], ['乙', 3]])), [ { value: 'all', label: '全部' }, { value: 1, label: '第 1 批' }, { value: 3, label: '第 3 批' }, ]) }) test('heatOptions:一条报名都没有时只有「全部」', () => { assert.deepEqual(heatOptions([]), [{ value: 'all', label: '全部' }]) }) test('filterRowsByHeat:全部 / 指定批次 / 未分批,顺序不变', () => { const list = rows([['甲', 1], ['乙', null], ['丙', 2], ['丁', 1]]) assert.deepEqual( filterRowsByHeat(list, 'all').map((row) => row.name), ['甲', '乙', '丙', '丁'], ) assert.deepEqual( filterRowsByHeat(list, 1).map((row) => row.name), ['甲', '丁'], ) assert.deepEqual( filterRowsByHeat(list, 'none').map((row) => row.name), ['乙'], ) assert.deepEqual(filterRowsByHeat(list, 9), []) // 返回新数组,不改动入参。 assert.equal(list.length, 4) }) test('maxHeat:取已有最大批次号,没有批次时是 0', () => { assert.equal(maxHeat([]), 0) assert.equal(maxHeat(rows([['甲', null], ['乙', null]])), 0) assert.equal(maxHeat(rows([['甲', 2], ['乙', null], ['丙', 5]])), 5) }) test('planHeatSplit:未分批的行按每批人数顺序切片,批次号从 1 开始', () => { const list = rows([ ['甲', null], ['乙', null], ['丙', null], ['丁', null], ['戊', null], ['己', null], ['庚', null], ['辛', null], ['壬', null], ['癸', null], ]) const plan = planHeatSplit(list, 8) assert.ok(plan !== null) assert.equal(plan.rowCount, 10) assert.equal(plan.batchCount, 2) assert.equal(plan.startHeat, 1) assert.equal(plan.perHeat, 8) assert.deepEqual(plan.allocations, [ { heat: 1, entryIds: [1, 2, 3, 4, 5, 6, 7, 8] }, { heat: 2, entryIds: [9, 10] }, ]) }) test('planHeatSplit:已经分批的行不动,新批次号接着最大号往后排', () => { const list = rows([ ['甲', 3], ['乙', null], ['丙', null], ['丁', 1], ['戊', null], ]) const plan = planHeatSplit(list, 2) assert.ok(plan !== null) assert.equal(plan.startHeat, 4, '接着最大批次号 3 往后排,而不是从 1 覆盖') assert.deepEqual(plan.allocations, [ { heat: 4, entryIds: [2, 3] }, { heat: 5, entryIds: [5] }, ]) // 已分批的甲 / 丁不在任何一批里。 const touched = plan.allocations.flatMap((item) => item.entryIds) assert.equal(touched.includes(1), false) assert.equal(touched.includes(4), false) }) test('planHeatSplit:每批人数大于剩余人数时只出一批', () => { const plan = planHeatSplit(rows([['甲', null], ['乙', null], ['丙', null]]), 8) assert.deepEqual(plan?.allocations, [{ heat: 1, entryIds: [1, 2, 3] }]) }) test('planHeatSplit:没有未分批的行、或每批人数非法时返回 null', () => { assert.equal(planHeatSplit(rows([['甲', 1]]), 8), null, '全都分过批就不该有编排') assert.equal(planHeatSplit(rows([['甲', null]]), 0), null) assert.equal(planHeatSplit(rows([['甲', null]]), -3), null) assert.equal(planHeatSplit(rows([['甲', null]]), 2.5), null) }) test('applyHeatToRows:只改命中的行,其余行(含正在编辑的成绩)原样保留', () => { const list = rows([['甲', null], ['乙', 1], ['丙', null]]) const next = applyHeatToRows(list, [1, 3], 7) assert.deepEqual(next.map((row) => row.heat), [7, 1, 7]) assert.deepEqual(list.map((row) => row.heat), [null, 1, null], '不能改到入参') const cleared = applyHeatToRows(next, [1], null) assert.deepEqual(cleared.map((row) => row.heat), [null, 1, 7]) // 未命中的批次筛选值不受影响。 assert.deepEqual(applyHeatToRows(list, [], 5).map((row) => row.heat), [null, 1, null]) })