test: cover render concurrency limit and winner picker

This commit is contained in:
tiennm99 committed 2026-07-25 01:17:19 +07:00
1 parent 8fece5fd7f
commit a6c65768f5
2 files changed
+64

No files matched your search

+41
View File
@@ -89,6 +89,47 @@ describe('POST /api/gif', () => {
await app.close();
});
test('rejects concurrent renders beyond the limit and frees the slot afterward', async () => {
/** @type {(value?: void) => void} */
let releaseRender = () => {};
/** @type {(value?: void) => void} */
let markRenderStarted = () => {};
const renderStarted = new Promise((resolve) => {
markRenderStarted = resolve;
});
const renderGate = new Promise((resolve) => {
releaseRender = resolve;
});
const app = await buildServer({
config,
renderGif: async () => {
markRenderStarted();
await renderGate;
return {buffer: Buffer.from('GIF89a-test'), byteLength: 11, durationMs: 1};
},
});
const payload = {options: ['alpha', 'beta'], winnerIndex: 0};
const first = app.inject({method: 'POST', url: '/api/gif', payload});
// The route acquires the semaphore before calling renderGif, so once the
// render body runs the single slot is held.
await renderStarted;
const rejected = await app.inject({method: 'POST', url: '/api/gif', payload});
expect(rejected.statusCode).toBe(429);
expect(rejected.json().error).toBe('too_many_renders');
releaseRender();
expect((await first).statusCode).toBe(200);
// The slot released in `finally` must let a later request through.
const afterRelease = await app.inject({method: 'POST', url: '/api/gif', payload});
expect(afterRelease.statusCode).toBe(200);
await app.close();
});
test('returns timeout response when rendering exceeds configured limit', async () => {
const app = await buildServer({
config: {...config, renderTimeoutMs: 7},
+23
View File
@@ -0,0 +1,23 @@
import {describe, expect, test} from 'vitest';
import {pickWinnerIndex} from '../src/lib/winner.js';
describe('pickWinnerIndex', () => {
test('returns an index within range across many draws', () => {
for (let i = 0; i < 100; i += 1) {
const index = pickWinnerIndex(4);
expect(Number.isInteger(index)).toBe(true);
expect(index).toBeGreaterThanOrEqual(0);
expect(index).toBeLessThan(4);
}
});
test('always returns 0 for a single option', () => {
expect(pickWinnerIndex(1)).toBe(0);
});
test('throws RangeError for non-positive or non-integer counts', () => {
expect(() => pickWinnerIndex(0)).toThrow(RangeError);
expect(() => pickWinnerIndex(-3)).toThrow(RangeError);
expect(() => pickWinnerIndex(2.5)).toThrow(RangeError);
});
});