Files
openswarm/electron/bundledModel.test.js
T
ciregenzandClaude Opus 5 66ca97d773 [eric] dictation: ship the model in the app so the first fn press never waits on a download
Costs ~181MB in the DMG; the alternative is pressing fn into silence while 190MB fetches. Checksum-gated so a truncated model fails the build instead of dying at the first press.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018foyDoK19jjbYdudfzQVkZ
2026-08-25 14:56:19 -07:00

47 lines
2.5 KiB
JavaScript

// Dictation has to work the instant the app opens: press fn, talk, done. The model is 190MB and
// lived only as a runtime download, so a fresh install (or anyone offline, or on slow wifi) pressed
// fn into silence while it fetched. It is now staged into Resources/whisper at build time.
//
// resolveModelFile already prefers a bundled model over userData, so these pin the two halves that
// can silently drift: the build must stage it, and the resolver must look for the one we staged.
const test = require('node:test');
const assert = require('node:assert');
const fs = require('fs');
const path = require('path');
const SH = path.join(__dirname, 'scripts', 'build-whisper.sh');
const MODELS = require('./voice/whisperModels.js');
test('the build stages the DEFAULT model, the same one the resolver looks for', () => {
const sh = fs.readFileSync(SH, 'utf-8');
const wanted = MODELS.modelById(MODELS.DEFAULT_MODEL_ID).file;
assert.ok(sh.includes(wanted),
`build-whisper.sh must stage ${wanted}; the resolver checks resourceDir for exactly that name`);
});
test('a corrupt model is refused at build time rather than shipped', () => {
const sh = fs.readFileSync(SH, 'utf-8');
const expected = MODELS.modelById(MODELS.DEFAULT_MODEL_ID).sha256;
assert.ok(sh.includes(expected), 'the staged checksum must match the catalog');
assert.ok(sh.includes('checksum mismatch') && sh.includes('exit 1'),
'a truncated model ships silently and dies at the first press, so it must fail the build');
});
test('a build without the model warns instead of passing quietly', () => {
const sh = fs.readFileSync(SH, 'utf-8');
assert.ok(sh.includes('WARNING') && sh.includes('WITHOUT a bundled model'),
'silently dropping the model reintroduces the first-press wait with nothing saying so');
});
test('the bundled model is NOT swept into app.asar', () => {
// It belongs in extraResources. Inside the asar it would cost every user the 181MB twice over,
// which is the regression removed on 2026-08-25.
const pkg = require('./package.json');
assert.ok(pkg.build.files.includes('!whisper') && pkg.build.files.includes('!whisper/**'));
// Platform-scoped: the entry lives under build.mac.extraResources, not build.extraResources.
const extra = JSON.stringify(pkg.build.mac.extraResources);
assert.ok(extra.includes('build-staging/whisper'), 'it reaches Resources via extraResources only');
assert.ok(extra.includes('"to":"whisper"') || extra.includes('"to": "whisper"'),
'and lands at Resources/whisper, which is what voiceResourceDir() resolves');
});