diff --git a/web/frontend/src/__tests__/watchdog-toast.test.ts b/web/frontend/src/__tests__/watchdog-toast.test.ts index 41b9eee..8edd4d5 100644 --- a/web/frontend/src/__tests__/watchdog-toast.test.ts +++ b/web/frontend/src/__tests__/watchdog-toast.test.ts @@ -290,3 +290,80 @@ describe('KIN-099: localStorage dismissed watchdog — сохранение (л // Size limit is capped at WATCHDOG_MAX_STORED (100) — only last 100 IDs stored }) }) + +// ───────────────────────────────────────────────────────────── +// KIN-OBS-017: saveDismissedWatchdog ограничивает рост localStorage +// WATCHDOG_MAX_STORED = 100: при >100 id сохраняются только последние 100 +// ───────────────────────────────────────────────────────────── + +describe('KIN-OBS-017: saveDismissedWatchdog не превышает 100 записей в localStorage', () => { + it('При добавлении 101-го id — в localStorage остаётся не более 100 записей', async () => { + // Предзаполняем localStorage 100 существующими id + const existingIds = Array.from({ length: 100 }, (_, i) => `KIN-OLD-${String(i).padStart(3, '0')}`) + localStorageMock.setItem('kin_dismissed_watchdog_toasts', JSON.stringify(existingIds)) + + vi.mocked(api.notifications).mockResolvedValue([ + makeNotification('KIN-NEW-101', 'Process died unexpectedly (PID 9999)'), + ]) + + const wrapper = mount(EscalationBanner) + await flushPromises() + + // Dismiss нового toast — итого 101 id + await wrapper.find('.border-red-700 button').trigger('click') + await flushPromises() + + const stored = localStorageMock.getItem('kin_dismissed_watchdog_toasts') + expect(stored).toBeTruthy() + const parsed = JSON.parse(stored!) + expect(parsed.length).toBeLessThanOrEqual(100) + }) + + it('Сохраняются последние (самые новые) id — самый старый обрезается', async () => { + const existingIds = Array.from({ length: 100 }, (_, i) => `KIN-OLD-${String(i).padStart(3, '0')}`) + localStorageMock.setItem('kin_dismissed_watchdog_toasts', JSON.stringify(existingIds)) + + vi.mocked(api.notifications).mockResolvedValue([ + makeNotification('KIN-NEWEST', 'Process died unexpectedly (PID 8888)'), + ]) + + const wrapper = mount(EscalationBanner) + await flushPromises() + + await wrapper.find('.border-red-700 button').trigger('click') + await flushPromises() + + const stored = localStorageMock.getItem('kin_dismissed_watchdog_toasts') + const parsed = JSON.parse(stored!) + + // Новейший id должен остаться + expect(parsed).toContain('KIN-NEWEST') + // Самый старый id должен быть вытеснен + expect(parsed).not.toContain('KIN-OLD-000') + }) + + it('При ровно 99 существующих + 1 новый = 100 — нет усечения, все 100 сохранены', async () => { + const existingIds = Array.from({ length: 99 }, (_, i) => `KIN-FIT-${String(i).padStart(3, '0')}`) + localStorageMock.setItem('kin_dismissed_watchdog_toasts', JSON.stringify(existingIds)) + + vi.mocked(api.notifications).mockResolvedValue([ + makeNotification('KIN-FIT-099', 'Process died unexpectedly (PID 7777)'), + ]) + + const wrapper = mount(EscalationBanner) + await flushPromises() + + await wrapper.find('.border-red-700 button').trigger('click') + await flushPromises() + + const stored = localStorageMock.getItem('kin_dismissed_watchdog_toasts') + const parsed = JSON.parse(stored!) + + // Ровно 100 — без усечения + expect(parsed.length).toBe(100) + // Первый старый id не вытеснен + expect(parsed).toContain('KIN-FIT-000') + // Новый id присутствует + expect(parsed).toContain('KIN-FIT-099') + }) +})