From cc0e34b3d1f5e549a22dc07a0816d6c14c7cf2f2 Mon Sep 17 00:00:00 2001 From: Gronod Date: Tue, 19 May 2026 12:12:44 +0100 Subject: [PATCH] fix: convert all test files from jest to vitest and fix QBittorrentClient import - Convert RTorrentClient.test.js to use vi.mock() instead of jest.mock() - Convert QBittorrentClient.test.js to use vi.mock() instead of jest.mock() - Convert SABnzbdClient.test.js to use vi.mock() instead of jest.mock() - Convert TransmissionClient.test.js to use vi.mock() instead of jest.mock() - Convert downloadClients.test.js to use vi.mock() instead of jest.mock() - Convert integration/downloadClients.test.js to use vi.mock() instead of jest.mock() - Fix legacy qbittorrent.test.js to import QBittorrentClient from new location - Add getRtorrentInstances mock to downloadClients.test.js - Add RTORRENT_INSTANCES to integration test environment variables --- tests/integration/downloadClients.test.js | 19 +++++-- tests/unit/clients/QBittorrentClient.test.js | 27 +++++----- tests/unit/clients/RTorrentClient.test.js | 13 ++--- tests/unit/clients/SABnzbdClient.test.js | 21 ++++---- tests/unit/clients/TransmissionClient.test.js | 23 ++++---- tests/unit/downloadClients.test.js | 54 ++++++++++--------- tests/unit/qbittorrent.test.js | 3 +- 7 files changed, 89 insertions(+), 71 deletions(-) diff --git a/tests/integration/downloadClients.test.js b/tests/integration/downloadClients.test.js index 2a45425..a76b06d 100644 --- a/tests/integration/downloadClients.test.js +++ b/tests/integration/downloadClients.test.js @@ -1,10 +1,11 @@ // Copyright (c) 2026 Gordon Bolton. MIT License. -const { +const { initializeClients, getAllDownloads, getDownloadsByClientType, testAllConnections } = require('../../server/utils/downloadClients'); +const { vi } = require('vitest'); // Mock environment variables for testing process.env.SABNZBD_INSTANCES = JSON.stringify([ @@ -36,10 +37,20 @@ process.env.TRANSMISSION_INSTANCES = JSON.stringify([ } ]); +process.env.RTORRENT_INSTANCES = JSON.stringify([ + { + id: 'test-rtorrent', + name: 'Test rTorrent', + url: 'http://localhost:8080/RPC2', + username: 'rtorrent', + password: 'rtorrent' + } +]); + // Mock axios to prevent actual network calls -jest.mock('axios'); -jest.mock('../../server/utils/logger', () => ({ - logToFile: jest.fn() +vi.mock('axios'); +vi.mock('../../server/utils/logger', () => ({ + logToFile: vi.fn() })); describe('Download Clients Integration Tests', () => { diff --git a/tests/unit/clients/QBittorrentClient.test.js b/tests/unit/clients/QBittorrentClient.test.js index 780a3ef..117853f 100644 --- a/tests/unit/clients/QBittorrentClient.test.js +++ b/tests/unit/clients/QBittorrentClient.test.js @@ -1,11 +1,12 @@ // Copyright (c) 2026 Gordon Bolton. MIT License. const QBittorrentClient = require('../../../server/clients/QBittorrentClient'); const axios = require('axios'); +const { vi } = require('vitest'); // Mock axios -jest.mock('axios'); -jest.mock('../../../server/utils/logger', () => ({ - logToFile: jest.fn() +vi.mock('axios'); +vi.mock('../../../server/utils/logger', () => ({ + logToFile: vi.fn() })); describe('QBittorrentClient', () => { @@ -22,9 +23,9 @@ describe('QBittorrentClient', () => { }; client = new QBittorrentClient(mockConfig); - + // Clear all mocks - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('Constructor', () => { @@ -89,11 +90,11 @@ describe('QBittorrentClient', () => { describe('Connection Test', () => { it('should test connection successfully', async () => { // Mock login success - client.login = jest.fn().mockResolvedValue(true); - + client.login = vi.fn().mockResolvedValue(true); + // Mock version request const mockResponse = { data: 'v4.3.5' }; - client.makeRequest = jest.fn().mockResolvedValue(mockResponse); + client.makeRequest = vi.fn().mockResolvedValue(mockResponse); const result = await client.testConnection(); @@ -102,7 +103,7 @@ describe('QBittorrentClient', () => { }); it('should handle connection test failure', async () => { - client.login = jest.fn().mockRejectedValue(new Error('Auth failed')); + client.login = vi.fn().mockRejectedValue(new Error('Auth failed')); const result = await client.testConnection(); @@ -200,15 +201,15 @@ describe('QBittorrentClient', () => { const authError = { response: { status: 403 } }; - + // Second login attempt succeeds - client.login = jest.fn() + client.login = vi.fn() .mockResolvedValueOnce(false) .mockResolvedValueOnce(true); - + // Retry request succeeds const successResponse = { data: 'success' }; - axios.get = jest.fn() + axios.get = vi.fn() .mockRejectedValueOnce(authError) .mockResolvedValueOnce(successResponse); diff --git a/tests/unit/clients/RTorrentClient.test.js b/tests/unit/clients/RTorrentClient.test.js index bae78a7..101dae2 100644 --- a/tests/unit/clients/RTorrentClient.test.js +++ b/tests/unit/clients/RTorrentClient.test.js @@ -1,13 +1,14 @@ // Copyright (c) 2026 Gordon Bolton. MIT License. const RTorrentClient = require('../../../server/clients/RTorrentClient'); const xmlrpc = require('xmlrpc'); +const { vi } = require('vitest'); -jest.mock('xmlrpc', () => ({ - createClient: jest.fn() +vi.mock('xmlrpc', () => ({ + createClient: vi.fn() })); -jest.mock('../../../server/utils/logger', () => ({ - logToFile: jest.fn() +vi.mock('../../../server/utils/logger', () => ({ + logToFile: vi.fn() })); describe('RTorrentClient', () => { @@ -16,7 +17,7 @@ describe('RTorrentClient', () => { let mockMethodCall; beforeEach(() => { - mockMethodCall = jest.fn(); + mockMethodCall = vi.fn(); xmlrpc.createClient.mockReturnValue({ methodCall: mockMethodCall }); @@ -30,7 +31,7 @@ describe('RTorrentClient', () => { }; client = new RTorrentClient(mockConfig); - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('Constructor', () => { diff --git a/tests/unit/clients/SABnzbdClient.test.js b/tests/unit/clients/SABnzbdClient.test.js index 32c6073..11a9d3b 100644 --- a/tests/unit/clients/SABnzbdClient.test.js +++ b/tests/unit/clients/SABnzbdClient.test.js @@ -1,11 +1,12 @@ // Copyright (c) 2026 Gordon Bolton. MIT License. const SABnzbdClient = require('../../../server/clients/SABnzbdClient'); const axios = require('axios'); +const { vi } = require('vitest'); // Mock axios -jest.mock('axios'); -jest.mock('../../../server/utils/logger', () => ({ - logToFile: jest.fn() +vi.mock('axios'); +vi.mock('../../../server/utils/logger', () => ({ + logToFile: vi.fn() })); describe('SABnzbdClient', () => { @@ -23,7 +24,7 @@ describe('SABnzbdClient', () => { client = new SABnzbdClient(mockConfig); // Clear all mocks - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('Constructor', () => { @@ -42,7 +43,7 @@ describe('SABnzbdClient', () => { data: { version: '3.6.1' } }; - client.makeRequest = jest.fn().mockResolvedValue(mockResponse); + client.makeRequest = vi.fn().mockResolvedValue(mockResponse); const result = await client.testConnection(); @@ -51,7 +52,7 @@ describe('SABnzbdClient', () => { }); it('should handle connection test failure', async () => { - client.makeRequest = jest.fn().mockRejectedValue(new Error('Connection failed')); + client.makeRequest = vi.fn().mockRejectedValue(new Error('Connection failed')); const result = await client.testConnection(); @@ -244,7 +245,7 @@ describe('SABnzbdClient', () => { } }; - client.makeRequest = jest.fn() + client.makeRequest = vi.fn() .mockResolvedValueOnce(mockQueueResponse) .mockResolvedValueOnce(mockHistoryResponse); @@ -258,7 +259,7 @@ describe('SABnzbdClient', () => { }); it('should handle API errors gracefully', async () => { - client.makeRequest = jest.fn().mockRejectedValue(new Error('API Error')); + client.makeRequest = vi.fn().mockRejectedValue(new Error('API Error')); const downloads = await client.getActiveDownloads(); @@ -280,7 +281,7 @@ describe('SABnzbdClient', () => { } }; - client.makeRequest = jest.fn().mockResolvedValue(mockResponse); + client.makeRequest = vi.fn().mockResolvedValue(mockResponse); const status = await client.getClientStatus(); @@ -294,7 +295,7 @@ describe('SABnzbdClient', () => { }); it('should handle status request errors', async () => { - client.makeRequest = jest.fn().mockRejectedValue(new Error('Status error')); + client.makeRequest = vi.fn().mockRejectedValue(new Error('Status error')); const status = await client.getClientStatus(); diff --git a/tests/unit/clients/TransmissionClient.test.js b/tests/unit/clients/TransmissionClient.test.js index 8d22716..b4496d7 100644 --- a/tests/unit/clients/TransmissionClient.test.js +++ b/tests/unit/clients/TransmissionClient.test.js @@ -1,11 +1,12 @@ // Copyright (c) 2026 Gordon Bolton. MIT License. const TransmissionClient = require('../../../server/clients/TransmissionClient'); const axios = require('axios'); +const { vi } = require('vitest'); // Mock axios -jest.mock('axios'); -jest.mock('../../../server/utils/logger', () => ({ - logToFile: jest.fn() +vi.mock('axios'); +vi.mock('../../../server/utils/logger', () => ({ + logToFile: vi.fn() })); describe('TransmissionClient', () => { @@ -24,7 +25,7 @@ describe('TransmissionClient', () => { client = new TransmissionClient(mockConfig); // Clear all mocks - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('Constructor', () => { @@ -44,7 +45,7 @@ describe('TransmissionClient', () => { data: { result: 'success', arguments: {} } }; - client.makeRequest = jest.fn().mockResolvedValue(mockResponse); + client.makeRequest = vi.fn().mockResolvedValue(mockResponse); const result = await client.testConnection(); @@ -53,7 +54,7 @@ describe('TransmissionClient', () => { }); it('should handle connection test failure', async () => { - client.makeRequest = jest.fn().mockRejectedValue(new Error('Connection failed')); + client.makeRequest = vi.fn().mockRejectedValue(new Error('Connection failed')); const result = await client.testConnection(); @@ -308,7 +309,7 @@ describe('TransmissionClient', () => { } }; - client.makeRequest = jest.fn().mockResolvedValue(mockResponse); + client.makeRequest = vi.fn().mockResolvedValue(mockResponse); const downloads = await client.getActiveDownloads(); @@ -330,7 +331,7 @@ describe('TransmissionClient', () => { }); it('should handle API errors gracefully', async () => { - client.makeRequest = jest.fn().mockRejectedValue(new Error('API Error')); + client.makeRequest = vi.fn().mockRejectedValue(new Error('API Error')); const downloads = await client.getActiveDownloads(); @@ -345,7 +346,7 @@ describe('TransmissionClient', () => { } }; - client.makeRequest = jest.fn().mockResolvedValue(mockResponse); + client.makeRequest = vi.fn().mockResolvedValue(mockResponse); const downloads = await client.getActiveDownloads(); @@ -377,7 +378,7 @@ describe('TransmissionClient', () => { } }; - client.makeRequest = jest.fn() + client.makeRequest = vi.fn() .mockResolvedValueOnce(mockSessionResponse) .mockResolvedValueOnce(mockStatsResponse); @@ -392,7 +393,7 @@ describe('TransmissionClient', () => { }); it('should handle status request errors', async () => { - client.makeRequest = jest.fn().mockRejectedValue(new Error('Status error')); + client.makeRequest = vi.fn().mockRejectedValue(new Error('Status error')); const status = await client.getClientStatus(); diff --git a/tests/unit/downloadClients.test.js b/tests/unit/downloadClients.test.js index 25207fa..53e97c1 100644 --- a/tests/unit/downloadClients.test.js +++ b/tests/unit/downloadClients.test.js @@ -1,6 +1,6 @@ // Copyright (c) 2026 Gordon Bolton. MIT License. -const { - DownloadClientRegistry, +const { + DownloadClientRegistry, registry, initializeClients, getAllClients, @@ -11,55 +11,57 @@ const { testAllConnections, getAllClientStatuses } = require('../../server/utils/downloadClients'); +const { vi } = require('vitest'); // Mock config and clients -jest.mock('../../server/utils/config', () => ({ - getSABnzbdInstances: jest.fn(), - getQbittorrentInstances: jest.fn(), - getTransmissionInstances: jest.fn() +vi.mock('../../server/utils/config', () => ({ + getSABnzbdInstances: vi.fn(), + getQbittorrentInstances: vi.fn(), + getTransmissionInstances: vi.fn(), + getRtorrentInstances: vi.fn() })); -jest.mock('../../server/utils/logger', () => ({ - logToFile: jest.fn() +vi.mock('../../server/utils/logger', () => ({ + logToFile: vi.fn() })); -jest.mock('../../server/clients/SABnzbdClient', () => { - return jest.fn().mockImplementation((config) => ({ +vi.mock('../../server/clients/SABnzbdClient', () => { + return vi.fn().mockImplementation((config) => ({ getClientType: () => 'sabnzbd', getInstanceId: () => config.id, name: config.name, - getActiveDownloads: jest.fn().mockResolvedValue([ + getActiveDownloads: vi.fn().mockResolvedValue([ { id: 'sab1', title: 'SAB Download 1', client: 'sabnzbd' } ]), - testConnection: jest.fn().mockResolvedValue(true), - getClientStatus: jest.fn().mockResolvedValue({ status: 'active' }) + testConnection: vi.fn().mockResolvedValue(true), + getClientStatus: vi.fn().mockResolvedValue({ status: 'active' }) })); }); -jest.mock('../../server/clients/QBittorrentClient', () => { - return jest.fn().mockImplementation((config) => ({ +vi.mock('../../server/clients/QBittorrentClient', () => { + return vi.fn().mockImplementation((config) => ({ getClientType: () => 'qbittorrent', getInstanceId: () => config.id, name: config.name, - getActiveDownloads: jest.fn().mockResolvedValue([ + getActiveDownloads: vi.fn().mockResolvedValue([ { id: 'qb1', title: 'QB Download 1', client: 'qbittorrent' } ]), - testConnection: jest.fn().mockResolvedValue(true), - getClientStatus: jest.fn().mockResolvedValue({ status: 'active' }), - resetFallbackFlag: jest.fn() + testConnection: vi.fn().mockResolvedValue(true), + getClientStatus: vi.fn().mockResolvedValue({ status: 'active' }), + resetFallbackFlag: vi.fn() })); }); -jest.mock('../../server/clients/TransmissionClient', () => { - return jest.fn().mockImplementation((config) => ({ +vi.mock('../../server/clients/TransmissionClient', () => { + return vi.fn().mockImplementation((config) => ({ getClientType: () => 'transmission', getInstanceId: () => config.id, name: config.name, - getActiveDownloads: jest.fn().mockResolvedValue([ + getActiveDownloads: vi.fn().mockResolvedValue([ { id: 'trans1', title: 'Trans Download 1', client: 'transmission' } ]), - testConnection: jest.fn().mockResolvedValue(true), - getClientStatus: jest.fn().mockResolvedValue({ status: 'active' }) + testConnection: vi.fn().mockResolvedValue(true), + getClientStatus: vi.fn().mockResolvedValue({ status: 'active' }) })); }); @@ -69,7 +71,7 @@ describe('DownloadClientRegistry', () => { beforeEach(() => { testRegistry = new DownloadClientRegistry(); - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('Initialization', () => { @@ -291,7 +293,7 @@ describe('DownloadClientRegistry', () => { describe('Convenience Functions', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('should delegate to singleton registry', async () => { diff --git a/tests/unit/qbittorrent.test.js b/tests/unit/qbittorrent.test.js index 3001fe3..86647f5 100644 --- a/tests/unit/qbittorrent.test.js +++ b/tests/unit/qbittorrent.test.js @@ -7,7 +7,8 @@ * dashboard card rendering so correctness matters for UX. */ -import { mapTorrentToDownload, formatBytes, formatSpeed, formatEta, QBittorrentClient } from '../../server/utils/qbittorrent.js'; +import { mapTorrentToDownload, formatBytes, formatSpeed, formatEta } from '../../server/utils/qbittorrent.js'; +import QBittorrentClient from '../../server/clients/QBittorrentClient.js'; import nock from 'nock'; // Minimal torrent fixture that satisfies mapTorrentToDownload's expectations