mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
chore(repo): remove dead code and useless tests (#3375)
This commit is contained in:
1 parent
7e177a1c90
commit
1cd62eb661
1391 files changed
+783
-120415
No files matched your search
@@ -1,276 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {
|
||||
assertAudioFrameInvariants,
|
||||
assertVideoFrameInvariants,
|
||||
VOICE_ENGINE_V2_AUDIO_FRAME_BYTES_MAX,
|
||||
VOICE_ENGINE_V2_VIDEO_DIMENSION_MAX,
|
||||
VOICE_ENGINE_V2_VIDEO_FRAME_BYTES_MAX,
|
||||
VoiceEngineV2FfiAssertError,
|
||||
} from '@fluxer/voice_engine_v2/src/bridge/ffi_assertions';
|
||||
import {
|
||||
assertVoiceEngineV2BridgeAudioOptionsInvariants,
|
||||
assertVoiceEngineV2BridgeVideoOptionsInvariants,
|
||||
normalizeVoiceEngineV2BridgeHardwareEncoderCapabilities,
|
||||
unavailableVoiceEngineV2BridgeHardwareEncoderCapabilities,
|
||||
VOICE_ENGINE_V2_HARDWARE_ENCODER_IPC_CHANNEL,
|
||||
} from '@fluxer/voice_engine_v2/src/bridge/index';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
describe('voice engine v2 bridge contract', () => {
|
||||
it('pins the hardware encoder ipc channel shared by preload and the main process', () => {
|
||||
expect(VOICE_ENGINE_V2_HARDWARE_ENCODER_IPC_CHANNEL).toBe('voice-engine-v2:get-hardware-encoder-capabilities');
|
||||
});
|
||||
});
|
||||
|
||||
describe('voice engine v2 audio frame invariants', () => {
|
||||
const canonicalAudioFrame = {
|
||||
sampleRateHz: 48000,
|
||||
numChannels: 2,
|
||||
frameBytes: 1920,
|
||||
timestampNs: 1000,
|
||||
};
|
||||
|
||||
it('accepts a canonical audio frame with no previous timestamp', () => {
|
||||
expect(() => assertAudioFrameInvariants(canonicalAudioFrame)).not.toThrow();
|
||||
});
|
||||
|
||||
it('accepts a canonical audio frame with an older previous timestamp', () => {
|
||||
expect(() => assertAudioFrameInvariants(canonicalAudioFrame, 500)).not.toThrow();
|
||||
});
|
||||
|
||||
it('rejects an empty audio frame', () => {
|
||||
try {
|
||||
assertAudioFrameInvariants({...canonicalAudioFrame, frameBytes: 0});
|
||||
expect.fail('expected frame bytes assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('audioFrameBytesOutOfRange');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects an audio frame past the byte cap', () => {
|
||||
try {
|
||||
assertAudioFrameInvariants({...canonicalAudioFrame, frameBytes: VOICE_ENGINE_V2_AUDIO_FRAME_BYTES_MAX + 1});
|
||||
expect.fail('expected frame bytes assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('audioFrameBytesOutOfRange');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects an unsupported sample rate', () => {
|
||||
try {
|
||||
assertAudioFrameInvariants({...canonicalAudioFrame, sampleRateHz: 44100});
|
||||
expect.fail('expected sample rate assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('audioSampleRateInvalid');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects an unsupported channel count', () => {
|
||||
try {
|
||||
assertAudioFrameInvariants({...canonicalAudioFrame, numChannels: 3});
|
||||
expect.fail('expected channels assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('audioChannelsInvalid');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects a repeated audio timestamp', () => {
|
||||
try {
|
||||
assertAudioFrameInvariants(canonicalAudioFrame, canonicalAudioFrame.timestampNs);
|
||||
expect.fail('expected timestamp assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('audioTimestampRegressed');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects a regressed audio timestamp', () => {
|
||||
try {
|
||||
assertAudioFrameInvariants(canonicalAudioFrame, canonicalAudioFrame.timestampNs + 1);
|
||||
expect.fail('expected timestamp assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('audioTimestampRegressed');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('voice engine v2 video frame invariants', () => {
|
||||
const canonicalVideoFrame = {
|
||||
widthPx: 1920,
|
||||
heightPx: 1080,
|
||||
frameBytes: 1920 * 1080 * 4,
|
||||
timestampNs: 1000,
|
||||
};
|
||||
|
||||
it('accepts a canonical video frame', () => {
|
||||
expect(() => assertVideoFrameInvariants(canonicalVideoFrame)).not.toThrow();
|
||||
});
|
||||
|
||||
it('rejects an empty video frame', () => {
|
||||
try {
|
||||
assertVideoFrameInvariants({...canonicalVideoFrame, frameBytes: 0});
|
||||
expect.fail('expected frame bytes assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('videoFrameBytesOutOfRange');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects a video frame past the byte cap', () => {
|
||||
try {
|
||||
assertVideoFrameInvariants({...canonicalVideoFrame, frameBytes: VOICE_ENGINE_V2_VIDEO_FRAME_BYTES_MAX + 1});
|
||||
expect.fail('expected frame bytes assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('videoFrameBytesOutOfRange');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects a zero video dimension', () => {
|
||||
try {
|
||||
assertVideoFrameInvariants({...canonicalVideoFrame, widthPx: 0});
|
||||
expect.fail('expected dimension assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('videoDimensionOutOfRange');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects a video dimension past the cap', () => {
|
||||
try {
|
||||
assertVideoFrameInvariants({...canonicalVideoFrame, heightPx: VOICE_ENGINE_V2_VIDEO_DIMENSION_MAX + 1});
|
||||
expect.fail('expected dimension assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('videoDimensionOutOfRange');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects a repeated video timestamp', () => {
|
||||
try {
|
||||
assertVideoFrameInvariants(canonicalVideoFrame, canonicalVideoFrame.timestampNs);
|
||||
expect.fail('expected timestamp assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('videoTimestampRegressed');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('voice engine v2 bridge option airlock helpers', () => {
|
||||
it('accepts canonical audio options', () => {
|
||||
expect(() => assertVoiceEngineV2BridgeAudioOptionsInvariants({sampleRate: 48_000, numChannels: 2})).not.toThrow();
|
||||
});
|
||||
|
||||
it('rejects audio options with an unsupported sample rate', () => {
|
||||
try {
|
||||
assertVoiceEngineV2BridgeAudioOptionsInvariants({sampleRate: 44_100, numChannels: 2});
|
||||
expect.fail('expected sample rate assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('audioSampleRateInvalid');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects audio options with an unsupported channel count', () => {
|
||||
try {
|
||||
assertVoiceEngineV2BridgeAudioOptionsInvariants({sampleRate: 48_000, numChannels: 5});
|
||||
expect.fail('expected channels assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('audioChannelsInvalid');
|
||||
}
|
||||
});
|
||||
|
||||
it('accepts canonical video options', () => {
|
||||
expect(() => assertVoiceEngineV2BridgeVideoOptionsInvariants({width: 1280, height: 720})).not.toThrow();
|
||||
});
|
||||
|
||||
it('rejects video options with a zero width', () => {
|
||||
try {
|
||||
assertVoiceEngineV2BridgeVideoOptionsInvariants({width: 0, height: 720});
|
||||
expect.fail('expected dimension assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('videoDimensionOutOfRange');
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects video options with an oversized height', () => {
|
||||
try {
|
||||
assertVoiceEngineV2BridgeVideoOptionsInvariants({width: 1280, height: 1 << 20});
|
||||
expect.fail('expected dimension assertion to throw');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(VoiceEngineV2FfiAssertError);
|
||||
expect((error as VoiceEngineV2FfiAssertError).code).toBe('videoDimensionOutOfRange');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('voice engine v2 bridge hardware encoder capabilities', () => {
|
||||
it('reports an unavailable encoder with the supplied reason and detail', () => {
|
||||
expect(unavailableVoiceEngineV2BridgeHardwareEncoderCapabilities('load-failed', 'addon missing')).toEqual({
|
||||
available: false,
|
||||
backend: 'none',
|
||||
compiled: false,
|
||||
runtime: false,
|
||||
codecs: [],
|
||||
zeroCopy: false,
|
||||
nativeInputs: [],
|
||||
reason: 'load-failed',
|
||||
detail: 'addon missing',
|
||||
});
|
||||
});
|
||||
|
||||
it('omits the detail field when the caller supplies none', () => {
|
||||
expect(unavailableVoiceEngineV2BridgeHardwareEncoderCapabilities('unsupported-addon-version')).not.toHaveProperty(
|
||||
'detail',
|
||||
);
|
||||
});
|
||||
|
||||
it('normalizes a well-formed addon payload', () => {
|
||||
expect(
|
||||
normalizeVoiceEngineV2BridgeHardwareEncoderCapabilities({
|
||||
available: true,
|
||||
backend: 'nvenc',
|
||||
compiled: true,
|
||||
runtime: true,
|
||||
codecs: ['h264', 42, 'av1'],
|
||||
zeroCopy: true,
|
||||
nativeInputs: ['d3d11Texture', null],
|
||||
}),
|
||||
).toEqual({
|
||||
available: true,
|
||||
backend: 'nvenc',
|
||||
compiled: true,
|
||||
runtime: true,
|
||||
codecs: ['h264', 'av1'],
|
||||
zeroCopy: true,
|
||||
nativeInputs: ['d3d11Texture'],
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to an unavailable encoder when the addon returns a non-object', () => {
|
||||
expect(normalizeVoiceEngineV2BridgeHardwareEncoderCapabilities(null)).toEqual({
|
||||
available: false,
|
||||
backend: 'none',
|
||||
compiled: false,
|
||||
runtime: false,
|
||||
codecs: [],
|
||||
zeroCopy: false,
|
||||
nativeInputs: [],
|
||||
reason: 'query-failed',
|
||||
detail: 'Native addon returned an invalid result',
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to the none backend when the addon omits one', () => {
|
||||
expect(normalizeVoiceEngineV2BridgeHardwareEncoderCapabilities({available: true}).backend).toBe('none');
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {FuzzPrng} from '@fluxer/voice_engine_v2/src/fuzz/FuzzPrng';
|
||||
import {
|
||||
EXTERNAL_CONNECTION_EVENT_KINDS,
|
||||
FUZZ_REDUCER_ARBITRARY_ITERATIONS,
|
||||
FUZZ_REDUCER_EXTERNAL_CONNECTION_ITERATIONS,
|
||||
ReducerFuzzer,
|
||||
@@ -23,34 +21,6 @@ describe('ReducerFuzzer.fuzzExternalConnectionCycles', () => {
|
||||
expect(report.dispatched).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('produces an identical snapshot hash and counters for the same seed (determinism)', () => {
|
||||
for (const seed of SEEDS) {
|
||||
const first = new ReducerFuzzer(seed).fuzzExternalConnectionCycles(256);
|
||||
const second = new ReducerFuzzer(seed).fuzzExternalConnectionCycles(256);
|
||||
expect(first.snapshotHash).toBeDefined();
|
||||
expect(first.snapshotHash).toBe(second.snapshotHash);
|
||||
expect(first.dispatched).toBe(second.dispatched);
|
||||
expect(first.rejected).toBe(second.rejected);
|
||||
expect(first.failures.length).toBe(second.failures.length);
|
||||
}
|
||||
});
|
||||
|
||||
it('produces diverging traces for different seeds', () => {
|
||||
const first = new ReducerFuzzer(1).fuzzExternalConnectionCycles(256);
|
||||
const second = new ReducerFuzzer(2).fuzzExternalConnectionCycles(256);
|
||||
expect(first.snapshotHash === second.snapshotHash && first.dispatched === second.dispatched).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects an iteration budget above the global cap', () => {
|
||||
const fuzzer = new ReducerFuzzer(1);
|
||||
expect(() => fuzzer.fuzzExternalConnectionCycles(1025)).toThrow('iterations must respect FUZZ_ITERATIONS_MAX');
|
||||
});
|
||||
|
||||
it('rejects a non-positive iteration budget', () => {
|
||||
const fuzzer = new ReducerFuzzer(1);
|
||||
expect(() => fuzzer.fuzzExternalConnectionCycles(0)).toThrow('iterations must be >= 1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReducerFuzzer arbitrary-order generation with external connection events', () => {
|
||||
@@ -62,35 +32,4 @@ describe('ReducerFuzzer arbitrary-order generation with external connection even
|
||||
expect(report.dispatched + report.rejected).toBe(FUZZ_REDUCER_ARBITRARY_ITERATIONS);
|
||||
}
|
||||
});
|
||||
|
||||
it('remains deterministic across runs with the widened event pool', () => {
|
||||
const first = new ReducerFuzzer(42).fuzzArbitraryOrder(128);
|
||||
const second = new ReducerFuzzer(42).fuzzArbitraryOrder(128);
|
||||
expect(first.dispatched).toBe(second.dispatched);
|
||||
expect(first.rejected).toBe(second.rejected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('external connection event kind catalogue', () => {
|
||||
it('contains exactly the externally-driven connection lifecycle events', () => {
|
||||
expect(EXTERNAL_CONNECTION_EVENT_KINDS).toEqual([
|
||||
'connection.externallyEstablished',
|
||||
'connection.remoteDisconnected',
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps the seeded PRNG as the only source of randomness for kind selection', () => {
|
||||
const prng = new FuzzPrng({seed: 7});
|
||||
const picks: Array<string> = [];
|
||||
for (let index = 0; index < 32; index += 1) {
|
||||
picks.push(prng.nextChoice(EXTERNAL_CONNECTION_EVENT_KINDS));
|
||||
}
|
||||
const replayPrng = new FuzzPrng({seed: 7});
|
||||
const replays: Array<string> = [];
|
||||
for (let index = 0; index < 32; index += 1) {
|
||||
replays.push(replayPrng.nextChoice(EXTERNAL_CONNECTION_EVENT_KINDS));
|
||||
}
|
||||
expect(picks).toEqual(replays);
|
||||
expect(new Set(picks).size).toBe(2);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {FuzzPrng} from '@fluxer/voice_engine_v2/src/fuzz/FuzzPrng';
|
||||
import {
|
||||
FUZZ_REDUCER_ARBITRARY_ITERATIONS,
|
||||
FUZZ_REDUCER_NEGATIVE_ITERATIONS,
|
||||
@@ -22,14 +21,6 @@ describe('ReducerFuzzer.fuzzPositive', () => {
|
||||
expect(report.failures).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('produces identical traces for the same seed (determinism)', () => {
|
||||
const first = new ReducerFuzzer(99).fuzzPositive(64);
|
||||
const second = new ReducerFuzzer(99).fuzzPositive(64);
|
||||
expect(first.dispatched).toBe(second.dispatched);
|
||||
expect(first.rejected).toBe(second.rejected);
|
||||
expect(first.failures.length).toBe(second.failures.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReducerFuzzer.fuzzNegative', () => {
|
||||
@@ -70,35 +61,3 @@ describe('ReducerFuzzer.fuzzArbitraryOrder', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('FuzzPrng determinism', () => {
|
||||
it('produces the same byte sequence for the same seed', () => {
|
||||
const first = new FuzzPrng({seed: 42, budgetBytes: 64});
|
||||
const second = new FuzzPrng({seed: 42, budgetBytes: 64});
|
||||
const firstBytes: Array<number> = [];
|
||||
const secondBytes: Array<number> = [];
|
||||
for (let i = 0; i < 64; i += 1) firstBytes.push(first.nextByte());
|
||||
for (let i = 0; i < 64; i += 1) secondBytes.push(second.nextByte());
|
||||
expect(firstBytes).toEqual(secondBytes);
|
||||
});
|
||||
|
||||
it('produces different byte sequences for different seeds', () => {
|
||||
const first = new FuzzPrng({seed: 1, budgetBytes: 64});
|
||||
const second = new FuzzPrng({seed: 2, budgetBytes: 64});
|
||||
const firstBytes: Array<number> = [];
|
||||
const secondBytes: Array<number> = [];
|
||||
for (let i = 0; i < 64; i += 1) firstBytes.push(first.nextByte());
|
||||
for (let i = 0; i < 64; i += 1) secondBytes.push(second.nextByte());
|
||||
expect(firstBytes).not.toEqual(secondBytes);
|
||||
});
|
||||
|
||||
it('wraps the cursor when the byte budget is exhausted', () => {
|
||||
const prng = new FuzzPrng({seed: 17, budgetBytes: 16});
|
||||
const head = prng.nextByte();
|
||||
for (let i = 0; i < 15; i += 1) prng.nextByte();
|
||||
expect(prng.bytesConsumed).toBe(0);
|
||||
const wrapped = prng.nextByte();
|
||||
expect(head).toBe(wrapped);
|
||||
expect(prng.bytesConsumed).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -17,20 +17,13 @@ export {
|
||||
} from '@fluxer/voice_engine_v2/src/runtime/frameCoalescing';
|
||||
export type {
|
||||
VoiceEngineV2ClockPort,
|
||||
VoiceEngineV2EntropySource,
|
||||
VoiceEngineV2PlatformPort,
|
||||
VoiceEngineV2RandomPort,
|
||||
VoiceEngineV2SystemClockPort,
|
||||
VoiceEngineV2SystemRandomPort,
|
||||
VoiceEngineV2WallClockSource,
|
||||
} from '@fluxer/voice_engine_v2/src/runtime/platformPort';
|
||||
export {
|
||||
createVoiceEngineV2DeterministicClockPort,
|
||||
createVoiceEngineV2DeterministicPlatformPort,
|
||||
createVoiceEngineV2SeededRandomPort,
|
||||
createVoiceEngineV2SystemClockPort,
|
||||
createVoiceEngineV2SystemPlatformPort,
|
||||
createVoiceEngineV2SystemRandomPort,
|
||||
} from '@fluxer/voice_engine_v2/src/runtime/platformPort';
|
||||
export {VoiceEngineV2Controller} from '@fluxer/voice_engine_v2/src/runtime/VoiceEngineV2Controller';
|
||||
export type {
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {
|
||||
createVoiceEngineV2SeededRandomPort,
|
||||
createVoiceEngineV2SystemClockPort,
|
||||
createVoiceEngineV2SystemRandomPort,
|
||||
} from '@fluxer/voice_engine_v2/src/runtime/platformPort';
|
||||
import {createVoiceEngineV2SystemClockPort} from '@fluxer/voice_engine_v2/src/runtime/platformPort';
|
||||
import {afterEach, describe, expect, it, vi} from 'vitest';
|
||||
|
||||
afterEach(() => {
|
||||
@@ -44,21 +40,6 @@ describe('voice engine v2 platform port source failures', () => {
|
||||
expect(clock.sourceFailureCount).toBe(1);
|
||||
});
|
||||
|
||||
it('keeps the random port non-throwing, counts failures, and logs exactly once', () => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const random = createVoiceEngineV2SystemRandomPort({
|
||||
read(): number {
|
||||
throw new Error('entropy source boom');
|
||||
},
|
||||
});
|
||||
|
||||
expect(random.next()).toBe(0);
|
||||
expect(random.next()).toBe(0);
|
||||
|
||||
expect(random.sourceFailureCount).toBe(2);
|
||||
expect(consoleError).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('reports zero failures and stays silent for healthy sources', () => {
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const clock = createVoiceEngineV2SystemClockPort({
|
||||
@@ -66,16 +47,9 @@ describe('voice engine v2 platform port source failures', () => {
|
||||
return 42;
|
||||
},
|
||||
});
|
||||
const random = createVoiceEngineV2SystemRandomPort({
|
||||
read(): number {
|
||||
return 0.5;
|
||||
},
|
||||
});
|
||||
|
||||
expect(clock.now()).toBe(42);
|
||||
expect(random.next()).toBe(0.5);
|
||||
expect(clock.sourceFailureCount).toBe(0);
|
||||
expect(random.sourceFailureCount).toBe(0);
|
||||
expect(consoleError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -100,16 +74,3 @@ describe('voice engine v2 platform port source failures', () => {
|
||||
expect(consoleError).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('voice engine v2 seeded random port', () => {
|
||||
it('produces a deterministic sequence in [0, 1) for a fixed seed', () => {
|
||||
const first = createVoiceEngineV2SeededRandomPort(1234);
|
||||
const second = createVoiceEngineV2SeededRandomPort(1234);
|
||||
for (let i = 0; i < 16; i += 1) {
|
||||
const value = first.next();
|
||||
expect(value).toBe(second.next());
|
||||
expect(value).toBeGreaterThanOrEqual(0);
|
||||
expect(value).toBeLessThan(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -8,30 +8,15 @@ export interface VoiceEngineV2RandomPort {
|
||||
next(): number;
|
||||
}
|
||||
|
||||
export interface VoiceEngineV2PlatformPort {
|
||||
clock: VoiceEngineV2ClockPort;
|
||||
random: VoiceEngineV2RandomPort;
|
||||
}
|
||||
|
||||
export interface VoiceEngineV2WallClockSource {
|
||||
read(): number;
|
||||
}
|
||||
|
||||
export interface VoiceEngineV2EntropySource {
|
||||
read(): number;
|
||||
}
|
||||
|
||||
export interface VoiceEngineV2SystemClockPort extends VoiceEngineV2ClockPort {
|
||||
readonly sourceFailureCount: number;
|
||||
}
|
||||
|
||||
export interface VoiceEngineV2SystemRandomPort extends VoiceEngineV2RandomPort {
|
||||
readonly sourceFailureCount: number;
|
||||
}
|
||||
|
||||
const PLATFORM_CLOCK_MAX_MS = Number.MAX_SAFE_INTEGER;
|
||||
const PLATFORM_RANDOM_MIN = 0;
|
||||
const PLATFORM_RANDOM_LIMIT = 1;
|
||||
|
||||
interface PlatformSourceFailureLatch {
|
||||
count: number;
|
||||
@@ -71,31 +56,6 @@ export function createVoiceEngineV2SystemClockPort(
|
||||
};
|
||||
}
|
||||
|
||||
export function createVoiceEngineV2SystemRandomPort(
|
||||
source?: VoiceEngineV2EntropySource,
|
||||
): VoiceEngineV2SystemRandomPort {
|
||||
const reader = source ?? defaultSystemEntropySource();
|
||||
const failureLatch = createPlatformSourceFailureLatch(
|
||||
'voice engine v2 entropy source threw (programmer error); pinning random readings to the minimum value',
|
||||
);
|
||||
return {
|
||||
get sourceFailureCount(): number {
|
||||
return failureLatch.count;
|
||||
},
|
||||
next(): number {
|
||||
const raw = readEntropySourceSafely(reader, failureLatch);
|
||||
return clampPlatformRandomReading(raw);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createVoiceEngineV2SystemPlatformPort(): VoiceEngineV2PlatformPort {
|
||||
return {
|
||||
clock: createVoiceEngineV2SystemClockPort(),
|
||||
random: createVoiceEngineV2SystemRandomPort(),
|
||||
};
|
||||
}
|
||||
|
||||
export function createVoiceEngineV2DeterministicClockPort(start = 0, stepMs = 1): VoiceEngineV2ClockPort {
|
||||
let value = start;
|
||||
return {
|
||||
@@ -107,23 +67,6 @@ export function createVoiceEngineV2DeterministicClockPort(start = 0, stepMs = 1)
|
||||
};
|
||||
}
|
||||
|
||||
export function createVoiceEngineV2SeededRandomPort(seed: number): VoiceEngineV2RandomPort {
|
||||
let state = normaliseSeed(seed);
|
||||
return {
|
||||
next(): number {
|
||||
state = advanceSeededState(state);
|
||||
return state / SEEDED_MODULUS;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createVoiceEngineV2DeterministicPlatformPort(seed = 0): VoiceEngineV2PlatformPort {
|
||||
return {
|
||||
clock: createVoiceEngineV2DeterministicClockPort(0, 1),
|
||||
random: createVoiceEngineV2SeededRandomPort(seed),
|
||||
};
|
||||
}
|
||||
|
||||
function defaultSystemWallClockSource(): VoiceEngineV2WallClockSource {
|
||||
return {
|
||||
read(): number {
|
||||
@@ -132,14 +75,6 @@ function defaultSystemWallClockSource(): VoiceEngineV2WallClockSource {
|
||||
};
|
||||
}
|
||||
|
||||
function defaultSystemEntropySource(): VoiceEngineV2EntropySource {
|
||||
return {
|
||||
read(): number {
|
||||
return globalThis.Math.random();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function readClockSourceSafely(reader: VoiceEngineV2WallClockSource, latch: PlatformSourceFailureLatch): number {
|
||||
try {
|
||||
return reader.read();
|
||||
@@ -149,15 +84,6 @@ function readClockSourceSafely(reader: VoiceEngineV2WallClockSource, latch: Plat
|
||||
}
|
||||
}
|
||||
|
||||
function readEntropySourceSafely(reader: VoiceEngineV2EntropySource, latch: PlatformSourceFailureLatch): number {
|
||||
try {
|
||||
return reader.read();
|
||||
} catch (error) {
|
||||
recordPlatformSourceFailure(latch, error);
|
||||
return PLATFORM_RANDOM_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
function clampPlatformClockReading(raw: number, lastValue: number): number {
|
||||
if (!Number.isFinite(raw)) return Math.max(lastValue, 0);
|
||||
if (raw < 0) return Math.max(lastValue, 0);
|
||||
@@ -165,28 +91,3 @@ function clampPlatformClockReading(raw: number, lastValue: number): number {
|
||||
if (raw < lastValue) return lastValue;
|
||||
return Math.trunc(raw);
|
||||
}
|
||||
|
||||
const PLATFORM_RANDOM_LARGEST_BELOW_ONE = 0.9999999999999999;
|
||||
|
||||
function clampPlatformRandomReading(raw: number): number {
|
||||
if (!Number.isFinite(raw)) return PLATFORM_RANDOM_MIN;
|
||||
if (raw < PLATFORM_RANDOM_MIN) return PLATFORM_RANDOM_MIN;
|
||||
if (raw >= PLATFORM_RANDOM_LIMIT) return PLATFORM_RANDOM_LARGEST_BELOW_ONE;
|
||||
return raw;
|
||||
}
|
||||
|
||||
const SEEDED_MODULUS = 2 ** 31 - 1;
|
||||
const SEEDED_MULTIPLIER = 48_271;
|
||||
|
||||
function normaliseSeed(seed: number): number {
|
||||
if (!Number.isFinite(seed)) return 1;
|
||||
const truncated = Math.trunc(seed);
|
||||
const positive = truncated <= 0 ? 1 : truncated;
|
||||
return positive % SEEDED_MODULUS;
|
||||
}
|
||||
|
||||
function advanceSeededState(state: number): number {
|
||||
const product = state * SEEDED_MULTIPLIER;
|
||||
const next = product % SEEDED_MODULUS;
|
||||
return next === 0 ? 1 : next;
|
||||
}
|
||||
@@ -4,11 +4,7 @@ import {
|
||||
createVoiceEngineV2EmptyFaultPlan,
|
||||
createVoiceEngineV2FaultPlan,
|
||||
} from '@fluxer/voice_engine_v2/src/simulation/FaultInjector';
|
||||
import {
|
||||
STABLE_STRINGIFY_DEPTH_MAX,
|
||||
stableStringify,
|
||||
VoiceEngineV2Simulator,
|
||||
} from '@fluxer/voice_engine_v2/src/simulation/Simulator';
|
||||
import {VoiceEngineV2Simulator} from '@fluxer/voice_engine_v2/src/simulation/Simulator';
|
||||
import {
|
||||
createVoiceEngineV2FiveParticipantConferenceWorkload,
|
||||
createVoiceEngineV2OneOnOneCallWorkload,
|
||||
@@ -17,33 +13,6 @@ import {
|
||||
} from '@fluxer/voice_engine_v2/src/simulation/Workload';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
describe('VoiceEngineV2Simulator determinism', () => {
|
||||
it('produces identical snapshot hashes for identical inputs', async () => {
|
||||
const workload = createVoiceEngineV2OneOnOneCallWorkload();
|
||||
const faults = createVoiceEngineV2EmptyFaultPlan();
|
||||
const first = await new VoiceEngineV2Simulator({seed: 1, workload, faults, mode: 'safety'}).run();
|
||||
const second = await new VoiceEngineV2Simulator({seed: 1, workload, faults, mode: 'safety'}).run();
|
||||
expect(first.snapshotHash).toBe(second.snapshotHash);
|
||||
expect(first.finalTick).toBe(second.finalTick);
|
||||
});
|
||||
|
||||
it('produces different event logs for different seeds with packet-loss faults', async () => {
|
||||
const builder = new VoiceEngineV2WorkloadBuilder('seed-divergence');
|
||||
for (let i = 0; i < 16; i++) {
|
||||
builder.at(i * 2).connect({url: 'wss://voice.example.test', token: `tok-${i}`});
|
||||
}
|
||||
const workload = builder.build();
|
||||
const faults = createVoiceEngineV2FaultPlan([
|
||||
{kind: 'packetLoss', rate: 0.5, fromTick: 0, untilTick: workload.tickCount},
|
||||
]);
|
||||
const seedOne = await new VoiceEngineV2Simulator({seed: 1, workload, faults, mode: 'safety'}).run();
|
||||
const seedTwo = await new VoiceEngineV2Simulator({seed: 7, workload, faults, mode: 'safety'}).run();
|
||||
expect(seedOne.eventLog.length).toBeGreaterThan(0);
|
||||
expect(seedTwo.eventLog.length).toBeGreaterThan(0);
|
||||
expect(seedOne.snapshotHash).not.toBe(seedTwo.snapshotHash);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceEngineV2Simulator safety mode', () => {
|
||||
it('reports no safety violations under an empty fault plan', async () => {
|
||||
const workload = createVoiceEngineV2OneOnOneCallWorkload();
|
||||
@@ -89,19 +58,6 @@ describe('VoiceEngineV2Simulator safety mode', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('respects deterministic fault ordering across runs', async () => {
|
||||
const workload = createVoiceEngineV2ScreenShareWorkload();
|
||||
const faults = createVoiceEngineV2FaultPlan([
|
||||
{kind: 'gpuDeviceLost', atTick: 3},
|
||||
{kind: 'encoderFailed', captureId: 'cap-1', atTick: 5},
|
||||
{kind: 'deviceDisconnect', deviceId: 'default-mic', atTick: 7},
|
||||
]);
|
||||
const first = await new VoiceEngineV2Simulator({seed: 11, workload, faults, mode: 'safety'}).run();
|
||||
const second = await new VoiceEngineV2Simulator({seed: 11, workload, faults, mode: 'safety'}).run();
|
||||
expect(first.snapshotHash).toBe(second.snapshotHash);
|
||||
expect(first.eventLog.length).toBe(second.eventLog.length);
|
||||
});
|
||||
|
||||
it('runs the screen-share workload without invariant violations', async () => {
|
||||
const workload = createVoiceEngineV2ScreenShareWorkload();
|
||||
const result = await new VoiceEngineV2Simulator({
|
||||
@@ -125,46 +81,6 @@ describe('VoiceEngineV2Simulator safety mode', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('stableStringify', () => {
|
||||
it('matches JSON.stringify for a nested fixture whose keys are already sorted', () => {
|
||||
const sortedKeyFixture = {
|
||||
alpha: {inner: {deep: [{}, [], null, 'text'], list: [1, 2.5, -3]}},
|
||||
beta: [true, false, {a: 'x', b: 'y'}],
|
||||
gamma: 'value',
|
||||
};
|
||||
expect(stableStringify(sortedKeyFixture)).toBe(JSON.stringify(sortedKeyFixture));
|
||||
});
|
||||
|
||||
it('sorts object keys so insertion order does not affect the output', () => {
|
||||
const insertionOrderOne = {gamma: 'value', alpha: {b: 2, a: 1}, beta: [{z: true, a: false}]};
|
||||
const insertionOrderTwo = {alpha: {a: 1, b: 2}, beta: [{a: false, z: true}], gamma: 'value'};
|
||||
expect(stableStringify(insertionOrderOne)).toBe(stableStringify(insertionOrderTwo));
|
||||
expect(stableStringify(insertionOrderOne)).toBe(JSON.stringify(insertionOrderTwo));
|
||||
});
|
||||
|
||||
it('serializes non-finite numbers and undefined as null like before', () => {
|
||||
expect(stableStringify({a: Number.NaN, b: Number.POSITIVE_INFINITY, c: undefined})).toBe(
|
||||
'{"a":null,"b":null,"c":null}',
|
||||
);
|
||||
});
|
||||
|
||||
it('handles nesting up to the named depth bound without recursion', () => {
|
||||
let nested: unknown = 'leaf';
|
||||
for (let i = 0; i < STABLE_STRINGIFY_DEPTH_MAX - 1; i += 1) {
|
||||
nested = [nested];
|
||||
}
|
||||
expect(stableStringify(nested)).toBe(JSON.stringify(nested));
|
||||
});
|
||||
|
||||
it('crashes loudly when the depth bound is exceeded', () => {
|
||||
let nested: unknown = 'leaf';
|
||||
for (let i = 0; i < STABLE_STRINGIFY_DEPTH_MAX + 1; i += 1) {
|
||||
nested = [nested];
|
||||
}
|
||||
expect(() => stableStringify(nested)).toThrow('stableStringify depth budget exceeded');
|
||||
});
|
||||
});
|
||||
|
||||
describe('VoiceEngineV2Simulator liveness mode', () => {
|
||||
it('recovers after a device disconnect by republishing the microphone', async () => {
|
||||
const builder = new VoiceEngineV2WorkloadBuilder('liveness-device');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {SIMULATOR_TICK_MAX, VoiceEngineV2Simulator} from '@fluxer/voice_engine_v2/src/simulation/Simulator';
|
||||
import {VoiceEngineV2Simulator} from '@fluxer/voice_engine_v2/src/simulation/Simulator';
|
||||
import {
|
||||
defineAsymmetricNatAfterConnectScenario,
|
||||
defineCaptureDeviceDisconnectScenario,
|
||||
@@ -11,14 +11,6 @@ import {
|
||||
} from '@fluxer/voice_engine_v2/src/simulation/scenarios/index';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
const SCENARIO_FACTORIES: ReadonlyArray<(seed: number) => VoiceEngineV2SimulationScenario> = [
|
||||
defineNetworkPartitionDuringScreenShareScenario,
|
||||
defineGpuTdrMidFrameScenario,
|
||||
defineCaptureDeviceDisconnectScenario,
|
||||
defineAsymmetricNatAfterConnectScenario,
|
||||
defineEncoderFailUnderLoadScenario,
|
||||
];
|
||||
|
||||
async function runScenario(scenario: VoiceEngineV2SimulationScenario, seed: number) {
|
||||
return new VoiceEngineV2Simulator({
|
||||
seed,
|
||||
@@ -77,28 +69,3 @@ describe('scenario: encoderFailUnderLoad', () => {
|
||||
expect(verdict.passed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('scenarios: cross-scenario determinism', () => {
|
||||
it('produces identical snapshot hashes across repeated runs of every scenario', async () => {
|
||||
const seed = 4242;
|
||||
for (const factory of SCENARIO_FACTORIES) {
|
||||
const scenario = factory(seed);
|
||||
const first = await runScenario(scenario, seed);
|
||||
const second = await runScenario(scenario, seed);
|
||||
expect(second.snapshotHash).toBe(first.snapshotHash);
|
||||
expect(second.finalTick).toBe(first.finalTick);
|
||||
expect(second.eventLog.length).toBe(first.eventLog.length);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('scenarios: tick budget', () => {
|
||||
it('each scenario completes within SIMULATOR_TICK_MAX', async () => {
|
||||
for (const factory of SCENARIO_FACTORIES) {
|
||||
const scenario = factory(7);
|
||||
expect(scenario.workload.tickCount).toBeLessThanOrEqual(SIMULATOR_TICK_MAX);
|
||||
const result = await runScenario(scenario, 7);
|
||||
expect(result.finalTick).toBeLessThanOrEqual(SIMULATOR_TICK_MAX);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,101 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import appVoiceSessionFixtureJson from '@fluxer/voice_engine_v2/fixtures/event_logs/app_voice_session.json';
|
||||
import {coalesceVoiceEngineV2OutboundStats} from '@fluxer/voice_engine_v2/src/policies/voiceStats';
|
||||
import type {VoiceEngineV2OutboundStats} from '@fluxer/voice_engine_v2/src/protocol/types';
|
||||
import {
|
||||
createVoiceEngineV2DeterministicPlatformPort,
|
||||
createVoiceEngineV2SeededRandomPort,
|
||||
createVoiceEngineV2SystemClockPort,
|
||||
} from '@fluxer/voice_engine_v2/src/runtime';
|
||||
import {
|
||||
replayVoiceEngineV2EventLogFixture,
|
||||
type VoiceEngineV2EventLogFixture,
|
||||
} from '@fluxer/voice_engine_v2/src/testing/eventLogReplay';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
const fixture = appVoiceSessionFixtureJson as unknown as VoiceEngineV2EventLogFixture;
|
||||
|
||||
describe('voice engine v2 determinism guarantees', () => {
|
||||
it('produces byte-identical snapshot output across fresh replay runs', () => {
|
||||
const first = replayVoiceEngineV2EventLogFixture(fixture);
|
||||
const second = replayVoiceEngineV2EventLogFixture(fixture);
|
||||
|
||||
const firstBytes = JSON.stringify(first.finalSnapshot);
|
||||
const secondBytes = JSON.stringify(second.finalSnapshot);
|
||||
|
||||
expect(secondBytes).toBe(firstBytes);
|
||||
expect(second.finalSnapshot).toEqual(first.finalSnapshot);
|
||||
expect(second.commandBatches).toEqual(first.commandBatches);
|
||||
});
|
||||
|
||||
it('keeps Map-style grouping iteration order stable across runs', () => {
|
||||
const tracks: Array<VoiceEngineV2OutboundStats> = buildGroupedOutboundTracks();
|
||||
|
||||
const firstRun = coalesceVoiceEngineV2OutboundStats(tracks);
|
||||
const secondRun = coalesceVoiceEngineV2OutboundStats(tracks);
|
||||
const firstKeys = firstRun.map((track) => `${track.kind}:${track.trackSid}`);
|
||||
const secondKeys = secondRun.map((track) => `${track.kind}:${track.trackSid}`);
|
||||
|
||||
expect(secondKeys).toEqual(firstKeys);
|
||||
expect(firstKeys.length).toBe(2);
|
||||
expect(firstKeys[0]).toBe('audio:micA');
|
||||
expect(firstKeys[1]).toBe('video:camA');
|
||||
});
|
||||
|
||||
it('produces stable seeded random sequences for replay-friendly entropy', () => {
|
||||
const firstSequence = drawSeededValues(7, 8);
|
||||
const secondSequence = drawSeededValues(7, 8);
|
||||
|
||||
expect(secondSequence).toEqual(firstSequence);
|
||||
expect(new Set(firstSequence).size).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
it('isolates the system clock port against retrograde wall clock motion', () => {
|
||||
let readingIndex = 0;
|
||||
const readings = [500, 200, 800, Number.NaN, -10];
|
||||
const clock = createVoiceEngineV2SystemClockPort({
|
||||
read(): number {
|
||||
const value = readings[readingIndex] ?? 0;
|
||||
readingIndex += 1;
|
||||
return value;
|
||||
},
|
||||
});
|
||||
|
||||
const observed = [clock.now(), clock.now(), clock.now(), clock.now(), clock.now()];
|
||||
const monotonic = observed.every((value, index) => index === 0 || value >= (observed[index - 1] ?? 0));
|
||||
|
||||
expect(monotonic).toBe(true);
|
||||
expect(observed[0]).toBe(500);
|
||||
});
|
||||
|
||||
it('exposes injectable Clock and Random ports through the runtime options surface', () => {
|
||||
const platform = createVoiceEngineV2DeterministicPlatformPort(42);
|
||||
|
||||
const first = platform.clock.now();
|
||||
const second = platform.clock.now();
|
||||
const randomValue = platform.random.next();
|
||||
|
||||
expect(second).toBeGreaterThan(first);
|
||||
expect(randomValue).toBeGreaterThanOrEqual(0);
|
||||
expect(randomValue).toBeLessThan(1);
|
||||
});
|
||||
});
|
||||
|
||||
function buildGroupedOutboundTracks(): Array<VoiceEngineV2OutboundStats> {
|
||||
return [
|
||||
{trackSid: 'micA', source: 'microphone', kind: 'audio', codec: 'opus', bitrateKbps: 32, packetsLost: 0},
|
||||
{trackSid: 'micA', source: 'microphone', kind: 'audio', codec: 'opus', bitrateKbps: 30, packetsLost: 1},
|
||||
{trackSid: 'camA', source: 'camera', kind: 'video', codec: 'h264', bitrateKbps: 1500, packetsLost: 2, fps: 30},
|
||||
{trackSid: 'camA', source: 'camera', kind: 'video', codec: 'h264', bitrateKbps: 1600, packetsLost: 3, fps: 31},
|
||||
];
|
||||
}
|
||||
|
||||
function drawSeededValues(seed: number, count: number): Array<number> {
|
||||
const random = createVoiceEngineV2SeededRandomPort(seed);
|
||||
const values: Array<number> = [];
|
||||
for (let index = 0; index < count; index += 1) {
|
||||
values.push(random.next());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
/// <reference types="node" />
|
||||
|
||||
import {readFileSync} from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import * as implementationsBarrel from '@fluxer/voice_engine_v2/src/implementations';
|
||||
import * as packageBarrel from '@fluxer/voice_engine_v2/src/index';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
||||
const PUBLIC_EXPORT_FILES = ['index.ts', 'implementations/index.ts'];
|
||||
const QUARANTINED_TESTING_EXPORTS = ['VoiceEngineV2TestImplementation', 'VoiceEngineV2TestDriver'];
|
||||
const LEGACY_TESTING_EXPORTS = ['JsVoiceEngineV2Implementation', 'VoiceEngineV2JsDriver'];
|
||||
const TESTING_MODULE_REFERENCE_PATTERN = /from\s+['"](?:[./]*|@fluxer\/voice_engine_v2\/src\/)testing(?:\/[^'"]*)?['"]/;
|
||||
|
||||
const PUBLIC_BARRELS: ReadonlyArray<[string, Record<string, unknown>]> = [
|
||||
['src/index.ts', packageBarrel],
|
||||
['src/implementations/index.ts', implementationsBarrel],
|
||||
];
|
||||
|
||||
function readPublicExportSource(relativePath: string): string {
|
||||
return readFileSync(path.join(PACKAGE_ROOT, relativePath), 'utf8');
|
||||
}
|
||||
|
||||
describe('voice engine v2 implementation export boundary', () => {
|
||||
it('does not expose the testing substrate from resolved public barrel exports', () => {
|
||||
for (const [label, barrel] of PUBLIC_BARRELS) {
|
||||
const exportNames = Object.keys(barrel);
|
||||
expect(exportNames.length, `${label} must have runtime exports`).toBeGreaterThan(0);
|
||||
for (const exportName of QUARANTINED_TESTING_EXPORTS) {
|
||||
expect(exportNames, `${label} must not export ${exportName}`).not.toContain(exportName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('does not reference the testing directory from public barrel sources', () => {
|
||||
for (const relativePath of PUBLIC_EXPORT_FILES) {
|
||||
const source = readPublicExportSource(relativePath);
|
||||
expect(source, `${relativePath} must not re-export from ./testing`).not.toMatch(TESTING_MODULE_REFERENCE_PATTERN);
|
||||
}
|
||||
});
|
||||
|
||||
it('does not mention quarantined or legacy testing identifiers in public barrel sources', () => {
|
||||
for (const relativePath of PUBLIC_EXPORT_FILES) {
|
||||
const source = readPublicExportSource(relativePath);
|
||||
for (const exportName of [...QUARANTINED_TESTING_EXPORTS, ...LEGACY_TESTING_EXPORTS]) {
|
||||
expect(source, `${relativePath} must not mention ${exportName}`).not.toContain(exportName);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user