added whisparr v3 seeker e2e tests

This commit is contained in:
Flaminel committed 2026-10-28 22:27:52 +02:00
1 parent 7078ceca53
commit 28dbdcdbb7
4 files changed
+117 -2

No files matched your search

@@ -3,6 +3,7 @@ import { indexerMock } from '../helpers/live-arr';
import {
RADARR,
SONARR,
WHISPARR,
TRANSITION_TIMEOUT,
TRIGGERED_SEARCH_TIMEOUT,
arrangeInstance,
@@ -42,7 +43,7 @@ test.describe('Seeker grab on demand', () => {
await resetLiveArrState();
});
for (const target of [SONARR, RADARR]) {
for (const target of [SONARR, RADARR, WHISPARR]) {
test(`records the real ${target.type} grab on the search event`, async ({ api }) => {
test.setTimeout(TEST_TIMEOUT);
@@ -4,6 +4,7 @@ import { indexerMock } from '../helpers/live-arr';
import {
RADARR,
SONARR,
WHISPARR,
addDecoyDownloads,
arrangeInstance,
arrangeQueueCleaner,
@@ -472,3 +473,56 @@ test.describe('Seeker monitored-only filters on Sonarr', () => {
expect(event?.itemTitle).toBe(`${SONARR.seededTitle} S01`);
});
});
test.describe('Seeker candidate filters on Whisparr v3', () => {
let library: Array<Record<string, unknown>> = [];
async function mutateWhisparrMovie(mutate: (movie: Record<string, unknown>) => Record<string, unknown>): Promise<void> {
const movie = await WHISPARR.arr.get<Record<string, unknown>>(`/api/v3/movie/${WHISPARR.itemId}`);
await WHISPARR.arr.put(`/api/v3/movie/${WHISPARR.itemId}`, mutate({ ...movie }));
}
test.beforeEach(async () => {
await resetLiveArrState();
library = await snapshotLibrary(WHISPARR);
await indexerMock.stubMany(grabbableRelease(WHISPARR.searchMode, WHISPARR.release, WHISPARR.category).mappings);
});
test.afterEach(async ({ api }) => {
await teardownInstances(api);
await restoreLibrary(WHISPARR, library);
await resetLiveArrState();
});
test('searches the seeded movie when nothing filters it out', async ({ api }) => {
const instanceId = await arrangeInstance(api, WHISPARR);
await triggerSeeker(api);
await expect.poll(async () => (await listSearchEvents(api, instanceId)).length, { timeout: SETTLE_MS }).toBe(1);
const event = await firstSearchEvent(api, instanceId);
expect(event?.itemTitle).toContain(WHISPARR.seededTitle);
});
test('skips a movie carrying a skipped tag', async ({ api }) => {
const tagId = await WHISPARR.arr.ensureTag(SKIP_TAG);
const instanceId = await arrangeInstance(api, WHISPARR, { instance: { skipTags: [SKIP_TAG] } });
await mutateWhisparrMovie((movie) => ({ ...movie, tags: [tagId] }));
await expectNoSearch(api, instanceId, SETTLE_MS);
});
test('skips an unmonitored movie when monitoredOnly is on', async ({ api }) => {
const instanceId = await arrangeInstance(api, WHISPARR, { instance: { monitoredOnly: true } });
await mutateWhisparrMovie((movie) => ({ ...movie, monitored: false }));
await expectNoSearch(api, instanceId, SETTLE_MS);
});
test('searches a movie released long ago at the longest grace period', async ({ api }) => {
const instanceId = await arrangeInstance(api, WHISPARR, { config: { postReleaseGraceHours: 72 } });
await triggerSeeker(api);
await expect.poll(async () => (await listSearchEvents(api, instanceId)).length, { timeout: SETTLE_MS }).toBe(1);
});
});
@@ -5,6 +5,7 @@ import {
RADARR,
RUN_TAG,
SONARR,
WHISPARR,
TRIGGERED_SEARCH_TIMEOUT,
arrangeInstance,
listSearchEvents,
@@ -172,3 +173,31 @@ test.describe('Seeker preview and funnel', () => {
expect(res.status).toBe(404);
});
});
test.describe('Seeker preview on Whisparr v3', () => {
let library: Array<Record<string, unknown>> = [];
test.beforeEach(async () => {
await resetLiveArrState();
library = await snapshotLibrary(WHISPARR);
await indexerMock.stub(torznabSearchStub(WHISPARR.searchMode, []));
});
test.afterEach(async ({ api }) => {
await teardownInstances(api);
await restoreLibrary(WHISPARR, library);
await resetLiveArrState();
});
test('lists the seeded movie next up as missing with funnel counts', async ({ api }) => {
const instanceId = await arrangeInstance(api, WHISPARR);
const preview = await getPreview(api, instanceId);
const next = preview.nextUp.find((u) => u.name === WHISPARR.seededTitle);
expect(next?.reason).toBe('Missing');
expect(preview.funnel.libraryItems).toBe(library.length);
expect(preview.funnel.eligibleUnits).toBeGreaterThan(0);
expect(await listSearchEvents(api, instanceId)).toHaveLength(0);
});
});
+32 -1
View File
@@ -1,4 +1,5 @@
import { test, expect } from '../fixtures/base';
import { test, expect, TEST_CONFIG } from '../fixtures/base';
import { ArrStubs } from '../helpers/mocks';
test.describe.serial('Seeker — config', () => {
test('returns default seeker config', async ({ api }) => {
@@ -20,6 +21,36 @@ test.describe.serial('Seeker — config', () => {
expect(body).not.toHaveProperty('useCustomFormatScore');
});
test('lists a Whisparr v3 instance but not a v2 one', async ({ api, mocks }) => {
await mocks.arr.stub(ArrStubs.arrHealthStub());
const created: string[] = [];
try {
for (const version of [2, 3]) {
const res = await api.arr.createInstance('whisparr', {
name: `whisparr-v${version}-e2e`,
url: TEST_CONFIG.mocks.arrUrl,
apiKey: 'e2e-test-key',
version,
enabled: true,
});
expect(res.status).toBeLessThan(300);
created.push((await res.json()).id);
}
const [v2Id, v3Id] = created;
const config = await (await api.seeker.getConfig()).json();
const listed = config.instances.map((i: { arrInstanceId: string }) => i.arrInstanceId);
expect(listed).toContain(v3Id);
expect(listed).not.toContain(v2Id);
} finally {
for (const id of created) {
await api.arr.deleteInstance('whisparr', id);
}
}
});
test('updates seeker config', async ({ api }) => {
const current = await (await api.seeker.getConfig()).json();