fix: make buildUserDownloads async to resolve test failures
Build and Push Docker Image / build (push) Successful in 50s
Docs Check / Markdown lint (push) Successful in 1m11s
CI / Security audit (push) Successful in 2m1s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 1m23s
CI / Tests & coverage (push) Successful in 2m30s
Docs Check / Mermaid diagram parse check (push) Successful in 2m9s
CI / Swagger Validation & Coverage (push) Successful in 4m2s
Build and Push Docker Image / build (push) Successful in 50s
Docs Check / Markdown lint (push) Successful in 1m11s
CI / Security audit (push) Successful in 2m1s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 1m23s
CI / Tests & coverage (push) Successful in 2m30s
Docs Check / Mermaid diagram parse check (push) Successful in 2m9s
CI / Swagger Validation & Coverage (push) Successful in 4m2s
The buildUserDownloads function was calling async matcher functions without awaiting them, causing Promise objects to be returned instead of resolved arrays. This resulted in empty download lists and 17 failing tests. - Made buildUserDownloads async - Added await to matchSabSlots, matchSabHistory, and matchTorrents calls - Updated unit tests to await buildUserDownloads calls All 759 tests now pass.
This commit is contained in:
@@ -26,7 +26,7 @@ const DownloadMatcher = require('./DownloadMatcher');
|
||||
* @param {string} options.ombiBaseUrl - Ombi base URL for link generation (optional)
|
||||
* @returns {Array} Array of download objects for the user
|
||||
*/
|
||||
function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmin, showAll, seriesMap, moviesMap, sonarrTagMap, radarrTagMap, embyUserMap, ombiRetriever, ombiBaseUrl }) {
|
||||
async function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmin, showAll, seriesMap, moviesMap, sonarrTagMap, radarrTagMap, embyUserMap, ombiRetriever, ombiBaseUrl }) {
|
||||
// Input validation
|
||||
if (!cacheSnapshot || typeof cacheSnapshot !== 'object') {
|
||||
console.error('[DownloadBuilder] Invalid cacheSnapshot provided');
|
||||
@@ -74,7 +74,7 @@ function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmi
|
||||
const seenDownloadKeys = new Set();
|
||||
|
||||
if (sabnzbdQueue.data?.queue?.slots) {
|
||||
const sabMatches = DownloadMatcher.matchSabSlots(sabnzbdQueue.data.queue.slots, context);
|
||||
const sabMatches = await DownloadMatcher.matchSabSlots(sabnzbdQueue.data.queue.slots, context);
|
||||
for (const dl of sabMatches) {
|
||||
const key = `${dl.type}:${dl.title}`;
|
||||
if (!seenDownloadKeys.has(key)) {
|
||||
@@ -85,7 +85,7 @@ function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmi
|
||||
}
|
||||
|
||||
if (sabnzbdHistory.data?.history?.slots) {
|
||||
const sabHistoryMatches = DownloadMatcher.matchSabHistory(sabnzbdHistory.data.history.slots, context);
|
||||
const sabHistoryMatches = await DownloadMatcher.matchSabHistory(sabnzbdHistory.data.history.slots, context);
|
||||
for (const dl of sabHistoryMatches) {
|
||||
const key = `${dl.type}:${dl.title}`;
|
||||
if (!seenDownloadKeys.has(key)) {
|
||||
@@ -95,7 +95,7 @@ function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmi
|
||||
}
|
||||
}
|
||||
|
||||
const torrentMatches = DownloadMatcher.matchTorrents(qbittorrentTorrents, context);
|
||||
const torrentMatches = await DownloadMatcher.matchTorrents(qbittorrentTorrents, context);
|
||||
for (const dl of torrentMatches) {
|
||||
const key = `${dl.type}:${dl.title}`;
|
||||
if (!seenDownloadKeys.has(key)) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { buildUserDownloads } from '../../../server/services/DownloadBuilder.js';
|
||||
|
||||
describe('buildUserDownloads', () => {
|
||||
// All tests in this suite are async because buildUserDownloads is async
|
||||
const username = 'alice';
|
||||
const usernameSanitized = 'alice';
|
||||
const isAdmin = false;
|
||||
@@ -56,7 +57,7 @@ describe('buildUserDownloads', () => {
|
||||
}]
|
||||
]);
|
||||
|
||||
it('returns empty array when no downloads match user', () => {
|
||||
it('returns empty array when no downloads match user', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: { data: { queue: { slots: [] } } },
|
||||
sabnzbdHistory: { data: { history: { slots: [] } } },
|
||||
@@ -67,7 +68,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -82,7 +83,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns empty array for null/undefined cache data', () => {
|
||||
it('returns empty array for null/undefined cache data', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: null,
|
||||
sabnzbdHistory: null,
|
||||
@@ -93,7 +94,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: null
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -108,7 +109,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('matches SABnzbd queue slot to Sonarr series for tagged user', () => {
|
||||
it('matches SABnzbd queue slot to Sonarr series for tagged user', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -151,7 +152,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -178,7 +179,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result[0].episodes).toBeInstanceOf(Array);
|
||||
});
|
||||
|
||||
it('matches SABnzbd queue slot to Radarr movie for tagged user', () => {
|
||||
it('matches SABnzbd queue slot to Radarr movie for tagged user', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -220,7 +221,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -246,7 +247,7 @@ describe('buildUserDownloads', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('matches qBittorrent torrent to Sonarr series for tagged user', () => {
|
||||
it('matches qBittorrent torrent to Sonarr series for tagged user', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: { data: { queue: { slots: [] } } },
|
||||
sabnzbdHistory: { data: { history: { slots: [] } } },
|
||||
@@ -280,7 +281,7 @@ describe('buildUserDownloads', () => {
|
||||
}]
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -307,7 +308,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result[0]).toHaveProperty('eta');
|
||||
});
|
||||
|
||||
it('includes admin-specific fields when isAdmin is true', () => {
|
||||
it('includes admin-specific fields when isAdmin is true', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -350,7 +351,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin: true,
|
||||
@@ -377,7 +378,7 @@ describe('buildUserDownloads', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('filters by user tag when showAll is false', () => {
|
||||
it('filters by user tag when showAll is false', async () => {
|
||||
const bobSeriesMap = new Map([
|
||||
[2, {
|
||||
id: 2,
|
||||
@@ -429,7 +430,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username: 'alice',
|
||||
usernameSanitized: 'alice',
|
||||
isAdmin: false,
|
||||
@@ -445,7 +446,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('shows all tagged downloads when showAll is true (admin mode)', () => {
|
||||
it('shows all tagged downloads when showAll is true (admin mode)', async () => {
|
||||
const bobSeriesMap = new Map([
|
||||
[2, {
|
||||
id: 2,
|
||||
@@ -497,7 +498,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username: 'alice',
|
||||
usernameSanitized: 'alice',
|
||||
isAdmin: true,
|
||||
@@ -520,7 +521,7 @@ describe('buildUserDownloads', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('includes importIssues when present in queue record', () => {
|
||||
it('includes importIssues when present in queue record', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -567,7 +568,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -583,7 +584,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result[0].importIssues).toEqual(['Sample needs repack', 'Disk space low']);
|
||||
});
|
||||
|
||||
it('handles mixed series and movie downloads', () => {
|
||||
it('handles mixed series and movie downloads', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -651,7 +652,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -668,7 +669,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result[1].type).toBe('movie');
|
||||
});
|
||||
|
||||
it('prevents duplicate downloads when same item matches multiple sources', () => {
|
||||
it('prevents duplicate downloads when same item matches multiple sources', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -730,7 +731,7 @@ describe('buildUserDownloads', () => {
|
||||
}]
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -746,7 +747,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('matches SABnzbd history slots to completed downloads', () => {
|
||||
it('matches SABnzbd history slots to completed downloads', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: { data: { queue: { slots: [] } } },
|
||||
sabnzbdHistory: {
|
||||
@@ -782,7 +783,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -803,7 +804,7 @@ describe('buildUserDownloads', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not display unmatched torrents', () => {
|
||||
it('does not display unmatched torrents', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: { data: { queue: { slots: [] } } },
|
||||
sabnzbdHistory: { data: { history: { slots: [] } } },
|
||||
@@ -824,7 +825,7 @@ describe('buildUserDownloads', () => {
|
||||
}]
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin: false,
|
||||
@@ -840,7 +841,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('includes sonarrLink and radarrLink when available', () => {
|
||||
it('includes sonarrLink and radarrLink when available', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -908,7 +909,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin: true,
|
||||
|
||||
Reference in New Issue
Block a user