103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
/**
|
|
* Config env parsing — specifically the knobs whose zero value is meaningful.
|
|
* `RENDER_MAX_JOBS_PER_USER=0` must *disable* the per-identity guard (documented
|
|
* behavior); the earlier `intEnv` rejected 0 and silently fell back to 1, so the
|
|
* guard couldn't be turned off. Config resolves env once at import, so each case
|
|
* resets the module registry and re-imports under a fresh environment.
|
|
*/
|
|
import { describe, it, expect, afterEach, vi } from 'vitest';
|
|
|
|
const KEYS = [
|
|
'RENDER_RESOURCE_PROFILE',
|
|
'RENDER_MAX_JOBS_PER_USER',
|
|
'RENDER_MAX_CONCURRENCY',
|
|
'RENDER_MAX_CONCURRENT_EXTRACTIONS',
|
|
'PRODUCER_MAX_WORKERS',
|
|
'PRODUCER_LOW_MEMORY_MODE',
|
|
'PRODUCER_FORCE_SCREENSHOT',
|
|
'PRODUCER_BROWSER_GPU_MODE',
|
|
'PRODUCER_ENABLE_BROWSER_POOL',
|
|
'RENDER_REQUIRE_BEGINFRAME',
|
|
] as const;
|
|
const originals = Object.fromEntries(KEYS.map((key) => [key, process.env[key]]));
|
|
|
|
afterEach(() => {
|
|
for (const key of KEYS) {
|
|
const original = originals[key];
|
|
if (original === undefined) delete process.env[key];
|
|
else process.env[key] = original;
|
|
}
|
|
vi.resetModules();
|
|
});
|
|
|
|
async function loadConfig() {
|
|
vi.resetModules();
|
|
const mod = await import('../src/config.js');
|
|
return mod.config;
|
|
}
|
|
|
|
describe('config maxJobsPerUser', () => {
|
|
it('accepts 0 to disable the per-identity guard', async () => {
|
|
process.env.RENDER_MAX_JOBS_PER_USER = '0';
|
|
expect((await loadConfig()).maxJobsPerUser).toBe(0);
|
|
});
|
|
|
|
it('accepts a positive override', async () => {
|
|
process.env.RENDER_MAX_JOBS_PER_USER = '5';
|
|
expect((await loadConfig()).maxJobsPerUser).toBe(5);
|
|
});
|
|
|
|
it('falls back to the default (1) for negative or non-numeric values', async () => {
|
|
process.env.RENDER_MAX_JOBS_PER_USER = '-3';
|
|
expect((await loadConfig()).maxJobsPerUser).toBe(1);
|
|
process.env.RENDER_MAX_JOBS_PER_USER = 'nonsense';
|
|
expect((await loadConfig()).maxJobsPerUser).toBe(1);
|
|
});
|
|
|
|
it('falls back to the default when unset', async () => {
|
|
delete process.env.RENDER_MAX_JOBS_PER_USER;
|
|
expect((await loadConfig()).maxJobsPerUser).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('config producerWorkers', () => {
|
|
it('defaults to one explicit worker in the standard profile', async () => {
|
|
delete process.env.PRODUCER_MAX_WORKERS;
|
|
expect((await loadConfig()).producerWorkers).toBe(1);
|
|
});
|
|
|
|
it('accepts an explicit single-worker profile without silently raising it', async () => {
|
|
process.env.PRODUCER_MAX_WORKERS = '1';
|
|
expect((await loadConfig()).producerWorkers).toBe(1);
|
|
});
|
|
|
|
it('rejects worker overrides that contradict the profile', async () => {
|
|
process.env.PRODUCER_MAX_WORKERS = '0';
|
|
await expect(loadConfig()).rejects.toThrow(/requires PRODUCER_MAX_WORKERS=1/);
|
|
});
|
|
});
|
|
|
|
describe('config latency-profile concurrency', () => {
|
|
it('defaults to one render and one extraction at a time', async () => {
|
|
delete process.env.RENDER_MAX_CONCURRENCY;
|
|
delete process.env.RENDER_MAX_CONCURRENT_EXTRACTIONS;
|
|
const config = await loadConfig();
|
|
expect(config.maxConcurrency).toBe(1);
|
|
expect(config.maxConcurrentExtractions).toBe(1);
|
|
});
|
|
|
|
it('rejects service concurrency that exceeds the selected profile', async () => {
|
|
process.env.RENDER_MAX_CONCURRENCY = '2';
|
|
process.env.RENDER_MAX_CONCURRENT_EXTRACTIONS = '2';
|
|
await expect(loadConfig()).rejects.toThrow(/requires RENDER_MAX_CONCURRENCY=1/);
|
|
});
|
|
|
|
it('selects the explicit low-memory profile', async () => {
|
|
process.env.RENDER_RESOURCE_PROFILE = 'low-memory';
|
|
const config = await loadConfig();
|
|
expect(config.resourceProfile.name).toBe('low-memory');
|
|
expect(config.requireBeginFrame).toBe(false);
|
|
expect(config.producerWorkers).toBe(1);
|
|
});
|
|
});
|