import type { Issue } from '../gitea/types' import { describe, expect, it } from 'vitest' import { applyIssueUpdate } from './kanbanUpdates' function issue(number: number, extra: Partial = {}): Issue { return { id: `repo#${number}`, number, title: `Issue ${number}`, column: 'review', htmlUrl: '', ...extra } } describe('applyIssueUpdate', () => { it('按 number 修改并删除 null 字段,不改变原数组、卡片或补丁', () => { const first = Object.freeze(issue(1, { pr: '12', reviewSessionId: 'session' })) const second = Object.freeze(issue(2)) const current = Object.freeze([first, second]) const patch = Object.freeze({ column: 'done', pr: null, reviewSessionId: null }) const result = applyIssueUpdate(current, { type: 'issue/patch', issueNumber: 1, patch }) expect(result).toEqual([issue(1, { column: 'done' }), second]) expect(result[0]).not.toHaveProperty('pr') expect(result[0]).not.toHaveProperty('reviewSessionId') expect(result[1]).toBe(second) expect(first.pr).toBe('12') expect(patch.pr).toBeNull() }) it('未知工单的补丁不新增卡片', () => { const current = [issue(1)] expect(applyIssueUpdate(current, { type: 'issue/patch', issueNumber: 2, patch: { column: 'done' } })).toEqual(current) }) it('append 同号卡片原位整对象替换,不保留旧字段', () => { const current = Object.freeze([issue(1), issue(2, { pr: '12' }), issue(3)]) const replacement = Object.freeze(issue(2, { id: 'new-id', column: 'done' })) const result = applyIssueUpdate(current, { type: 'issue/append', issue: replacement }) expect(result).toEqual([current[0], replacement, current[2]]) expect(result[1]).not.toHaveProperty('pr') expect(current[1].pr).toBe('12') }) it('append 新编号即追加,不按 id 去重', () => { const first = issue(1) const next = issue(2, { id: first.id }) expect(applyIssueUpdate([first], { type: 'issue/append', issue: next })).toEqual([first, next]) }) it('remove 删除所有同号卡片,不改变原数组', () => { const current = Object.freeze([issue(1), issue(2), issue(1, { id: 'another' })]) expect(applyIssueUpdate(current, { type: 'issue/remove', issueNumber: 1 })).toEqual([current[1]]) expect(current).toHaveLength(3) }) })