import assert from 'node:assert/strict' import { test } from 'node:test' import { addRecord, advanceMockResults, createInitialState, formatTime, markRead, recognizePhotos, students, validateUpload } from '../src/data/workbench.ts' const photo = (id) => ({ id, path: '', name: id, demo: true }) const metadata = { name: '测试卷', year: '2026', paperType: '模拟', province: '浙江省' } test('appending and deleting photos retains question edits and renumbers pages', () => { const first = recognizePhotos([photo('a'), photo('b')]) first[0].content = '修改后的题干' first[0].options[0] = '修改后的选项' first[0].isWrong = true first[0].tags.push('几何') const appended = recognizePhotos([photo('a'), photo('b'), photo('c')], first) assert.equal(appended.length, 9) assert.equal(appended[0].content, '修改后的题干') assert.equal(appended[0].options[0], '修改后的选项') assert.equal(appended[0].isWrong, true) assert.ok(appended[0].tags.includes('几何')) const removed = recognizePhotos([photo('b'), photo('c')], appended) assert.equal(removed.length, 6) assert.equal(removed[0].page, 1) assert.equal(removed[3].page, 2) assert.deepEqual( removed.map((q) => q.num), [1, 2, 3, 4, 5, 6] ) }) test('required fields and student identity work across different classes with identical names', () => { assert.notEqual(students[0].id, students[3].id) assert.equal(students[0].name, students[3].name) assert.equal(validateUpload('mistakes', [photo('a')], [], metadata), '请先关联学生') assert.equal(validateUpload('mistakes', [photo('a')], [students[0], students[3]], metadata), '') for (const field of ['name', 'year', 'paperType', 'province']) assert.ok(validateUpload('papers', [photo('a')], [], { ...metadata, [field]: '' })) assert.equal(validateUpload('answers', [], [], metadata), '请先添加照片') }) test('submission, approval and explanation completion are idempotent and survive serialization', () => { const now = Date.now() let state = createInitialState() const record = { id: 'new-paper', kind: 'papers', ...metadata, photos: [photo('a')], questions: recognizePhotos([photo('a')]), students: [], submittedAt: new Date(now).toISOString(), reviewStatus: 'pending' } state = addRecord(state, record) assert.equal(addRecord(state, record), state) assert.equal(state.records[0].reviewStatus, 'pending') state = JSON.parse(JSON.stringify(state)) const complete = advanceMockResults(state, now + 21000) assert.equal(complete.records[0].reviewStatus, 'approved') assert.equal(complete.notices.filter((n) => n.id === 'approved-new-paper').length, 1) assert.equal( complete.explanations.find((v) => v.status === 'waiting'), undefined ) assert.equal(advanceMockResults(complete, now + 30000), complete) assert.equal(state.records[0].reviewStatus, 'pending', 'previous state must remain immutable') }) test('opening an explanation synchronizes its message badge, while unrelated notices stay unread', () => { const initial = createInitialState() const read = markRead(initial, initial.explanations[0].id) assert.equal(read.explanations[0].unread, false) assert.equal(read.notices[0].unread, false) assert.equal(read.notices[1].unread, true) const all = markRead(read) assert.ok(all.notices.every((n) => !n.unread)) assert.ok(all.explanations.every((v) => !v.unread)) }) test('China time display stays stable across device timezones', () => { assert.equal(formatTime('2026-10-26T01:05:00Z'), '2026/10/26 09:05') assert.equal(formatTime('invalid'), '待同步') })