d3ro-voice/apps/desktop/tests/main/services/audio-capture-redteam-r1-3.test.ts

75 lines
2.3 KiB
TypeScript

// tests/main/services/audio-capture-redteam-r1-3.test.ts
// SoX 가 장치를 못 열고 끝났을 때 'error' 리스너가 없어 ERR_UNHANDLED_ERROR 가 나고
// 뒤이은 'stopped' 가 영영 나가지 않던 버그의 회귀 테스트.
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { EventEmitter } from 'events'
vi.mock('../../../src/main/services/LoggerService', () => ({
getLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }),
}))
vi.mock('../../../src/main/services/ConfigService', () => ({
configGet: vi.fn(() => 'usb-headset-gone'),
}))
vi.mock('../../../src/main/utils/paths', () => ({
getSoxPath: () => 'sox',
}))
class FakeProcess extends EventEmitter {
stdout = new EventEmitter()
stderr = new EventEmitter()
kill = vi.fn()
}
const spawned = vi.hoisted(() => ({ list: [] as unknown[] }))
vi.mock('child_process', async (importOriginal) => {
const actual = await importOriginal<typeof import('child_process')>()
return {
...actual,
spawn: vi.fn(() => {
const proc = new FakeProcess()
spawned.list.push(proc)
return proc
}),
}
})
type Module = typeof import('../../../src/main/services/AudioCaptureService')
let mod: Module
beforeEach(async () => {
vi.resetModules()
spawned.list = []
mod = await import('../../../src/main/services/AudioCaptureService')
mod.resetAudioCaptureServiceForTests()
})
describe('AudioCaptureService — 캡처 프로세스 종료', () => {
it("SoX 가 녹음 중 끝나도 예외 없이 'stopped'(device-lost) 를 알린다", async () => {
const audio = mod.getAudioCaptureService()
const stopped: string[] = []
audio.on('stopped', ({ reason }) => stopped.push(reason))
await audio.start()
expect(audio.state).toBe('capturing')
const proc = spawned.list[0] as FakeProcess
expect(() => proc.emit('close', 1)).not.toThrow()
expect(stopped).toEqual(['device-lost'])
expect(audio.state).toBe('error')
})
it('실패 뒤에도 다시 시작할 수 있다', async () => {
const audio = mod.getAudioCaptureService()
await audio.start()
;(spawned.list[0] as FakeProcess).emit('close', 1)
await audio.start()
expect(audio.state).toBe('capturing')
expect(spawned.list).toHaveLength(2)
})
})