fix(runtime): stage runtime installs strictly, time out only on stalls, and make reinstall actually reinstall
This commit is contained in:
parent
ba0dbbd813
commit
524d390bc2
10 changed files with 1160 additions and 238 deletions
|
|
@ -0,0 +1,62 @@
|
|||
// tests/unit/stt-tab-subscriptions-redteam-r1-8.test.ts
|
||||
// STT 탭 언마운트 시 모든 진행률 구독이 해제되는지 (runtime:progress 리스너 누수 회귀).
|
||||
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { subscribeSttTabEvents } from '../../src/renderer/components/stt/sttTabSubscriptions'
|
||||
|
||||
type Listener = (event: number) => void
|
||||
|
||||
function fakeChannel(): { subscribe: (cb: Listener) => () => void; listeners: Set<Listener> } {
|
||||
const listeners = new Set<Listener>()
|
||||
return {
|
||||
listeners,
|
||||
subscribe: (cb) => {
|
||||
listeners.add(cb)
|
||||
return () => {
|
||||
listeners.delete(cb)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
describe('subscribeSttTabEvents', () => {
|
||||
it('마운트·언마운트를 반복해도 리스너가 쌓이지 않는다', () => {
|
||||
const download = fakeChannel()
|
||||
const runtime = fakeChannel()
|
||||
|
||||
for (let mount = 0; mount < 12; mount += 1) {
|
||||
const cleanup = subscribeSttTabEvents(
|
||||
{ onDownloadProgress: download.subscribe, onRuntimeProgress: runtime.subscribe },
|
||||
{ onDownloadProgress: () => undefined, onRuntimeProgress: () => undefined },
|
||||
)
|
||||
expect(download.listeners.size).toBe(1)
|
||||
expect(runtime.listeners.size).toBe(1)
|
||||
cleanup()
|
||||
}
|
||||
|
||||
expect(download.listeners.size).toBe(0)
|
||||
expect(runtime.listeners.size).toBe(0)
|
||||
})
|
||||
|
||||
it('cleanup 을 두 번 불러도 안전하다 (StrictMode 이중 호출)', () => {
|
||||
const download = fakeChannel()
|
||||
const runtime = fakeChannel()
|
||||
const cleanup = subscribeSttTabEvents(
|
||||
{ onDownloadProgress: download.subscribe, onRuntimeProgress: runtime.subscribe },
|
||||
{ onDownloadProgress: () => undefined, onRuntimeProgress: () => undefined },
|
||||
)
|
||||
cleanup()
|
||||
expect(() => cleanup()).not.toThrow()
|
||||
})
|
||||
|
||||
it('STTTab 은 진행률 이벤트를 이 헬퍼로만 구독한다 (개별 구독 + 누락된 해제 재발 방지)', () => {
|
||||
const source = readFileSync(
|
||||
join(__dirname, '../../src/renderer/components/STTTab.tsx'),
|
||||
'utf8',
|
||||
)
|
||||
expect(source).toContain('return subscribeSttTabEvents(')
|
||||
expect(source).not.toMatch(/const\s+unsub\w*\s*=\s*window\.electronAPI\.(stt|runtime)\.on/)
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue