// Copyright (c) 2026 Gordon Bolton. MIT License. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import nock from 'nock'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); const { decorateDownloadsWithArrLinks } = require('../../server/utils/ombiHelpers.js'); const arrRetrieverRegistry = require('../../server/utils/arrRetrievers.js'); const SONARR_BASE = 'https://sonarr-decor.test'; const RADARR_BASE = 'https://radarr-decor.test'; describe('decorateDownloadsWithArrLinks Integration Tests', () => { beforeEach(() => { vi.restoreAllMocks(); nock.cleanAll(); // Reset the singleton retrievers registry so we can inject our test instances arrRetrieverRegistry.retrievers.clear(); arrRetrieverRegistry.initialized = false; // Configure test environment variables for retrievers process.env.SONARR_INSTANCES = JSON.stringify([ { id: 'sonarr-1', name: 'Test Sonarr', url: SONARR_BASE, apiKey: 'sonarr-key' } ]); process.env.RADARR_INSTANCES = JSON.stringify([ { id: 'radarr-1', name: 'Test Radarr', url: RADARR_BASE, apiKey: 'radarr-key' } ]); }); afterEach(() => { nock.cleanAll(); delete process.env.SONARR_INSTANCES; delete process.env.RADARR_INSTANCES; arrRetrieverRegistry.retrievers.clear(); arrRetrieverRegistry.initialized = false; }); it('decorates a series download with Sonarr link matching on title', async () => { // Mock Sonarr series query nock(SONARR_BASE) .get('/api/v3/series') .reply(200, [ { id: 42, title: 'The Mandalorian', titleSlug: 'the-mandalorian' } ]); // Mock Radarr movie query (empty) nock(RADARR_BASE) .get('/api/v3/movie') .reply(200, []); const downloads = [ { title: 'The.Mandalorian.S01E01.1080p', type: 'series', seriesName: 'The Mandalorian', arrSeriesId: null } ]; await decorateDownloadsWithArrLinks(downloads, true); expect(downloads[0].arrLink).toBe(`${SONARR_BASE}/series/the-mandalorian`); expect(downloads[0].arrType).toBe('sonarr'); }); it('decorates a movie download with Radarr link matching on content ID', async () => { // Mock Sonarr series query (empty) nock(SONARR_BASE) .get('/api/v3/series') .reply(200, []); // Mock Radarr movie query with matching ID nock(RADARR_BASE) .get('/api/v3/movie') .reply(200, [ { id: 99, title: 'Blade Runner 2049', titleSlug: 'blade-runner-2049' } ]); const downloads = [ { title: 'Blade.Runner.2049.2017.1080p', type: 'movie', movieName: 'Blade Runner 2049', arrInstanceUrl: RADARR_BASE, arrContentId: 99 } ]; await decorateDownloadsWithArrLinks(downloads, true); expect(downloads[0].arrLink).toBe(`${RADARR_BASE}/movie/blade-runner-2049`); expect(downloads[0].arrType).toBe('radarr'); }); it('skips decoration entirely when isAdmin is false', async () => { const downloads = [ { title: 'The.Mandalorian.S01E01.1080p', type: 'series', seriesName: 'The Mandalorian' } ]; // No nocks are set up, so any HTTP calls would throw or error await decorateDownloadsWithArrLinks(downloads, false); expect(downloads[0].arrLink).toBeUndefined(); expect(downloads[0].arrType).toBeUndefined(); }); it('handles empty downloads array gracefully', async () => { // No mock setups needed, should complete without throwing await expect(decorateDownloadsWithArrLinks([], true)).resolves.not.toThrow(); }); it('handles external API fetch failures gracefully without failing the decoration pipeline', async () => { // Mock Sonarr series query throwing connection error nock(SONARR_BASE) .get('/api/v3/series') .replyWithError('connection refused'); // Mock Radarr movie query throwing timeout error nock(RADARR_BASE) .get('/api/v3/movie') .replyWithError('timeout'); const downloads = [ { title: 'The.Mandalorian.S01E01.1080p', type: 'series', seriesName: 'The Mandalorian' } ]; await expect(decorateDownloadsWithArrLinks(downloads, true)).resolves.not.toThrow(); // No links decorated since the fetch failed expect(downloads[0].arrLink).toBeUndefined(); expect(downloads[0].arrType).toBeUndefined(); }); });