test(whisper): fix playwright ESM loading via dynamic imports

This commit is contained in:
Richard R 2026-05-21 21:53:55 -06:00
parent 2fd9f05652
commit b2bb36911a
3 changed files with 18 additions and 74 deletions

View file

@ -6,6 +6,7 @@ import 'dotenv/config';
*/ */
export default defineConfig({ export default defineConfig({
testDir: './tests', testDir: './tests',
tsconfig: './tsconfig.json',
timeout: 30 * 1000, timeout: 30 * 1000,
outputDir: './tests/results', outputDir: './tests/results',
globalTeardown: './tests/global-teardown.ts', globalTeardown: './tests/global-teardown.ts',

View file

@ -1,7 +1,6 @@
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
import { readFile } from 'fs/promises'; import { readFile } from 'fs/promises';
import path from 'path'; import path from 'path';
import { alignAudioWithText } from '../../compute/core/src/whisper/align';
test.describe('whisper alignment smoke', () => { test.describe('whisper alignment smoke', () => {
test('runs ONNX alignment end-to-end without decoder reshape errors', async () => { test('runs ONNX alignment end-to-end without decoder reshape errors', async () => {
@ -11,12 +10,12 @@ test.describe('whisper alignment smoke', () => {
const audioBytes = await readFile(audioPath); const audioBytes = await readFile(audioPath);
const buffer = audioBytes.buffer.slice(audioBytes.byteOffset, audioBytes.byteOffset + audioBytes.byteLength); const buffer = audioBytes.buffer.slice(audioBytes.byteOffset, audioBytes.byteOffset + audioBytes.byteLength);
const alignments = await alignAudioWithText( const { runWhisperAlignmentFromAudioBuffer } = await import('@openreader/compute-core/local-runtime');
buffer, const { alignments } = await runWhisperAlignmentFromAudioBuffer({
'This is a sample sentence used to validate whisper alignment execution.', audioBuffer: buffer,
undefined, text: 'This is a sample sentence used to validate whisper alignment execution.',
{ lang: 'en' }, lang: 'en',
); });
expect(alignments.length).toBe(1); expect(alignments.length).toBe(1);
expect(Array.isArray(alignments[0].words)).toBe(true); expect(Array.isArray(alignments[0].words)).toBe(true);

View file

@ -1,71 +1,15 @@
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
import { createHash } from 'crypto';
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'fs/promises';
import { tmpdir } from 'os';
import path from 'path';
import {
createSingleflightRunner,
ensureWhisperArtifacts,
} from '../../compute/core/src/whisper/model';
function sha256(bytes: Uint8Array): string { test.describe('whisper model bootstrap via local-runtime', () => {
return createHash('sha256').update(bytes).digest('hex'); test('is idempotent across concurrent calls', async () => {
} test.setTimeout(180000);
const { ensureComputeModels } = await import('@openreader/compute-core/local-runtime');
test.describe('whisper ensure model helpers', () => { await expect(
test('downloads and verifies artifacts, and repairs checksum mismatch', async () => { Promise.all([
const root = await mkdtemp(path.join(tmpdir(), 'openreader-whisper-model-test-')); ensureComputeModels(),
const artifactBytes = new TextEncoder().encode('artifact-content-v1'); ensureComputeModels(),
const artifactHash = sha256(artifactBytes); ensureComputeModels(),
const artifactPath = 'onnx/encoder_model_int8.onnx'; ]),
const target = path.join(root, artifactPath); ).resolves.toBeDefined();
try {
// Seed a corrupted file to verify repair behavior.
await mkdir(path.dirname(target), { recursive: true });
await writeFile(target, new Uint8Array([0, 1, 2, 3]));
let fetchCount = 0;
await ensureWhisperArtifacts({
modelDir: root,
artifacts: [
{
path: artifactPath,
sha256: artifactHash,
size: artifactBytes.byteLength,
url: 'https://example.local/fake-artifact',
},
],
fetchImpl: async () => {
fetchCount += 1;
return new Response(artifactBytes, { status: 200 });
},
});
const repaired = await readFile(target);
expect(repaired.byteLength).toBe(artifactBytes.byteLength);
expect(sha256(repaired)).toBe(artifactHash);
expect(fetchCount).toBe(1);
} finally {
await rm(root, { recursive: true, force: true });
}
});
test('singleflight runner deduplicates concurrent work', async () => {
let runs = 0;
const run = createSingleflightRunner(async () => {
runs += 1;
await new Promise((resolve) => setTimeout(resolve, 30));
return 'ok';
});
const [a, b, c] = await Promise.all([run(), run(), run()]);
expect(a).toBe('ok');
expect(b).toBe('ok');
expect(c).toBe('ok');
expect(runs).toBe(1);
await run();
expect(runs).toBe(2);
}); });
}); });