Every song in songs///song.js, evaluated the way the REPL does and played through to its end against a stand-in Jev, so a song that cannot run fails here instead of in a listener's tab: a question jev() refuses, a fallback that is not an option, a form walk() rejects, a syntax error, or a request the relay would refuse (worker/src/relay.ts). It also measures each song's calls a minute against the relay's rate limit (worker/wrangler.json; the budget's arithmetic is in relay.ts).
Runs in nix flake check (flake.nix, checks.jev-tests).
10import { readdirSync, readFileSync, statSync } from 'node:fs'; 11import { join } from 'node:path'; 12import { describe, expect, it } from 'vitest'; 13import { LIMIT, MAX_BYTES, whyNot } from '../../../worker/src/relay'; 14import wrangler from '../../../worker/wrangler.json'; 15import { artRequest, CRITIC_MODEL, songCode, specBody } from './critic.mjs'; 16import { measuredFor, readMeasured, withoutSectionParts } from './measured.mjs'; 17import { playThrough, STEP } from './playThrough.mjs';
JEV_SONGS_DIR points it at another songs/ tree, e.g. to prove a break fails.
20const SONGS = process.env.JEV_SONGS_DIR ?? new URL('../../../songs/', import.meta.url).pathname;
A song this long has not ended: every form has a cap well under it.
22const MAX_CYCLES = 480;
24export const songDirs = () => 25 readdirSync(SONGS) 26 .filter((theme) => statSync(join(SONGS, theme)).isDirectory()) 27 .flatMap((theme) => 28 readdirSync(join(SONGS, theme)) 29 .filter((song) => statSync(join(SONGS, theme, song)).isDirectory()) 30 .map((song) => ({ id: `${theme}/${song}`, file: join(SONGS, theme, song, 'song.js') })), 31 );
Calls a minute at the song's busiest, at its own tempo: the most requests in any minute of it, or, for a song shorter than a minute, its requests scaled to one (the radio plays the next song straight after).
36function callsPerMinute(requests, cps, length) { 37 const minute = cps * 60; 38 const window = Math.min(minute, length); 39 const at = requests.map((r) => r.cycle).sort((a, b) => a - b); 40 let most = 0; 41 for (let i = 0, j = 0; i < at.length; i++) { 42 while (at[i] - at[j] >= window) j++; 43 most = Math.max(most, i - j + 1); 44 } 45 return (most * minute) / window; 46}
48describe('the rate limit', () => { 49 it('is the same in production and in dev', () => { 50 // wrangler does not let an environment inherit its bindings, so the 51 // limit is written twice 52 expect(wrangler.env.dev.ratelimits).toEqual(wrangler.ratelimits); 53 expect(LIMIT.period, 'the budget in relay.ts counts calls a minute').toBe(60); 54 }); 55}); 56 57describe('every song', () => { 58 for (const { id, file } of songDirs()) { 59 it(`${id} evaluates, asks what the relay accepts, and ends`, async () => { 60 const { requests, cps, heard, lastSound, views } = await playThrough(readFileSync(file, 'utf8'), MAX_CYCLES); 61 62 expect(heard, 'the song plays').toBeGreaterThan(0); 63 expect(lastSound, 'the song ends').toBeLessThan(MAX_CYCLES - 16); 64 // The stand-in always answers, so a decision that fell back for a 65 // reason is the song's own rule throwing (an `allow` reading the 66 // section before the opening fell back silently, 2026-09-26) 67 const threw = views.flatMap((v) => 68 v.decisions.filter((d) => d?.status === 'fallback' && d.reason && !d.ended), 69 ); 70 expect(threw.map((d) => `${d.cycles}: ${d.reason}`), 'no rule of the song throws').toEqual([]); 71 const refused = requests.filter((r) => r.why); 72 expect(refused.map((r) => r.why), 'the relay accepts every request').toEqual([]); 73 for (const r of requests) expect(r.bytes, 'a request fits the relay').toBeLessThanOrEqual(MAX_BYTES); 74 if (/\bjev\s*\(/.test(readFileSync(file, 'utf8'))) { 75 expect(requests.length, 'Jev is asked').toBeGreaterThan(0); 76 expect(cps, 'the song sets its tempo, so its rate can be measured').toBeGreaterThan(0); 77 // half the limit: the other half is a second tab, the mood picker, 78 // the art check and the radio (relay.ts) 79 expect(callsPerMinute(requests, cps, lastSound + STEP), 'calls a minute').toBeLessThanOrEqual(LIMIT.limit / 2); 80 // a form asked after its first sections hears what the jev()s asked after it arranged 81 if (/after:\s*\{/.test(readFileSync(file, 'utf8'))) { 82 const later = requests.filter((r) => r.form && r.cycle > 16); 83 if (later.length) expect(later.every((r) => r.arranged), "the form hears the arrangement").toBe(true); 84 } 85 }
"is it art?" on the unedited song sends its spec, code and measured mix whole (JevPicks.jsx, songs.mjs): the relay must take that too.
89 const specText = readFileSync(join(file, '..', 'SPEC.md'), 'utf8'); 90 const raw = readFileSync(file, 'utf8'); 91 const title = specText.split('---')[1].match(/^title:\s*(.+)$/m)[1]; 92 const art = { 93 model: CRITIC_MODEL, 94 ...artRequest({ 95 title, 96 spec: specBody(specText), 97 code: songCode(`// @title ${title}\n${raw}`), 98 measured: withoutSectionParts(measuredFor(readMeasured(specText), raw)), 99 }), 100 }; 101 expect(whyNot(art), 'the relay accepts the art check').toBeFalsy(); 102 expect(new TextEncoder().encode(JSON.stringify(art)).length, 'the art check fits the relay').toBeLessThanOrEqual(MAX_BYTES); 103 }, 120_000); 104 } 105});