import { beforeEach, describe, expect, it, vi } from 'vitest' import type { DesktopUpdateStatus } from '@/global' const storage = new Map() vi.mock('@/lib/storage', () => ({ persistString: (key: string, value: null | string) => { if (value === null) { storage.delete(key) } else { storage.set(key, value) } }, storedString: (key: string) => storage.get(key) ?? null })) const notifySpy = vi.fn() const dismissSpy = vi.fn() vi.mock('@/store/notifications', () => ({ notify: (...args: unknown[]) => notifySpy(...args), dismissNotification: (...args: unknown[]) => dismissSpy(...args) })) const { maybeNotifyUpdateAvailable } = await import('./updates') const status = (over: Partial = {}): DesktopUpdateStatus => ({ supported: true, behind: 3, targetSha: 'sha-a', fetchedAt: 0, ...over }) const lastToast = () => notifySpy.mock.calls.at(-1)?.[0] as { onDismiss: () => void } describe('maybeNotifyUpdateAvailable', () => { beforeEach(() => { storage.clear() notifySpy.mockClear() vi.useRealTimers() }) it('shows when an update is available and not snoozed', () => { maybeNotifyUpdateAvailable(status()) expect(notifySpy).toHaveBeenCalledTimes(1) }) it('stays quiet for new commits once the toast was closed', () => { maybeNotifyUpdateAvailable(status()) lastToast().onDismiss() // user closes it → cooldown starts notifySpy.mockClear() // A different commit lands while still within the cooldown window. maybeNotifyUpdateAvailable(status({ targetSha: 'sha-b', behind: 9 })) expect(notifySpy).not.toHaveBeenCalled() }) it('re-shows once the cooldown elapses', () => { vi.useFakeTimers() vi.setSystemTime(0) maybeNotifyUpdateAvailable(status()) lastToast().onDismiss() notifySpy.mockClear() vi.setSystemTime(25 * 60 * 60 * 1000) // > 24h cooldown maybeNotifyUpdateAvailable(status({ targetSha: 'sha-b' })) expect(notifySpy).toHaveBeenCalledTimes(1) }) it('does nothing when already up to date', () => { maybeNotifyUpdateAvailable(status({ behind: 0 })) expect(notifySpy).not.toHaveBeenCalled() }) })