workshop-board-series-deploy.test.mjs 3.3 KB

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