Files
Firelink/src/store/downloadStore.test.ts
T

205 lines
6.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { initDownloadListener, useDownloadProgressStore } from './downloadStore';
import { useDownloadStore } from './useDownloadStore';
import * as ipc from '../ipc';
vi.mock('../ipc', () => ({
invokeCommand: vi.fn(),
listenEvent: vi.fn(),
}));
describe('useDownloadProgressStore', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(ipc.invokeCommand).mockResolvedValue(undefined);
useDownloadProgressStore.setState({ progressMap: {} });
});
it('prunes terminal progress entries', () => {
useDownloadProgressStore.getState().updateDownloadProgress('download-1', {
id: 'download-1',
fraction: 0.5,
speed: '1 MB/s',
eta: '10s',
size: '2 MB',
size_is_final: false
});
useDownloadProgressStore.getState().clearDownloadProgress('download-1');
expect(useDownloadProgressStore.getState().progressMap).toEqual({});
});
it('shares listener setup across overlapping consumers and tears down after the last release', async () => {
const unlisten = vi.fn();
vi.mocked(ipc.listenEvent).mockResolvedValue(unlisten);
const first = initDownloadListener();
const second = initDownloadListener();
expect(ipc.listenEvent).toHaveBeenCalledTimes(3);
const releaseFirst = await first;
const releaseSecond = await second;
releaseFirst();
expect(unlisten).not.toHaveBeenCalled();
releaseSecond();
expect(unlisten).toHaveBeenCalledTimes(3);
});
it('ignores late progress and opposite terminal events from an older lifecycle', async () => {
const handlers: Record<string, (event: any) => void> = {};
vi.mocked(ipc.listenEvent).mockImplementation((event, handler) => {
handlers[event] = handler as (event: any) => void;
return Promise.resolve(vi.fn());
});
useDownloadStore.setState({
downloads: [{
id: 'terminal',
url: 'https://example.com/file',
fileName: 'file.bin',
status: 'completed',
category: 'Other',
dateAdded: ''
}]
});
const release = await initDownloadListener();
handlers['download-progress']({ payload: {
id: 'terminal',
fraction: 0.1,
speed: '1 MB/s',
eta: '10s',
size: '1 MB',
size_is_final: false
} });
handlers['download-state']({ payload: {
id: 'terminal',
status: 'failed',
error: 'stale failure'
} });
expect(useDownloadProgressStore.getState().progressMap).toEqual({});
expect(useDownloadStore.getState().downloads[0].status).toBe('completed');
release();
});
it('clears progress when events arrive after a download row was removed', async () => {
const handlers: Record<string, (event: any) => void> = {};
vi.mocked(ipc.listenEvent).mockImplementation((event, handler) => {
handlers[event] = handler as (event: any) => void;
return Promise.resolve(vi.fn());
});
useDownloadProgressStore.getState().updateDownloadProgress('removed', {
id: 'removed',
fraction: 0.8,
speed: '1 MB/s',
eta: '2s',
size: '8 MB',
size_is_final: false
});
useDownloadStore.setState({ downloads: [] });
const release = await initDownloadListener();
handlers['download-progress']({ payload: {
id: 'removed',
fraction: 0.9,
speed: '2 MB/s',
eta: '1s',
size: '9 MB',
size_is_final: false
} });
expect(useDownloadProgressStore.getState().progressMap).toEqual({});
release();
});
it('drops stale progress when a download returns to a queued lifecycle', async () => {
const handlers: Record<string, (event: any) => void> = {};
vi.mocked(ipc.listenEvent).mockImplementation((event, handler) => {
handlers[event] = handler as (event: any) => void;
return Promise.resolve(vi.fn());
});
useDownloadStore.setState({
downloads: [{
id: 'reused',
url: 'https://example.com/file',
fileName: 'file.bin',
status: 'downloading',
category: 'Other',
dateAdded: ''
}]
});
const release = await initDownloadListener();
handlers['download-progress']({ payload: {
id: 'reused',
fraction: 0.8,
speed: '1 MB/s',
eta: '2s',
size: '8 MB',
size_is_final: false
} });
handlers['download-state']({ payload: {
id: 'reused',
status: 'queued'
} });
handlers['download-progress']({ payload: {
id: 'reused',
fraction: 0.9,
speed: '2 MB/s',
eta: '1s',
size: '9 MB',
size_is_final: false
} });
expect(useDownloadProgressStore.getState().progressMap).toEqual({});
release();
});
it('snapshots live progress before clearing it on a terminal transition', async () => {
const handlers: Record<string, (event: any) => void> = {};
vi.mocked(ipc.listenEvent).mockImplementation((event, handler) => {
handlers[event] = handler as (event: any) => void;
return Promise.resolve(vi.fn());
});
useDownloadStore.setState({
downloads: [{
id: 'snapshot',
url: 'https://example.com/file',
fileName: 'file.bin',
status: 'downloading',
category: 'Other',
dateAdded: ''
}]
});
const release = await initDownloadListener();
handlers['download-progress']({ payload: {
id: 'snapshot',
fraction: 0.8,
speed: '1 MB/s',
eta: '2s',
size: '8 MB',
size_is_final: false,
downloaded_bytes: 8192,
total_bytes: 10240,
total_is_estimate: true
} });
handlers['download-state']({ payload: {
id: 'snapshot',
status: 'paused'
} });
const row = useDownloadStore.getState().downloads[0];
expect(row.status).toBe('paused');
expect(row.fraction).toBe(0.8);
expect(row.downloadedBytes).toBe(8192);
expect(row.totalBytes).toBe(10240);
expect(row.totalIsEstimate).toBe(true);
expect(useDownloadProgressStore.getState().progressMap).toEqual({});
release();
});
});