fix: surface speech model download failures (#3880)

This commit is contained in:
Neil 2026-05-30 12:52:37 -07:00 committed by GitHub
parent 6c2219ac5e
commit f739e2246d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 0 deletions

View File

@ -0,0 +1,70 @@
import { mkdtempSync, rmSync } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { SPEECH_MODEL_CATALOG } from './model-catalog'
import { ModelManager } from './model-manager'
const { httpsGetMock } = vi.hoisted(() => ({
httpsGetMock: vi.fn()
}))
vi.mock('electron', () => ({
app: {
getPath: () => '/tmp/orca-speech-models-test'
}
}))
vi.mock('https', async () => {
const actual = await vi.importActual('https')
return { ...(actual as Record<string, unknown>), get: httpsGetMock }
})
describe('ModelManager download failures', () => {
beforeEach(() => {
httpsGetMock.mockReset()
})
it('rejects failed model downloads so the caller can surface the error', async () => {
const dir = mkdtempSync(join(tmpdir(), 'orca-model-manager-'))
try {
const manifest = SPEECH_MODEL_CATALOG[0]
const errorHandlers: ((err: Error) => void)[] = []
const request = {
destroy: vi.fn(() => request),
setTimeout: vi.fn(() => request),
on: vi.fn((event: string, cb: (err: Error) => void) => {
if (event === 'error') {
errorHandlers.push(cb)
}
return request
}),
off: vi.fn((event: string, cb: (err: Error) => void) => {
if (event === 'error') {
const index = errorHandlers.indexOf(cb)
if (index !== -1) {
errorHandlers.splice(index, 1)
}
}
return request
})
}
httpsGetMock.mockImplementation(() => {
queueMicrotask(() => {
for (const handler of errorHandlers) {
handler(new Error('network down'))
}
})
return request
})
const manager = new ModelManager(dir)
await expect(manager.downloadModel(manifest.id)).rejects.toThrow('network down')
expect(request.off).toHaveBeenCalledWith('error', expect.any(Function))
expect(errorHandlers).toHaveLength(0)
} finally {
rmSync(dir, { recursive: true, force: true })
}
})
})

View File

@ -179,6 +179,11 @@ export class ModelManager {
this.updateState(modelId, 'error', undefined, String(err))
}
this.cleanup(modelId, archivePath)
if (!aborted) {
// Why: the settings UI awaits this promise to show download failures;
// cancellation stays quiet, but real failures must reach the caller.
throw err
}
} finally {
this.activeDownloads.delete(modelId)
try {