Some checks failed
- 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
223 lines
6.0 KiB
JavaScript
223 lines
6.0 KiB
JavaScript
// Copyright (c) 2026 Gordon Bolton. MIT License.
|
|
const QBittorrentClient = require('../../../server/clients/QBittorrentClient');
|
|
const axios = require('axios');
|
|
const { vi } = require('vitest');
|
|
|
|
// Mock axios
|
|
vi.mock('axios');
|
|
vi.mock('../../../server/utils/logger', () => ({
|
|
logToFile: vi.fn()
|
|
}));
|
|
|
|
describe('QBittorrentClient', () => {
|
|
let client;
|
|
let mockConfig;
|
|
|
|
beforeEach(() => {
|
|
mockConfig = {
|
|
id: 'test-qb',
|
|
name: 'Test qBittorrent',
|
|
url: 'http://localhost:8080',
|
|
username: 'admin',
|
|
password: 'adminadmin'
|
|
};
|
|
|
|
client = new QBittorrentClient(mockConfig);
|
|
|
|
// Clear all mocks
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('Constructor', () => {
|
|
it('should initialize with correct properties', () => {
|
|
expect(client.getClientType()).toBe('qbittorrent');
|
|
expect(client.getInstanceId()).toBe('test-qb');
|
|
expect(client.name).toBe('Test qBittorrent');
|
|
expect(client.url).toBe('http://localhost:8080');
|
|
expect(client.authCookie).toBeNull();
|
|
expect(client.lastRid).toBe(0);
|
|
expect(client.torrentMap).toBeInstanceOf(Map);
|
|
expect(client.fallbackThisCycle).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('Authentication', () => {
|
|
it('should login successfully with valid credentials', async () => {
|
|
const mockResponse = {
|
|
headers: {
|
|
'set-cookie': ['SID=test-cookie']
|
|
}
|
|
};
|
|
|
|
axios.post.mockResolvedValue(mockResponse);
|
|
|
|
const result = await client.login();
|
|
|
|
expect(result).toBe(true);
|
|
expect(client.authCookie).toBe('SID=test-cookie');
|
|
expect(axios.post).toHaveBeenCalledWith(
|
|
'http://localhost:8080/api/v2/auth/login',
|
|
'username=admin&password=adminadmin',
|
|
expect.objectContaining({
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should handle login failure', async () => {
|
|
const mockResponse = {
|
|
headers: {}
|
|
};
|
|
|
|
axios.post.mockResolvedValue(mockResponse);
|
|
|
|
const result = await client.login();
|
|
|
|
expect(result).toBe(false);
|
|
expect(client.authCookie).toBeNull();
|
|
});
|
|
|
|
it('should handle login error', async () => {
|
|
axios.post.mockRejectedValue(new Error('Network error'));
|
|
|
|
const result = await client.login();
|
|
|
|
expect(result).toBe(false);
|
|
expect(client.authCookie).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('Connection Test', () => {
|
|
it('should test connection successfully', async () => {
|
|
// Mock login success
|
|
client.login = vi.fn().mockResolvedValue(true);
|
|
|
|
// Mock version request
|
|
const mockResponse = { data: 'v4.3.5' };
|
|
client.makeRequest = vi.fn().mockResolvedValue(mockResponse);
|
|
|
|
const result = await client.testConnection();
|
|
|
|
expect(result).toBe(true);
|
|
expect(client.makeRequest).toHaveBeenCalledWith('/api/v2/app/version');
|
|
});
|
|
|
|
it('should handle connection test failure', async () => {
|
|
client.login = vi.fn().mockRejectedValue(new Error('Auth failed'));
|
|
|
|
const result = await client.testConnection();
|
|
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('Download Normalization', () => {
|
|
it('should normalize torrent data correctly', () => {
|
|
const torrent = {
|
|
hash: 'abc123',
|
|
name: 'Test Torrent',
|
|
state: 'downloading',
|
|
progress: 0.75,
|
|
size: 1000000000,
|
|
completed: 750000000,
|
|
dlspeed: 1048576,
|
|
eta: 3600,
|
|
category: 'movies',
|
|
tags: 'movie,hd',
|
|
content_path: '/downloads/test',
|
|
added_on: 1640995200
|
|
};
|
|
|
|
const normalized = client.normalizeDownload(torrent);
|
|
|
|
expect(normalized).toEqual({
|
|
id: 'abc123',
|
|
title: 'Test Torrent',
|
|
type: 'torrent',
|
|
client: 'qbittorrent',
|
|
instanceId: 'test-qb',
|
|
instanceName: 'Test qBittorrent',
|
|
status: 'Downloading',
|
|
progress: 75,
|
|
size: 1000000000,
|
|
downloaded: 750000000,
|
|
speed: 1048576,
|
|
eta: 3600,
|
|
category: 'movies',
|
|
tags: ['movie', 'hd'],
|
|
savePath: '/downloads/test',
|
|
addedOn: '2022-01-01T00:00:00.000Z',
|
|
raw: torrent
|
|
});
|
|
});
|
|
|
|
it('should handle unknown torrent states', () => {
|
|
const torrent = {
|
|
hash: 'abc123',
|
|
name: 'Test Torrent',
|
|
state: 'unknown_state',
|
|
progress: 0.5,
|
|
size: 1000000,
|
|
completed: 500000,
|
|
dlspeed: 0,
|
|
eta: -1
|
|
};
|
|
|
|
const normalized = client.normalizeDownload(torrent);
|
|
|
|
expect(normalized.status).toBe('unknown_state');
|
|
expect(normalized.eta).toBeNull();
|
|
});
|
|
|
|
it('should handle missing completed field', () => {
|
|
const torrent = {
|
|
hash: 'abc123',
|
|
name: 'Test Torrent',
|
|
state: 'downloading',
|
|
progress: 0.5,
|
|
size: 1000000,
|
|
dlspeed: 0
|
|
};
|
|
|
|
const normalized = client.normalizeDownload(torrent);
|
|
|
|
expect(normalized.downloaded).toBe(500000);
|
|
});
|
|
});
|
|
|
|
describe('Fallback Flag Management', () => {
|
|
it('should reset fallback flag', () => {
|
|
client.fallbackThisCycle = true;
|
|
client.resetFallbackFlag();
|
|
expect(client.fallbackThisCycle).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('Error Handling', () => {
|
|
it('should handle makeRequest authentication failure', async () => {
|
|
client.authCookie = 'invalid-cookie';
|
|
|
|
// First call fails with 403
|
|
const authError = {
|
|
response: { status: 403 }
|
|
};
|
|
|
|
// Second login attempt succeeds
|
|
client.login = vi.fn()
|
|
.mockResolvedValueOnce(false)
|
|
.mockResolvedValueOnce(true);
|
|
|
|
// Retry request succeeds
|
|
const successResponse = { data: 'success' };
|
|
axios.get = vi.fn()
|
|
.mockRejectedValueOnce(authError)
|
|
.mockResolvedValueOnce(successResponse);
|
|
|
|
const result = await client.makeRequest('/test');
|
|
|
|
expect(result).toEqual(successResponse);
|
|
expect(client.login).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|
|
});
|