const { test } = require('node:test'); const assert = require('node:assert/strict'); const { createController } = require('./vendor/jamesdsp/jamesdsp-feature.js'); function fixture({ loadNode } = {}) { const routed = [], errors = [], created = [], configured = [], bypass = []; const graph = { routeAll: (elements, destination) => routed.push([elements.map(x => x.id), destination]) }; const gainParam = { value: 1, cancelScheduledValues() {}, setValueAtTime(v) { this.value = v; }, linearRampToValueAtTime(v) { this.value = v; } }; const context = { sampleRate: 48000, state: 'running', currentTime: 0, destination: 'destination', createGain: () => ({ gain: gainParam, connect() {}, disconnect() {} }), resume: async () => {} }; const standardInput = 'standard-input', elements = [{ id: 'video' }, { id: 'audio' }]; const prepareGraph = async () => ({ context, standardInput, elements }); const defaultLoad = async () => { const listeners = new Map(); const instance = { node: { connect() {}, disconnect() {} }, destroyed: false, addEventListener(type, fn) { listeners.set(type, fn); }, removeEventListener(type, fn) { if (listeners.get(type) === fn) listeners.delete(type); }, async setBypass(v) { bypass.push(v); }, async applyPreset(params) { configured.push({ ...params }); }, async setParam(name, value) { configured.push({ [name]: value }); }, destroy() { this.destroyed = true; }, fail(error) { listeners.get('error')?.({ detail: error }); }, }; created.push(instance); return instance; }; const controller = createController({ graph, prepareGraph, loadNode: loadNode || defaultLoad, onError: error => errors.push(error) }); return { controller, routed, errors, created, configured, bypass, context, elements, standardInput }; } test('enable routes one initialized JamesDSP node and disable restores Standard before destroying it', async () => { const f = fixture(); assert.equal(await f.controller.enable({ preset: 'bass', params: { gain: -2 } }, 3), true); assert.equal(f.controller.active, true); assert.deepEqual(f.routed, [[['video', 'audio'], f.created[0].node]]); assert.equal(f.created.length, 1); assert.equal(f.controller.state().liveHeaps, 1); assert.equal(f.controller.state().maxLiveHeaps, 1); const standard = f.controller.disable(); assert.deepEqual(f.routed.at(-1), [['video', 'audio'], f.standardInput]); await standard; assert.equal(f.created[0].destroyed, true); assert.equal(f.controller.state().liveHeaps, 0); assert.equal(f.controller.state().nodesDestroyed, 1); assert.equal(f.errors.length, 0); }); test('a rejected or unsupported engine leaves the Standard route active', async () => { const f = fixture({ loadNode: async () => { throw new Error('corrupt wasm'); } }); await assert.rejects(f.controller.enable({ preset: 'flat' }), /corrupt wasm/); assert.deepEqual(f.routed, [[['video', 'audio'], f.standardInput]]); assert.equal(f.controller.active, false); assert.equal(f.controller.state().liveHeaps, 0); assert.equal(f.errors.length, 1); const wrongRate = fixture(); wrongRate.context.sampleRate = 32000; await assert.rejects(wrongRate.controller.enable({ preset: 'flat' }), /44\.1 or 48 kHz/); assert.equal(wrongRate.created.length, 0); assert.equal(wrongRate.controller.state().liveHeaps, 0); }); test('a runtime worklet failure switches sources to Standard before destroying the heap', async () => { const f = fixture(); await f.controller.enable({ preset: 'flat' }); const instance = f.created[0]; instance.fail(new Error('processor fault')); assert.deepEqual(f.routed.at(-1), [['video', 'audio'], f.standardInput]); assert.equal(instance.destroyed, true); assert.equal(f.controller.active, false); assert.equal(f.controller.state().liveHeaps, 0); assert.match(f.errors[0].message, /processor fault/); }); test('disable during async node loading prevents a late DSP route', async () => { let finish; const f = fixture({ loadNode: () => new Promise(resolve => { finish = resolve; }) }); const enabling = f.controller.enable({ preset: 'flat' }); await new Promise(resolve => setImmediate(resolve)); const disabling = f.controller.disable(); const neverRouted = { node: { connect() {}, disconnect() {} }, addEventListener() {}, removeEventListener() {}, setBypass: async () => {}, applyPreset: async () => {}, setParam: async () => {}, destroy() { this.destroyed = true; }, }; finish(neverRouted); await enabling; await disabling; assert.equal(f.routed.some(([, destination]) => destination === neverRouted.node), false); assert.equal(neverRouted.destroyed, true); assert.equal(f.controller.state().maxLiveHeaps, 0); }); test('preset gain and Level gain combine within engine bounds without pausing playback', async () => { const f = fixture(); await f.controller.enable({ preset: 'loudness', params: { gain: 8 } }, 9); assert.ok(f.configured.some(entry => entry.gain === 12)); assert.equal(f.created.length, 1); assert.equal(f.controller.state().maxLiveHeaps, 1); assert.equal(f.controller.active, true); }); test('A/B bypass is retained when settings are reapplied', async () => { const f = fixture(); await f.controller.enable({ preset: 'warm' }, 0); assert.equal(await f.controller.toggleBypass(), true); await f.controller.enable({ preset: 'bass' }, 0); assert.equal(f.controller.state().bypassed, true); assert.ok(f.bypass.at(-1)); assert.equal(await f.controller.toggleBypass(), false); assert.equal(f.bypass.at(-1), false); });