mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-12 20:47:22 +00:00
fix: harden scheduler, permissions, and download safety
- Implement scheduler hydration barrier to prevent premature triggers - Track scheduler exact runs using keys to avoid false stops - Use 'System Events' for accurate macOS automation permissions - Prevent system-sleep via proper idle assertions - Ensure download pauses use channel acknowledgements (PauseWithAck) - Require Firelink ownership before replacing files in add/conflict UI - Retain partial download assets when removing entries without deletion - Clear progress state in store when downloads complete or pause to reduce churn - Handle empty/invalid queue selections gracefully
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useDownloadStore } from './useDownloadStore';
|
||||
import { useSettingsStore } from './useSettingsStore';
|
||||
import * as ipc from '../ipc';
|
||||
|
||||
vi.mock('../ipc', () => ({
|
||||
@@ -172,6 +173,26 @@ describe('useDownloadStore', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('reports a rejected immediate start instead of claiming success', async () => {
|
||||
vi.mocked(ipc.invokeCommand).mockImplementation(async (command: string) => {
|
||||
if (command === 'enqueue_download') {
|
||||
throw new Error('backend unavailable');
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const added = await useDownloadStore.getState().addDownload({
|
||||
id: 'rejected-start',
|
||||
url: 'https://example.com/rejected.bin',
|
||||
fileName: 'rejected.bin',
|
||||
category: 'Other',
|
||||
dateAdded: ''
|
||||
}, { type: 'start-now' });
|
||||
|
||||
expect(added).toBe(false);
|
||||
expect(useDownloadStore.getState().downloads[0].status).toBe('failed');
|
||||
});
|
||||
|
||||
it('redownloads fallback media without requiring a format selector', async () => {
|
||||
useDownloadStore.setState({
|
||||
downloads: [{
|
||||
@@ -280,6 +301,33 @@ describe('useDownloadStore', () => {
|
||||
expect(useDownloadStore.getState().downloads.find(item => item.id === 'done')?.queueId).toBe('old');
|
||||
});
|
||||
|
||||
it('disables scheduler when its last selected queue is deleted', async () => {
|
||||
const originalSettings = useSettingsStore.getState();
|
||||
const setScheduler = vi.fn();
|
||||
vi.mocked(useSettingsStore.getState).mockReturnValue({
|
||||
scheduler: {
|
||||
enabled: true,
|
||||
selectedQueueIds: ['queue-a']
|
||||
},
|
||||
setScheduler
|
||||
} as any);
|
||||
useDownloadStore.setState({
|
||||
queues: [
|
||||
{ id: '00000000-0000-0000-0000-000000000001', name: 'Main Queue', isMain: true },
|
||||
{ id: 'queue-a', name: 'Scheduled', isMain: false }
|
||||
],
|
||||
downloads: []
|
||||
});
|
||||
|
||||
await useDownloadStore.getState().removeQueue('queue-a');
|
||||
|
||||
expect(setScheduler).toHaveBeenCalledWith(expect.objectContaining({
|
||||
enabled: false,
|
||||
selectedQueueIds: []
|
||||
}));
|
||||
vi.mocked(useSettingsStore.getState).mockReturnValue(originalSettings);
|
||||
});
|
||||
|
||||
it('retains the UI item when backend removal fails', async () => {
|
||||
useDownloadStore.setState({
|
||||
downloads: [
|
||||
|
||||
Reference in New Issue
Block a user