62 lines
2.2 KiB
TypeScript
62 lines
2.2 KiB
TypeScript
// 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/)
|
|
})
|
|
})
|