'use strict'; // The image copies top-level modules by name; a new module missing there crashes the reader at start. const test = require('node:test'), assert = require('node:assert/strict'); const fs = require('fs'), path = require('path'); const root = path.resolve(__dirname, '..'); test('every top-level module the reader requires is copied into the image', () => { const dockerfile = fs.readFileSync(path.join(root, 'Dockerfile'), 'utf8'); const copied = new Set(dockerfile.split('\n').filter(line => line.startsWith('COPY ')).flatMap(line => line.split(/\s+/).slice(1, -1))); const pending = ['server.js'], seen = new Set(); while (pending.length) { const file = pending.pop(); if (seen.has(file)) continue; seen.add(file); for (const [, name] of fs.readFileSync(path.join(root, file), 'utf8').matchAll(/require\('\.\/([\w.-]+?)(?:\.js)?'\)/g)) { // A folder (e.g. ./greatest) is copied as a whole; only single files need their own name in COPY. if (fs.existsSync(path.join(root, name)) && fs.statSync(path.join(root, name)).isDirectory()) assert.ok(copied.has(name), `${name}/ is not copied`); else pending.push(name + '.js'); } } // .dockerignore is an allowlist too: a file not let through never reaches COPY. const allowed = new Set(fs.readFileSync(path.join(root, '.dockerignore'), 'utf8').split(/\r?\n/).filter(line => line.startsWith('!')).map(line => line.slice(1))); for (const file of seen) { assert.ok(copied.has(file), `${file} is required but not copied by the Dockerfile`); assert.ok(allowed.has(file), `${file} is required but not allowed through .dockerignore`); } });