import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { deployBoardSeries, patchPublicBoardCode } from '../scripts/deploy-workshop-board-series.mjs'; const code = `const fields={artifact:'objectId rawText report ownerName version dataMode dashboardState workshopKey'};function ksfGetPublicBoard(){};/*existing account wrapper retained*/`; function fixture() { let row = { objectId: 'fn', name: 'ksfGetPublicBoard', path: '/ksfGetPublicBoard', code, ACL: {} }; const puts = []; return { puts, get row() { return row; }, request: async (method, route, body) => { if (route.startsWith('/classes/Function?')) return { results: [structuredClone(row)] }; if (route === '/schemas') return { results: [{ className: 'KfArtifact', fields: { reportSeriesKey: { type: 'String' } } }] }; if (method === 'PUT') { puts.push(body);row = { ...row, ...body }; } return structuredClone(row); } }; } test('补字段保持当前完整函数与身份包装,默认只读;正常应用与重复执行不重写', async () => { const f = fixture(), dir = fs.mkdtempSync(path.resolve('work/series-deploy-test-')); const plan = await deployBoardSeries({ request: f.request });assert.equal(plan.changed, true);assert.equal(f.puts.length, 0); const result = await deployBoardSeries({ request: f.request, manifest: plan, apply: true, backupDir: dir }); assert.equal(result.readbackVerified, true);assert.equal(result.schemaAndPermissionsPreserved, true);assert.equal(f.puts.length, 1); assert.equal(f.row.code, code.replace('version dataMode', 'version reportSeriesKey dataMode')); const repeat = await deployBoardSeries({ request: f.request, manifest: plan, apply: true, backupDir: dir }); assert.equal(repeat.alreadyDeployed, true);assert.equal(f.puts.length, 1); }); test('未知合同、模糊字段集与部署基线变化在写入前拒绝', async () => { assert.throws(() => patchPublicBoardCode('ksfGetPublicBoard'), { code: 'PUBLIC_ARTIFACT_WHITELIST_AMBIGUOUS' }); assert.throws(() => patchPublicBoardCode(code + code), { code: 'PUBLIC_ARTIFACT_WHITELIST_AMBIGUOUS' }); const f = fixture(), plan = await deployBoardSeries({ request: f.request }); await assert.rejects(() => deployBoardSeries({ request: f.request, apply: true, manifest: { ...plan, beforeSha256: 'changed' }, backupDir: path.resolve('work') }), { code: 'DEPLOYMENT_BASELINE_CHANGED' }); assert.equal(f.puts.length, 0); }); test('不确定写结果先回读,再恢复本次字段变更;不覆盖并发版本', async () => { for (const concurrent of [false, true]) { const f = fixture(), plan = await deployBoardSeries({ request: f.request });let once = false; const request = async (m, r, b) => { const result = await f.request(m, r, b); if (m === 'PUT' && !once) { once = true;if (concurrent) await f.request('PUT', r, { code: f.row.code + '//another publisher' });throw Error('response lost'); } return result; }; await assert.rejects(() => deployBoardSeries({ request, manifest: plan, apply: true, backupDir: fs.mkdtempSync(path.resolve('work/series-deploy-failure-')) }), e => { assert.equal(e.receipt.rollbackComplete, !concurrent);return true; }); assert.equal(f.row.code === code, !concurrent); } });