const assert = require('node:assert/strict'); const test = require('node:test'); const { createSoundDirector, COUNTS, CATALOG } = require('../../data/game_sounds.js'); function recorder() { const played = []; return { played, playPreset: preset => { played.push(preset); return true; } }; } test('every required event family has its planned minimum of original synthesis presets', () => { Object.entries(COUNTS).forEach(([family, count]) => { assert.equal(CATALOG[family].length, count); assert.equal(new Set(CATALOG[family].map(preset => preset.id)).size, count); }); }); test('selection is randomized without immediate repeats and keeps bounded history', () => { const output = recorder(); let sample = 0; const director = createSoundDirector({ engine: output, random: () => (sample++ % 11) / 11, historyLimit: 3 }); const selected = []; for (let index = 0; index < 30; index += 1) selected.push(director.play('shot').preset.id); selected.slice(1).forEach((id, index) => assert.notEqual(id, selected[index])); assert.ok(director.getHistory('shot').length <= 3); }); test('preset parameter ranges remain safe through many randomized selections', () => { const output = recorder(); const director = createSoundDirector({ engine: output, random: Math.random }); for (let index = 0; index < 3000; index += 1) director.play(Object.keys(COUNTS)[index % Object.keys(COUNTS).length]); output.played.forEach(preset => { assert.ok(preset.frequency >= 100 && preset.frequency <= 1000); assert.ok(preset.endFrequency >= 100 && preset.endFrequency <= 1000); assert.ok(preset.durationMs >= 100 && preset.durationMs <= 900); assert.ok(preset.filterHz >= 900 && preset.filterHz <= 3500); assert.ok(Math.abs(preset.pan) <= 0.4 && preset.gain <= 0.75); }); }); test('state versions, perspectives, priority, waiting rate limit, and reconnect summaries are safe', () => { const output = recorder(); let clock = 0; const director = createSoundDirector({ engine: output, random: () => 0, now: () => clock, waitingIntervalMs: 12000 }); assert.ok(director.getPriority('victory') > director.getPriority('sunk')); assert.ok(director.getPriority('sunk') > director.getPriority('hit')); assert.ok(director.getPriority('hit') > director.getPriority('miss')); assert.equal(director.play('watch-hit', { version: 4 }).preset.family, 'hit'); assert.equal(director.play('damage', { version: 4 }), false); assert.equal(director.play('watch-sunk', { version: 5 }).preset.family, 'sunk'); assert.equal(director.play('waiting').preset.family, 'waiting'); assert.equal(director.play('waiting'), false); clock = 12001; assert.equal(director.play('waiting').preset.family, 'waiting'); assert.equal(director.play('summary', { version: 6, summary: true }).preset.family, 'victory'); }); test('combos escalate, reduced intensity suppresses extras, and recovery remains calm', () => { const output = recorder(); const director = createSoundDirector({ engine: output, random: () => 0.3 }); assert.equal(director.play('hit', { combo: 2 }).combo, 'combo'); assert.equal(director.play('hit', { combo: 3 }).combo, 'mega'); assert.equal(director.play('sunk', { combo: 4 }).combo, 'ultra'); const count = output.played.length; assert.equal(director.play('hit', { combo: 4, reduced: true }).combo, 'ultra'); assert.equal(output.played.length, count + 1); assert.equal(director.play('waiting', { reduced: true }), false); const recovery = ['recovery-open', 'recovery-cancel', 'recovery-leave', 'recovery-profile', 'recovery-game'].map(event => director.play(event).preset); assert.equal(new Set(recovery.map(preset => preset.id)).size, 5); recovery.forEach(preset => assert.ok(preset.family === 'recovery' && preset.gain <= 0.16 && preset.durationMs <= 300)); });