// Source articles for AI generation are held whole in memory to be parsed, so // the ceiling matters, and the declared type is chosen by whoever uploads. const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const route = fs.readFileSync(path.join(__dirname, '..', 'src/routes/learningAI.js'), 'utf8'); test('the per-file ceiling is 10 MB, in one place', () => { assert.match(route, /var MAX_SOURCE_BYTES = 10 \* 1024 \* 1024;/); assert.match(route, /fileSize: MAX_SOURCE_BYTES/); assert.doesNotMatch(route, /100 \* 1024 \* 1024/, 'the old 100 MB ceiling is gone'); }); test('the type and the extension must both be allowed, not either', () => { // It was `mime OR extension`, so naming a file .pdf was enough on its own. assert.match(route, /ALLOWED_SOURCE_TYPES\.includes\(file\.mimetype\) && ALLOWED_SOURCE_EXTENSIONS\.test\(file\.originalname\)/); assert.doesNotMatch(route, /allowed\.includes\(file\.mimetype\) \|\| file\.originalname\.match/); }); test('the bytes are sniffed once the buffer exists, before anything parses them', () => { // multer filters on headers alone, before any byte has arrived. assert.match(route, /function verifySources\(files\)/); assert.match(route, /fileType\.matches\(file\.mimetype, file\.buffer\)/); assert.match(route, /is not the file type it claims to be/); // And it runs ahead of extraction, not after. const call = route.indexOf('verifySources(req.files)'); const extract = route.indexOf('await extractText('); assert.ok(call > -1 && call < extract, 'verification must precede extraction'); }); test('the size is re-checked on the buffer, not trusted from the header', () => { assert.match(route, /file\.size > MAX_SOURCE_BYTES/); assert.match(route, /is larger than 10 MB/); }); test('what the screen promises matches what the server accepts', () => { const cms = fs.readFileSync(path.join(__dirname, '..', 'public/components/cms.html'), 'utf8'); assert.match(cms, /max 10 MB each, up to 10 files/); assert.doesNotMatch(cms, /100 MB/); });