// The image library: every picture this account generated, across all three // features, with thumbnails rather than originals and a delete that removes the // bytes and not only the row. const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const read = file => fs.readFileSync(path.join(__dirname, '..', file), 'utf8'); test('the listing is the owner’s own finished images, newest first', () => { const route = read('src/routes/generatedImages.js'); assert.match(route, /router\.get\('\/generated-images'/); // Scoped by owner in the statement, not filtered afterwards. assert.match(route, /owner_id=\$1 AND stage='done'/); assert.match(route, /ORDER BY created_at DESC/); // Unfinished jobs have nothing to show; listing one puts a broken frame in a // gallery. assert.match(route, /Only finished jobs/); }); test('paging is keyset, not OFFSET', () => { // A gallery that grows while you scroll repeats or skips a row under OFFSET. const route = read('src/routes/generatedImages.js'); assert.match(route, /created_at < \$/); assert.doesNotMatch(route, /\bOFFSET\s+\$?\d/i, 'no SQL OFFSET; the word in a comment is fine'); assert.match(route, /nextBefore/); }); test('a prompt that cannot be decrypted does not cost the gallery its picture', () => { const route = read('src/routes/generatedImages.js'); assert.match(route, /try \{ prompt = String\(encryption\.decryptString/); assert.match(route, /catch \(e\) \{ prompt = ''; \}/); }); test('deleting removes the bytes before the row, and refuses if it cannot', () => { // A row without its object is a broken image in a gallery; an object without // its row is only wasted space. Only one of those is allowed to happen. const lib = read('src/utils/generatedImages.js'); assert.match(lib, /async function discard\(id, owner\)/); const discard = lib.slice(lib.indexOf('async function discard')); const storageFirst = discard.indexOf('getStorage().remove(id)'); const rowSecond = discard.indexOf('DELETE FROM generated_image_jobs'); assert.ok(storageFirst > -1 && rowSecond > storageFirst, 'storage must be removed first'); assert.match(discard.slice(0, 800), /throw failure\(503, 'Image storage is unavailable; nothing was deleted'\)/); }); test('deleting removes the previews too, not just the original', () => { // Both derived widths live under their own prefix. Missing them leaves paid-for // bytes in the bucket that are still readable. const storage = read('src/utils/generatedImageStorage.js'); assert.match(storage, /async remove\(id\)/); assert.match(storage, /\['assets\/' \+ id\]\.concat\(THUMB_WIDTHS\.map/); }); test('the thumbnail widths have one definition', () => { // Two copies drift: a width written but never deleted is paid for once and // then left in the bucket forever. const storage = read('src/utils/generatedImageStorage.js'); const lib = read('src/utils/generatedImages.js'); assert.match(storage, /const THUMB_WIDTHS = Object\.freeze\(\[256, 640\]\)/); assert.match(lib, /const \{ THUMB_WIDTHS \} = storageUtil/); assert.doesNotMatch(lib, /THUMB_WIDTHS = Object\.freeze/, 'not defined twice'); assert.deepEqual(require('../src/utils/generatedImages').THUMB_WIDTHS, [256, 640]); }); test('a borrowed id deletes nothing rather than someone else’s picture', () => { const route = read('src/routes/generatedImages.js'); assert.match(route, /SELECT id FROM generated_image_jobs WHERE id=\$1 AND owner_id=\$2/); const lib = read('src/utils/generatedImages.js'); assert.match(lib, /DELETE FROM generated_image_jobs WHERE id=\$1 AND owner_id=\$2/); }); test('the grid asks for the stored preview, not the original', () => { // Thirty tiles at full size is thirty full-size downloads. const ui = read('public/js/myResources.js'); assert.match(ui, /setAttribute\('data-image-thumb', '256'\)/); assert.match(ui, /hydrateImage\(img, image\.imageUrl\)/, 'fetched through the authenticated loader, never a bare src'); }); test('the caption is a model-written prompt and never reaches the page as HTML', () => { const ui = read('public/js/myResources.js'); const tile = ui.slice(ui.indexOf('function imageTile'), ui.indexOf('function openImage')); assert.match(tile, /prompt\.textContent = image\.prompt/); assert.doesNotMatch(tile, /innerHTML/); }); test('the images tab loads on first open, not on page load', () => { // Most visits never open it, and it costs a query plus a thumbnail per tile. const ui = read('public/js/myResources.js'); assert.match(ui, /if \(!docs && !imagesLoaded\) loadImages\(true\)/); }); test('the library is in My Resources and never in the Clinical Assistant', () => { // Assistant images appear here — that was the point — but the assistant page // does not grow a gallery of its own. const ui = require('fs').readFileSync(require('path').join(__dirname, '..', 'public/js/myResources.js'), 'utf8'); assert.match(ui, /function loadImages/); const assistantFiles = require('fs').readdirSync(require('path').join(__dirname, '..', 'public/js/assistant')); for (const file of assistantFiles) { const src = require('fs').readFileSync(require('path').join(__dirname, '..', 'public/js/assistant', file), 'utf8'); assert.doesNotMatch(src, /\/api\/generated-images\?/, file + ' must not list the library'); } const component = require('fs').readFileSync(require('path').join(__dirname, '..', 'public/components/my-resources.html'), 'utf8'); assert.match(component, /id="mr-images-panel"/); }); test('downloading goes through the authenticated blob, not a bare link', () => { // A mobile client's session is a bearer token an cannot send, and the // asset is served no-store on purpose. const ui = require('fs').readFileSync(require('path').join(__dirname, '..', 'public/js/myResources.js'), 'utf8'); const save = ui.slice(ui.indexOf('function saveImage'), ui.indexOf('function deleteImage')); assert.match(save, /m\.privateImageBlob\(image\.imageUrl\)/); assert.match(save, /URL\.revokeObjectURL/, 'the object URL is released'); assert.doesNotMatch(save, /href = image\.downloadUrl/); }); test('the tile actions stay reachable without a pointer', () => { // Hiding delete behind :hover puts it out of reach on touch and keyboard. const css = require('fs').readFileSync(require('path').join(__dirname, '..', 'public/css/styles.css'), 'utf8'); const block = css.slice(css.indexOf('.img-tile-actions')); assert.match(block.slice(0, 400), /@media \(hover:hover\)/, 'only hidden where hover exists'); assert.match(css, /\.img-tile:focus-within \.img-tile-actions/); assert.match(css, /prefers-reduced-motion/); }); test('the picture itself is a button, so opening it needs no invented key handling', () => { const ui = require('fs').readFileSync(require('path').join(__dirname, '..', 'public/js/myResources.js'), 'utf8'); const tile = ui.slice(ui.indexOf('function imageTile'), ui.indexOf('function openImage')); assert.match(tile, /frame\.type = 'button'/); assert.match(tile, /loading = 'lazy'/, 'a grid of thumbnails should not all fetch at once'); }); test('the lightbox closes on Escape and on the backdrop, and restores focus', () => { const ui = require('fs').readFileSync(require('path').join(__dirname, '..', 'public/js/myResources.js'), 'utf8'); const box = ui.slice(ui.indexOf('function openImage'), ui.indexOf('function saveImage')); assert.match(box, /aria-modal/); assert.match(box, /e\.key === 'Escape'/); assert.match(box, /if \(e\.target === overlay\) dismiss\(\)/, 'a click on the picture must not close it'); assert.match(box, /lastFocus\.focus\(\)/); assert.match(box, /removeEventListener\('keydown', onKey\)/, 'no listener left behind'); });