Files
picpeak/backend/__tests__/services/uploadSettingsMaxFileSize.test.js
T
Paul Nothaft 1e38d84808 fix(uploads): apply configured max file size to guest uploads (#613 follow-up)
The admin's Settings → General → "Max File Size (MB)" value
(general_max_file_size_mb) never applied to guest gallery uploads — the guest
route hardcoded multer's per-file cap at 50MB (gallery.js) and the guest UI
hardcoded the same 50MB client-side guard and "max 50MB" hint text. So a guest
could not upload a large video even when the admin raised the limit (reported by
mat1990dj on #613). Same class as the file-count miss fixed in #614, for size.

- uploadSettings.js: new getMaxFileSizeMb()/getMaxFileSizeBytes() reading
  general_max_file_size_mb (default 50MB, cached 60s, clamped to a 10GB ceiling),
  mirroring getMaxFilesPerUpload.
- gallery.js (guest upload): multer limits.fileSize now resolves from the
  setting; a LIMIT_FILE_SIZE error returns an actionable "max N MB" message.
- publicSettings.js: exposes general_max_file_size_mb (default 50) so the gallery
  UI can render the real limit and guard client-side before an oversized POST.
- UserPhotoUpload.tsx: reads the limit, uses it for the client-side size guard,
  and passes it to the requirements hint. The "max 50MB" literal in
  upload.fileRequirements is now interpolated ({{sizeLimit}}) across all 8
  locales; adds upload.fileTooLarge (en/de; others fall back to en).

Scope: guest path only (the reported gap). The admin path keeps its generous
10GB cap — admins are trusted and default 50MB would otherwise regress large
admin video uploads. Format and batch-size limits already work correctly and are
untouched. Adds SQLite-backed unit tests for the new getter.

Verified end-to-end on a booted instance: admin sets 500MB → persisted → public
settings exposes 500 → guest multer sources its cap from it.
2026-07-17 20:41:27 +02:00

66 lines
2.4 KiB
JavaScript

/**
* Unit tests for the per-file upload size limit getter (general_max_file_size_mb),
* added so the admin's "Max File Size (MB)" setting applies to guest uploads
* (#613 follow-up — mat1990dj). Real in-memory SQLite app_settings so the
* read/parse/cache path runs exactly as in production.
*/
const knex = require('knex');
let db;
let svc;
beforeEach(async () => {
db = knex({ client: 'sqlite3', connection: { filename: ':memory:' }, useNullAsDefault: true });
await db.schema.createTable('app_settings', (t) => {
t.increments('id');
t.string('setting_key').notNullable().unique();
t.text('setting_value');
t.string('setting_type');
t.timestamp('updated_at');
});
jest.resetModules();
jest.doMock('../../src/database/db', () => ({ db }));
svc = require('../../src/services/uploadSettings');
svc.clearMaxFileSizeCache();
});
afterEach(async () => {
jest.dontMock('../../src/database/db');
await db.destroy();
});
async function setLimit(mb) {
await db('app_settings')
.insert({ setting_key: 'general_max_file_size_mb', setting_value: JSON.stringify(mb), setting_type: 'general', updated_at: new Date() })
.onConflict('setting_key').merge({ setting_value: JSON.stringify(mb) });
svc.clearMaxFileSizeCache();
}
test('defaults to 50MB when the setting is absent', async () => {
expect(await svc.getMaxFileSizeMb()).toBe(50);
expect(await svc.getMaxFileSizeBytes()).toBe(50 * 1024 * 1024);
});
test('honours a configured value (e.g. 500MB video)', async () => {
await setLimit(500);
expect(await svc.getMaxFileSizeMb()).toBe(500);
expect(await svc.getMaxFileSizeBytes()).toBe(500 * 1024 * 1024);
});
test('clamps a nonsense value to the default and caps absurd values at the ceiling', async () => {
await setLimit(0);
expect(await svc.getMaxFileSizeMb()).toBe(50); // 0 → default
await setLimit(99_999_999);
expect(await svc.getMaxFileSizeMb()).toBe(svc.MAX_ALLOWED_FILE_SIZE_MB); // ceiling
});
test('caches for the TTL — a mid-window DB change is not seen until the cache is cleared', async () => {
await setLimit(200);
expect(await svc.getMaxFileSizeMb()).toBe(200);
// change the DB but do NOT clear cache
await db('app_settings').where({ setting_key: 'general_max_file_size_mb' }).update({ setting_value: JSON.stringify(300) });
expect(await svc.getMaxFileSizeMb()).toBe(200); // still cached
svc.clearMaxFileSizeCache();
expect(await svc.getMaxFileSizeMb()).toBe(300); // refreshed
});