test: remediate test suite, enable skipped frontend/SSE tests, and add comprehensive unit tests
Build and Push Docker Image / build (push) Successful in 53s
Docs Check / Markdown lint (push) Successful in 1m36s
Licence Check / Licence compatibility and copyright header verification (push) Failing after 2m31s
CI / Security audit (push) Successful in 2m55s
Docs Check / Mermaid diagram parse check (push) Successful in 3m24s
CI / Swagger Validation & Coverage (push) Successful in 3m30s
CI / Tests & coverage (push) Successful in 3m50s

- Deleted redundant unit test file tests/unit/dashboard.test.js
- Enabled skipped frontend DOM state and API tests in tests/frontend/state.test.js
- Fixed Supertest client socket abort exception in SSE stream integration tests using new graceful testClose parameter
- Consolidated duplicate helpers in server/routes/history.js and server/utils/arrRetrievers.js to unified services TagMatcher and DownloadAssembler
- Added comprehensive unit tests for loadSecrets.js, PollingSonarrRetriever.js, PollingRadarrRetriever.js, and ombiHelpers.js
- Achieved a 100% Vitest pass rate (834/834 tests) with robust code coverage
This commit is contained in:
2026-05-22 13:33:21 +01:00
parent d3d085d614
commit 4aa3590017
16 changed files with 1058 additions and 761 deletions
+46 -35
View File
@@ -23,6 +23,7 @@ import { createApp } from '../../server/app.js';
const require = createRequire(import.meta.url);
const cache = require('../../server/utils/cache.js');
const arrRetrieverRegistry = require('../../server/utils/arrRetrievers.js');
// ---------------------------------------------------------------------------
// Constants
@@ -56,11 +57,11 @@ const EMBY_ADMIN_BODY = {
const OMBI_REQUESTS = {
movie: [
{ id: 1, title: 'Test Movie', requestedUser: { userName: 'testuser' }, requestedByAlias: 'testuser', type: 'movie' },
{ id: 2, title: 'Admin Movie', requestedUser: { userName: 'admin' }, requestedByAlias: 'admin', type: 'movie' }
{ id: 2, title: 'Admin Movie', requestedUser: { userName: 'adminuser' }, requestedByAlias: 'adminuser', type: 'movie' }
],
tv: [
{ id: 3, title: 'Test Show', requestedUser: { userName: 'testuser' }, requestedByAlias: 'testuser', type: 'tv' },
{ id: 4, title: 'Admin Show', requestedUser: { userName: 'admin' }, requestedByAlias: 'admin', type: 'tv' }
{ id: 4, title: 'Admin Show', requestedUser: { userName: 'adminuser' }, requestedByAlias: 'adminuser', type: 'tv' }
]
};
@@ -143,15 +144,21 @@ afterEach(() => {
// GET /api/ombi/requests
// ---------------------------------------------------------------------------
// TODO: Unskip these tests once arrRetrieverRegistry can be properly mocked.
// The tests need to control the return value of getOmbiRequests() to test
// user filtering, showAll parameter, and edge cases reliably.
describe.skip('GET /api/ombi/requests', () => {
describe('GET /api/ombi/requests', () => {
let app;
beforeEach(() => {
app = makeApp();
setupOmbiRequestMocks();
// Reset the singleton registry so it re-initializes on each request
arrRetrieverRegistry.retrievers.clear();
arrRetrieverRegistry.initialized = false;
});
afterEach(() => {
arrRetrieverRegistry.retrievers.clear();
arrRetrieverRegistry.initialized = false;
});
it('returns 401 when not authenticated', async () => {
@@ -162,7 +169,7 @@ describe.skip('GET /api/ombi/requests', () => {
});
it('returns user-filtered requests for non-admin users', async () => {
const cookies = await authenticateUser(app, 'TestUser', false);
const { cookies } = await authenticateUser(app, 'TestUser', false);
const res = await request(app)
.get('/api/ombi/requests')
@@ -180,7 +187,7 @@ describe.skip('GET /api/ombi/requests', () => {
});
it('returns all requests when admin with showAll=true', async () => {
const cookies = await authenticateUser(app, 'AdminUser', true);
const { cookies } = await authenticateUser(app, 'AdminUser', true);
const res = await request(app)
.get('/api/ombi/requests?showAll=true')
@@ -196,7 +203,7 @@ describe.skip('GET /api/ombi/requests', () => {
});
it('returns user-filtered requests when admin with showAll=false', async () => {
const cookies = await authenticateUser(app, 'AdminUser', true);
const { cookies } = await authenticateUser(app, 'AdminUser', true);
const res = await request(app)
.get('/api/ombi/requests?showAll=false')
@@ -207,14 +214,14 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.isAdmin).toBe(true);
expect(res.body.showAll).toBe(false);
expect(res.body.requests.movie).toHaveLength(1);
expect(res.body.requests.movie[0].requestedUser.userName).toBe('admin');
expect(res.body.requests.movie[0].requestedUser.userName).toBe('adminuser');
expect(res.body.requests.tv).toHaveLength(1);
expect(res.body.requests.tv[0].requestedUser.userName).toBe('admin');
expect(res.body.requests.tv[0].requestedUser.userName).toBe('adminuser');
expect(res.body.total).toBe(2);
});
it('returns user-filtered requests when admin without showAll parameter', async () => {
const cookies = await authenticateUser(app, 'AdminUser', true);
const { cookies } = await authenticateUser(app, 'AdminUser', true);
const res = await request(app)
.get('/api/ombi/requests')
@@ -223,10 +230,10 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.showAll).toBe(false);
expect(res.body.requests.movie).toHaveLength(1);
expect(res.body.requests.movie[0].requestedUser.userName).toBe('admin');
expect(res.body.requests.movie[0].requestedUser.userName).toBe('adminuser');
});
it.skip('handles case-insensitive username matching', async () => {
it('handles case-insensitive username matching', async () => {
const requestsWithMixedCase = [
{ id: 1, title: 'Test Movie', requestedUser: { userName: 'TestUser' }, requestedByAlias: 'TestUser', type: 'movie' },
{ id: 2, title: 'Admin Movie', requestedUser: { userName: 'ADMIN' }, requestedByAlias: 'ADMIN', type: 'movie' }
@@ -235,7 +242,7 @@ describe.skip('GET /api/ombi/requests', () => {
nock.cleanAll();
setupOmbiRequestMocks(requestsWithMixedCase, []);
const cookies = await authenticateUser(app, 'testuser', false);
const { cookies } = await authenticateUser(app, 'testuser', false);
const res = await request(app)
.get('/api/ombi/requests')
@@ -246,7 +253,7 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.requests.movie[0].requestedUser.userName).toBe('TestUser');
});
it.skip('handles missing requestedUser field gracefully', async () => {
it('handles missing requestedUser field gracefully', async () => {
const requestsWithMissingUser = [
{ id: 1, title: 'Test Movie', type: 'movie' }
];
@@ -254,7 +261,7 @@ describe.skip('GET /api/ombi/requests', () => {
nock.cleanAll();
setupOmbiRequestMocks(requestsWithMissingUser, []);
const cookies = await authenticateUser(app, 'TestUser', false);
const { cookies } = await authenticateUser(app, 'TestUser', false);
const res = await request(app)
.get('/api/ombi/requests')
@@ -265,11 +272,11 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.total).toBe(0);
});
it.skip('handles empty requests array', async () => {
it('handles empty requests array', async () => {
nock.cleanAll();
setupOmbiRequestMocks([], []);
const cookies = await authenticateUser(app, 'TestUser', false);
const { cookies } = await authenticateUser(app, 'TestUser', false);
const res = await request(app)
.get('/api/ombi/requests')
@@ -281,14 +288,15 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.total).toBe(0);
});
it.skip('handles object-format requestedUser with alias field', async () => {
it('handles object-format requestedUser with alias field', async () => {
const requestsWithAlias = [
{ id: 1, title: 'Test Movie', requestedUser: { alias: 'testuser' }, requestedByAlias: 'testuser', type: 'movie' }
];
nock.cleanAll();
setupOmbiRequestMocks(requestsWithAlias, []);
const cookies = await authenticateUser(app, 'TestUser', false);
const { cookies } = await authenticateUser(app, 'TestUser', false);
const res = await request(app)
.get('/api/ombi/requests')
@@ -299,14 +307,15 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.requests.movie[0].requestedUser.alias).toBe('testuser');
});
it.skip('handles object-format requestedUser with userName field', async () => {
it('handles object-format requestedUser with userName field', async () => {
const requestsWithUserName = [
{ id: 1, title: 'Test Movie', requestedUser: { userName: 'testuser' }, requestedByAlias: 'testuser', type: 'movie' }
];
nock.cleanAll();
setupOmbiRequestMocks(requestsWithUserName, []);
const cookies = await authenticateUser(app, 'TestUser', false);
const { cookies } = await authenticateUser(app, 'TestUser', false);
const res = await request(app)
.get('/api/ombi/requests')
@@ -317,14 +326,15 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.requests.movie[0].requestedUser.userName).toBe('testuser');
});
it.skip('handles object-format requestedUser with userAlias field', async () => {
it('handles object-format requestedUser with userAlias field', async () => {
const requestsWithUserAlias = [
{ id: 1, title: 'Test Movie', requestedUser: { userAlias: 'testuser' }, requestedByAlias: 'testuser', type: 'movie' }
];
nock.cleanAll();
setupOmbiRequestMocks(requestsWithUserAlias, []);
const cookies = await authenticateUser(app, 'TestUser', false);
const { cookies } = await authenticateUser(app, 'TestUser', false);
const res = await request(app)
.get('/api/ombi/requests')
@@ -335,14 +345,15 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.requests.movie[0].requestedUser.userAlias).toBe('testuser');
});
it.skip('handles object-format requestedUser with normalizedUserName field', async () => {
it('handles object-format requestedUser with normalizedUserName field', async () => {
const requestsWithNormalizedUserName = [
{ id: 1, title: 'Test Movie', requestedUser: { normalizedUserName: 'testuser' }, requestedByAlias: 'testuser', type: 'movie' }
];
nock.cleanAll();
setupOmbiRequestMocks(requestsWithNormalizedUserName, []);
const cookies = await authenticateUser(app, 'TestUser', false);
const { cookies } = await authenticateUser(app, 'TestUser', false);
const res = await request(app)
.get('/api/ombi/requests')
@@ -353,14 +364,15 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.requests.movie[0].requestedUser.normalizedUserName).toBe('testuser');
});
it.skip('handles requestedUser as null gracefully', async () => {
it('handles requestedUser as null gracefully', async () => {
const requestsWithNullUser = [
{ id: 1, title: 'Test Movie', requestedUser: null, requestedByAlias: 'otheruser', type: 'movie' }
];
nock.cleanAll();
setupOmbiRequestMocks(requestsWithNullUser, []);
const cookies = await authenticateUser(app, 'TestUser', false);
const { cookies } = await authenticateUser(app, 'TestUser', false);
const res = await request(app)
.get('/api/ombi/requests')
@@ -371,22 +383,23 @@ describe.skip('GET /api/ombi/requests', () => {
expect(res.body.total).toBe(0);
});
it.skip('handles requestedUser as empty object gracefully', async () => {
it('handles requestedUser as empty object gracefully', async () => {
const requestsWithEmptyObject = [
{ id: 1, title: 'Test Movie', requestedUser: {}, requestedByAlias: 'testuser', type: 'movie' }
];
nock.cleanAll();
setupOmbiRequestMocks(requestsWithEmptyObject, []);
const cookies = await authenticateUser(app, 'TestUser', false);
const { cookies } = await authenticateUser(app, 'TestUser', false);
const res = await request(app)
.get('/api/ombi/requests')
.set('Cookie', cookies)
.expect(200);
expect(res.body.requests.movie).toHaveLength(0);
expect(res.body.total).toBe(0);
expect(res.body.requests.movie).toHaveLength(1);
expect(res.body.total).toBe(1);
});
});
@@ -405,14 +418,12 @@ const FILTERED_TV_REQUESTS = [
describe('GET /api/ombi/requests query params', () => {
let app;
let arrRetrieverRegistry;
beforeEach(() => {
app = makeApp();
setupOmbiRequestMocks(FILTERED_MOVIE_REQUESTS, FILTERED_TV_REQUESTS);
// Reset the singleton registry so it re-initializes on each request
arrRetrieverRegistry = require('../../server/utils/arrRetrievers');
arrRetrieverRegistry.retrievers.clear();
arrRetrieverRegistry.initialized = false;
});