|
- import fs from 'node:fs';
- import path from 'node:path';
- import crypto from 'node:crypto';
- import assert from 'node:assert/strict';
- import { fileURLToPath } from 'node:url';
- import { login, root } from './role-permissions-live.mjs';
- export { login };
- export const work = path.resolve(root, '../.codex-tmp/scene-closure-20260906');
- export const report = path.join(root, 'ute2e/reports/scene-editor-closure-20260906');
- export const fixturePath = path.join(work, 'fixtures.json');
- fs.mkdirSync(work, { recursive: true }); fs.mkdirSync(report, { recursive: true });
- export const fixture = () => JSON.parse(fs.readFileSync(fixturePath, 'utf8'));
- export function writeFixture(value) {
- const lock = `${fixturePath}.lock`;
- let handle;
- for (let attempt=0; attempt<100; attempt++) {
- try { handle=fs.openSync(lock,'wx'); break; }
- catch (error) { if(error.code!=='EEXIST')throw error; Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20); }
- }
- if(handle===undefined)throw new Error('Scene fixture registry is busy');
- try {
- const existing=fs.existsSync(fixturePath)?fixture():{};
- const merged={...existing,...value};
- for(const key of ['projects','extraUsers','roleIds']) {
- const entries=[...(existing[key]||[]),...(value[key]||[])];
- if(entries.length)merged[key]=[...new Map(entries.map(item=>[typeof item==='object'?String(item.id):String(item),item])).values()];
- }
- fs.writeFileSync(fixturePath,JSON.stringify(merged,null,2));
- } finally { fs.closeSync(handle); fs.unlinkSync(lock); }
- }
- export const hash = value => crypto.createHash('sha256').update(JSON.stringify(value)).digest('hex');
- export async function call(endpoint, token, body, method = body === undefined ? 'GET' : 'POST') {
- const response = await fetch(`http://127.0.0.1:6180/api/${endpoint}`, {
- method, headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) },
- body: body === undefined ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(120000),
- });
- const value = await response.json();
- return { status: response.status, code: value.code, data: value.data, message: value.message };
- }
- export async function teacherLogin() {
- const user = fixture().user;
- const response = await call('auth/v1/auth/login', null, { userId: user.id, password: user.password, roleCode: 'teacher', departmentId: user.departmentId, rememberMe: false });
- assert.equal(response.status, 200, 'Scene review teacher signs in');
- return response.data;
- }
- export function registerProject(project) {
- const f = fixture();
- if (!f.projects.some(p => p.id === project.id)) f.projects.push({ id: project.id, name: project.name, code: project.code, ownerId: project.ownerId });
- writeFixture(f);
- }
- export async function freshScene(token, key = 'fox', suffix = '') {
- const f = fixture(), source = f.originals[key];
- const result = await call(`tran/v1/content/projects/${source.id}/copy`, token, { name: `场景闭环-${key}-${suffix}-${Date.now()}`, version: source.version });
- assert.equal(result.status, 200, `Copy scene: ${result.message}`);
- registerProject(result.data.project);
- return result.data;
- }
- async function prepare() {
- if (fs.existsSync(fixturePath) && !fixture().cleanedAt && fixture().user) throw new Error('Scene fixtures already active; reuse existing fixtures');
- const session = await login('root'), token = session.accessToken;
- const f = { createdAt: new Date().toISOString(), originals: {}, projects: [], user: null };
- writeFixture(f);
- try {
- for (const [key, id] of [['fox', '160'], ['truck', '159'], ['foxModel', '157'], ['truckModel', '156']]) {
- const original = await call(`tran/v1/content/projects/${id}`, token);
- assert.equal(original.status, 200);
- f.originals[key] = { id, name: original.data.project.name, version: original.data.project.version, hash: hash(original.data) };
- fs.writeFileSync(path.join(work, `original-${key}.json`), JSON.stringify(original.data, null, 2));
- writeFixture(f);
- }
- const me = (await call('auth/v1/auth/me', token)).data;
- const roles = (await call('auth/v1/roles/all', token)).data;
- const teacher = (roles.records || roles).find(r => r.code === 'teacher');
- const password = `Sc!${crypto.randomBytes(7).toString('hex')}`;
- const made = await call('auth/v1/users', token, {
- username: `rv_scene_${crypto.randomBytes(4).toString('hex')}`, displayName: '场景编辑器闭环验收',
- departmentId: me.departmentId, roleIds: [teacher.id], defaultRoleId: teacher.id,
- enabled: true, password, remark: 'UTE2E-SCENE-CLOSURE-20260906',
- });
- assert.equal(made.status, 200, `Temporary teacher: ${made.message}`);
- f.user = { ...made.data, password }; writeFixture(f);
- console.log(JSON.stringify({ prepared: true, originals: Object.fromEntries(Object.entries(f.originals).map(([k,v])=>[k,v.id])) }));
- } finally { await call('auth/v1/auth/logout', token, {}).catch(()=>{}); }
- }
- if (process.argv[1] === fileURLToPath(import.meta.url)) prepare().catch(e => { console.error(e.message); process.exitCode = 1; });
|