import { check } from '@tauri-apps/plugin-updater'; import { afterEach, describe, expect, it, vi } from 'vitest'; vi.mock('@tauri-apps/plugin-updater', () => ({ check: vi.fn() })); const checkMock = vi.mocked(check); type FakeDownloadEvent = | { event: 'Started'; data: { contentLength?: number } } | { event: 'Progress'; data: { chunkLength: number } } | { event: 'Finished' }; function fakeUpdate() { return { version: '99.0.0', currentVersion: '0.1.47', body: '修复与改进', downloadAndInstall: vi.fn( async (onEvent: (event: FakeDownloadEvent) => void) => { onEvent({ event: 'Started', data: { contentLength: 100 } }); onEvent({ event: 'Progress', data: { chunkLength: 40 } }); onEvent({ event: 'Progress', data: { chunkLength: 60 } }); onEvent({ event: 'Finished' }); }, ), }; } function stubTauriWindow() { const invoke = vi.fn(async () => undefined); vi.stubGlobal('window', { __TAURI__: { core: { invoke } } }); return invoke; } afterEach(() => { vi.unstubAllEnvs(); vi.unstubAllGlobals(); vi.resetModules(); checkMock.mockReset(); }); describe('AGC 客户端更新', () => { it('开发态开关关闭时不请求清单', async () => { vi.stubEnv('VITE_AGC_ENABLE_APP_UPDATE_CHECK', '0'); vi.resetModules(); const { checkForAppUpdate, resetAppUpdateCheckForTests } = await import( '../src/services/appUpdate' ); await expect(checkForAppUpdate()).resolves.toBeNull(); expect(checkMock).not.toHaveBeenCalled(); resetAppUpdateCheckForTests(); }); it('开关打开时把插件返回的更新映射给界面,且同一生命周期只查一次', async () => { vi.stubEnv('VITE_AGC_ENABLE_APP_UPDATE_CHECK', '1'); vi.resetModules(); checkMock.mockResolvedValue(fakeUpdate() as never); const { checkForAppUpdate, resetAppUpdateCheckForTests } = await import( '../src/services/appUpdate' ); await expect(checkForAppUpdate()).resolves.toEqual({ version: '99.0.0', currentVersion: '0.1.47', releaseNotes: '修复与改进', }); await checkForAppUpdate(); expect(checkMock).toHaveBeenCalledTimes(1); resetAppUpdateCheckForTests(); }); it('清单缺失或网络失败时静默按无更新收口', async () => { vi.stubEnv('VITE_AGC_ENABLE_APP_UPDATE_CHECK', '1'); vi.resetModules(); checkMock.mockRejectedValue(new Error('updater: manifest 404')); const { checkForAppUpdate, resetAppUpdateCheckForTests } = await import( '../src/services/appUpdate' ); await expect(checkForAppUpdate()).resolves.toBeNull(); resetAppUpdateCheckForTests(); }); it('安装时按下载事件上报进度并在完成后重启进程', async () => { vi.stubEnv('VITE_AGC_ENABLE_APP_UPDATE_CHECK', '1'); vi.resetModules(); const invoke = stubTauriWindow(); const update = fakeUpdate(); checkMock.mockResolvedValue(update as never); const { checkForAppUpdate, installAppUpdate, resetAppUpdateCheckForTests } = await import('../src/services/appUpdate'); await checkForAppUpdate(); const progress: Array<{ downloadedBytes: number; totalBytes?: number }> = []; await installAppUpdate((value) => progress.push(value)); expect(update.downloadAndInstall).toHaveBeenCalledTimes(1); expect(progress).toEqual([ { downloadedBytes: 0, totalBytes: 100 }, { downloadedBytes: 40, totalBytes: 100 }, { downloadedBytes: 100, totalBytes: 100 }, { downloadedBytes: 100, totalBytes: 100 }, ]); expect(invoke).toHaveBeenCalledWith('restart_agc_app'); resetAppUpdateCheckForTests(); }); it('没有待安装更新时安装请求失败关闭', async () => { vi.stubEnv('VITE_AGC_ENABLE_APP_UPDATE_CHECK', '1'); vi.resetModules(); const { installAppUpdate, resetAppUpdateCheckForTests } = await import( '../src/services/appUpdate' ); await expect(installAppUpdate()).rejects.toThrow('没有可安装的更新'); resetAppUpdateCheckForTests(); }); it('下载失败后仍可重试安装', async () => { vi.stubEnv('VITE_AGC_ENABLE_APP_UPDATE_CHECK', '1'); vi.resetModules(); const update = fakeUpdate(); update.downloadAndInstall.mockRejectedValue(new Error('下载更新失败')); checkMock.mockResolvedValue(update as never); const { checkForAppUpdate, installAppUpdate, resetAppUpdateCheckForTests } = await import('../src/services/appUpdate'); await checkForAppUpdate(); await expect(installAppUpdate()).rejects.toThrow('下载更新失败'); // 重试仍能拿到待装更新,而不是报“没有可安装的更新”。 await expect(installAppUpdate()).rejects.toThrow('下载更新失败'); expect(update.downloadAndInstall).toHaveBeenCalledTimes(2); resetAppUpdateCheckForTests(); }); });