|
- import fs from 'node:fs';
- import assert from 'node:assert/strict';
- import { login, call } from './role-permissions-live.mjs';
-
- // Read-only final audit. Never publishes or changes an existing assignment.
- const dir = 'reports/bridge-005-authoring-20260916';
- const read = name => JSON.parse(fs.readFileSync(`${dir}/${name}.json`, 'utf8'));
- const fixture = read('pilot');
- const before = read('original-assignments');
- const session = await login('root');
- async function api(endpoint) {
- const result = await call(`tran/v1/${endpoint}`, session.accessToken);
- assert.equal(result.status, 200, endpoint);
- return result.data;
- }
- const originals = [];
- for (const id of ['86', '87', '88', '89']) {
- const current = await api(`teaching/assignments/${id}`);
- delete current.serverNow;
- delete before[id].serverNow;
- assert.deepEqual(current, before[id], `Original assignment ${id} changed`);
- const project = await api(`content/projects/${current.contentProjectId}`);
- assert.equal(project.project.publishedVersionId, current.contentVersionId);
- originals.push({ id, code: current.code, versionId: current.contentVersionId, unchanged: true });
- }
- const task = await api(`teaching/assignments/${fixture.taskId}`);
- assert.equal(task.status, 'DRAFT');
- assert.equal(task.contentVersionId, fixture.trainingVersionId);
- const training = await api(`content/projects/${fixture.trainingId}`);
- const scene = await api(`content/projects/${fixture.sceneId}`);
- assert.equal(scene.project.publishedVersionId, fixture.sceneVersionId);
- const content = training.currentVersion.content;
- assert.equal(content.runtime?.scriptId, undefined);
- assert.notEqual(content.runtime?.definitionSource, 'SCRIPT');
- assert.equal(content.steps.length, 5);
- assert.equal(content.steps.flatMap(s => s.action.mechanism.steps).length, 6);
- assert.equal(content.steps[2].action.mechanism.steps[0].deltaDegrees, 90);
- assert.equal(scene.currentVersion.content.objects.flatMap(o => o.modelPartTargets || []).length, 7);
- assert.ok(training.currentVersion.dependencies.some(d => d.relationType === 'TRAINING_SCENE' && d.targetVersionId === fixture.sceneVersionId));
- const media = content.steps.map((s, index) => {
- const codes = [s.audio.narrationVideoAssetCode, s.audio.narrationAssetCode];
- for (const code of codes) assert.ok(training.currentVersion.assets.some(a => a.code === code && a.status === 'READY'), code);
- return { step: index + 1, video: codes[0], audio: codes[1] };
- });
- const edited = await api(`content/projects/${fixture.trainingId}/versions/543`);
- assert.equal(edited.content.steps[2].action.mechanism.steps[0].deltaDegrees, 75);
- const evidence = ['editor-result', 'editor-result-90', 'runtime-edited-75', 'runtime-final-90-lan', 'original-tasks-regression', 'original-005-full-regression'];
- for (const name of evidence) assert.equal(read(name).pass, true, name);
- const audit = { pass: true, checkedAt: new Date().toISOString(), fixture, originals, steps: 5, operations: 6, partTargets: 7, editedAngle: 75, restoredAngle: 90, media, evidence };
- fs.writeFileSync(`${dir}/audit.json`, JSON.stringify(audit, null, 2));
- const links = evidence.map(name => `<li><a href="${name}.json">${name}</a>:通过</li>`).join('');
- fs.writeFileSync(`${dir}/index.html`, `<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>005 场景与编排验收</title><style>body{font:16px/1.7 system-ui;margin:32px auto;padding:0 24px;max-width:1100px;color:#173d38;background:#f4f8f7}a{color:#087f70}section{background:white;padding:22px;border:1px solid #d5e7e3;border-radius:12px;margin:20px 0}img{width:100%;border-radius:8px}h1{font-size:28px}li{margin:8px 0}</style><h1>005 液压泵应急切换:场景制作 → 训练编排 → 教学预览</h1><p>验收时间:${audit.checkedAt}。独立验证工程,原四个已发布任务数据和版本完整对比一致。</p><section><h2>入口</h2><ul><li><a href="http://127.0.0.1:6180/content/scenes/${fixture.sceneId}/edit">场景制作:整车、地形、七个真实部件目标</a></li><li><a href="http://127.0.0.1:6180/content/training-projects/${fixture.trainingId}/legacy">训练编排:五步、六个机构动作及逐步数字人讲解</a></li><li><a href="http://127.0.0.1:6180${fixture.previewUrl}">独立教学预览</a>(草稿任务,不替换现有学员入口)</li></ul></section><section><h2>验证结果</h2><p>实际编辑场景并发布后,训练重新绑定已发布场景。球阀从 90° 改为 75°,保存、刷新、发布后,完成全部六次鼠标操作及服务端预览校验;随后恢复 90° 并在局域网完整复测。五段视频和五段语音已作为训练资产绑定。</p><p>原 005 完整流程回归、原四任务初始场景回归均通过。前端构建与合同测试、后端单元测试和打包通过。当前验证仅覆盖教员预览,不代表新增学员成绩验收。</p><ul>${links}<li><a href="audit.json">任务、版本、讲解资产与原数据核对结果</a></li></ul></section><section><h2>场景制作</h2><img src="scene-editor.png" alt="005 场景制作"></section><section><h2>训练编排</h2><img src="training-editor.png" alt="005 训练编排"></section><section><h2>球阀操作与互救开关</h2><img src="task-116-step-3.png" alt="球阀操作"><img src="task-116-step-5.png" alt="互救开关操作"></section></html>`);
- console.log('AUDIT PASS: original tasks unchanged; 005 authoring and runtime verified');
|