const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const source = fs.readFileSync(path.join(__dirname, 'add-week3-jetson-camera.cjs'), 'utf8') .replace(/main\(\)\.catch[\s\S]*$/, 'module.exports = { main };'); const ids = ['4eb01197-7605-4c3b-9a92-f9a3942050bd', '8e296f81-8445-455c-b1a2-47f2ce9099a4', '26d46589-31a5-4d94-b0e0-385a12c41721']; const image = 'future-nav2-camera-test:humble-jetson_orin'; const release = { runtimeImages: [{ image, url: 'https://example.test/image', checksumSha256: 'a'.repeat(64) }], prebuiltArtifacts: [{ architecture: 'arm64', url: 'https://example.test/prebuilt', checksumSha256: 'b'.repeat(64) }] }; function fixture() { return ids.map((id, i) => ({ id, title: `Course ${i}`, slug: i ? `course-${i}` : 'futuredrive-autonomy-basic', weeks: [{ id: `${i}-w1`, week: 1, modules: [] }, { id: `${i}-w2`, week: 2, modules: [] }, { id: `${i}-w3`, week: 3, modules: [{ id: `${i}-mk`, name: 'NAV2-MKMini', folderName: 'mkmini', sortOrder: 1 }] }], environmentProfiles: ['local', 'remote-jetson'].map(target => ({ id: `${i}-${target}`, week: 3, config: { target, otherSetting: 'preserve', moduleSet: { includeUnlisted: false, modules: [{ id: `${i}-mk` }] }, runtimeImages: [{ image: 'future-nav2:humble-jetson_orin', url: 'existing' }] } })), })); } function harness(apply, initial = fixture()) { let state = structuredClone(initial); const db = { course: { findMany: async () => structuredClone(state) }, courseWeekModule: { create: async ({ data }) => { const week = state.flatMap(c => c.weeks).find(w => w.id === data.weekId); const m = { id: `${week.id}-camera`, ...structuredClone(data) }; week.modules.push(m); return structuredClone(m); }, update: async ({ where, data }) => { const m = state.flatMap(c => c.weeks.flatMap(w => w.modules)).find(m => m.id === where.id); Object.assign(m, structuredClone(data)); return structuredClone(m); }, }, courseEnvironmentProfile: { update: async ({ where, data }) => { const p = state.flatMap(c => c.environmentProfiles).find(p => p.id === where.id); Object.assign(p, structuredClone(data)); return structuredClone(p); } }, $transaction: async callback => { const before = structuredClone(state); try { return await callback(db); } catch (error) { state = before; throw error; } }, }; const context = { module: { exports: {} }, URL, structuredClone, console: { log() {} }, fetch: async () => ({ ok: true }), AbortSignal, process: { argv: apply ? ['--apply'] : [], env: { DATABASE_URL: 'postgresql://user:password@localhost/lms' } }, require: name => { if (name === 'dotenv') return { config() {} }; if (name === '@prisma/client') return { PrismaClient: function () { return db; } }; if (name === 'node:fs') return { readFileSync: () => JSON.stringify(release), mkdirSync() {}, writeFileSync() {} }; return require(name); }, }; vm.runInNewContext(source, context); return { main: context.module.exports.main, state: () => structuredClone(state) }; } (async () => { const dry = harness(false); await dry.main(); assert.deepEqual(dry.state(), fixture()); const applied = harness(true); await applied.main(); for (const c of applied.state()) { assert.equal(c.weeks.find(w => w.week === 3).modules.length, 2); assert.equal(c.weeks.find(w => w.week === 3).modules.find(m => m.name === 'NAV2-Camera').category, 'Camera'); assert.equal(c.environmentProfiles.find(p => p.config.target === 'remote-jetson').config.moduleSet.modules.length, 2); assert.equal(c.environmentProfiles.find(p => p.config.target === 'local').config.moduleSet.modules.length, 1); } const once = applied.state(); await applied.main(); assert.deepEqual(applied.state(), once); const broken = fixture(); broken[0].environmentProfiles = broken[0].environmentProfiles.filter(p => p.config.target === 'local'); const rejected = harness(true, broken); await assert.rejects(rejected.main()); assert.deepEqual(rejected.state(), broken); console.log('Camera registration dry-run, preservation, idempotency and profile guard tests passed.'); })().catch(error => { console.error(error); process.exitCode = 1; });