This commit is contained in:
2026-07-21 16:43:57 +08:00 Unverified
parent 722bb6f016
commit 48bef02904
7 changed files with 168 additions and 5 deletions
+51
View File
@@ -156,6 +156,7 @@ try {
const clientSources = await Promise.all([
'app.js',
'src/client/admin-views.mjs',
'src/client/admission-views.mjs',
'src/client/candidate-views.mjs',
'src/client/public-views.mjs'
].map(file => readFile(resolve(root, file), 'utf8')));
@@ -170,6 +171,9 @@ try {
assert.match(appSource, /data-action="edit-exam"/, '考试草稿应提供编辑入口');
assert.match(appSource, /data-action="archive-exam"/, '超级管理员界面应提供不可逆考试归档入口');
assert.match(appSource, /data-action="refresh-results-cache"/, '成绩管理中心应提供 Redis 成绩缓存刷新入口');
assert.match(appSource, /data-action="bulk-placement-review"/, '招生学校投档审核应提供批量处理入口');
assert.match(appSource, /data-action="toggle-admission-account"/, '超级管理员应有招生学校账户启停入口');
assert.match(appSource, /data-action="reset-admission-account-password"/, '超级管理员应有招生学校账户密码重置入口');
assert.match(appSource, /归档不可撤销/, '归档前应明确提示成绩将永久锁定');
const { DatabaseSync } = await import('node:sqlite');
@@ -620,6 +624,53 @@ try {
assert.equal(qualificationPublication.rows.find(item => item.registrationNumber === candidateNumber).eligible, true, '资格公示应公开考生有无指标分配资格');
const invalidSpecialtyPlan = await admissionSchoolClient.request('/api/admission/plans', { method: 'POST', body: { examId: exam.id, categories: [{ code: 'bad', name: '错误特长类别', quota: 1, specialtyCategory: 'arts', specialtyType: 'track_field', indicatorAllocations: [] }] } });
assert.equal(invalidSpecialtyPlan.response.status, 400, '招生计划不得把艺术大类与体育小类混用');
const placementCandidates = qualificationExam.qualificationStatus.rows.slice(0, 3);
assert.equal(placementCandidates.length, 3, '批量投档测试至少需要三名候选考生');
const placementIds = placementCandidates.map((row, index) => `placement_bulk_test_${index + 1}`);
const placementCreatedAt = new Date().toISOString();
const placementWriter = new DatabaseSync(testDb);
const insertPlacement = placementWriter.prepare(`
INSERT INTO admission_records (id, kind, exam_id, user_id, school_id, status, payload_json, created_at, updated_at)
VALUES (?, 'placement', ?, ?, ?, 'school_review', ?, ?, ?)
`);
for (const [index, row] of placementCandidates.entries()) {
insertPlacement.run(
placementIds[index], exam.id, row.userId, admissionOnlySchool.data.school.id,
JSON.stringify({ categoryCode: index === 2 ? 'arts' : 'general', categoryName: index === 2 ? '美术特长生' : '普通生', totalScore: 620 - index, preferenceOrder: index + 1 }),
placementCreatedAt, placementCreatedAt
);
}
placementWriter.close();
const placementReviewLedger = await admissionSchoolClient.request('/api/admission/placements');
const seededPlacements = placementReviewLedger.data.placements.filter(item => placementIds.includes(item.id));
assert.equal(seededPlacements.length, 3, '招生学校投档台账应返回可供筛选、搜索的完整候选记录');
assert.ok(seededPlacements.every(item => item.examName === exam.name && item.candidate.registrationNumber), '投档台账应包含考试名称和考生报名号');
const bulkAccept = await admissionSchoolClient.request('/api/admission/placements/bulk', { method: 'POST', body: { ids: placementIds.slice(0, 2), decision: 'accept', note: '批量核验通过' } });
assert.equal(bulkAccept.response.status, 200, '招生学校应能批量接收多名投档考生');
assert.equal(bulkAccept.data.count, 2, '批量接收应返回实际处理人数');
const shortBulkWithdrawal = await admissionSchoolClient.request('/api/admission/placements/bulk', { method: 'POST', body: { ids: [placementIds[2]], decision: 'withdraw', note: '材料不符' } });
assert.equal(shortBulkWithdrawal.response.status, 400, '批量退档必须填写充分的特殊理由');
const bulkWithdrawal = await admissionSchoolClient.request('/api/admission/placements/bulk', { method: 'POST', body: { ids: [placementIds[2]], decision: 'withdraw', note: '专项资格证明材料复核不通过' } });
assert.equal(bulkWithdrawal.response.status, 200, '招生学校应能批量申请退档');
const processedPlacementLedger = await admissionSchoolClient.request('/api/admission/placements');
assert.ok(processedPlacementLedger.data.placements.filter(item => placementIds.slice(0, 2).includes(item.id)).every(item => item.status === 'admitted'), '批量接收后所选记录应全部进入拟录取状态');
assert.equal(processedPlacementLedger.data.placements.find(item => item.id === placementIds[2]).status, 'withdrawal_pending', '批量退档后记录应进入上级审核状态');
assert.equal((await admissionSchoolClient.request('/api/admission/placements/bulk', { method: 'POST', body: { ids: placementIds.slice(0, 2), decision: 'accept' } })).response.status, 409, '已处理记录不得被重复批量审核');
const admissionAccountLedger = await admin.request('/api/admin/admissions');
const managedAdmissionAccount = admissionAccountLedger.data.schoolAccounts.find(item => item.id === admissionAccount.data.account.id);
assert.equal(managedAdmissionAccount.schoolName, '海州市招生实验学校', '招生学校账户台账应显示绑定学校');
assert.equal(managedAdmissionAccount.active, true, '招生学校账户台账应返回真实启停状态');
const disabledAdmissionAccount = await admin.request(`/api/admin/admission-school-accounts/${managedAdmissionAccount.id}`, { method: 'PATCH', body: { active: false } });
assert.equal(disabledAdmissionAccount.data.account.active, false, '超级管理员应能停用招生学校账户');
assert.equal((await admissionSchoolClient.request('/api/admission/context')).response.status, 401, '停用招生学校账户应立即使既有会话失效');
assert.equal((await admin.request(`/api/admin/admission-school-accounts/${managedAdmissionAccount.id}`, { method: 'PATCH', body: { active: true } })).data.account.active, true, '超级管理员应能重新启用招生学校账户');
const resetAdmissionPassword = await admin.request(`/api/admin/admission-school-accounts/${managedAdmissionAccount.id}/reset-password`, { method: 'POST' });
assert.match(resetAdmissionPassword.data.temporaryPassword, /^Reset-/, '重置招生学校账户密码应返回一次性临时密码');
assert.equal((await createClient().request('/api/auth/login', { method: 'POST', body: { username: managedAdmissionAccount.username, password: 'Admission123!' } })).response.status, 401, '招生学校账户重置后原密码应失效');
assert.equal((await createClient().request('/api/auth/login', { method: 'POST', body: { username: managedAdmissionAccount.username, password: resetAdmissionPassword.data.temporaryPassword } })).response.status, 200, '招生学校账户应能使用临时密码重新登录');
const createdExamInspector = new DatabaseSync(testDb, { readOnly: true });
const createdExamPartition = createdExamInspector.prepare('SELECT * FROM exam_data_partitions WHERE exam_id = ?').get(exam.id);
assert.ok(createdExamPartition, '创建考试时应同步登记该场考试的专属物理表');