feat: add weekly curriculum cover image upload and display
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
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; });
|
||||
Reference in New Issue
Block a user