From f84c149fd96b98217a2dc6729f47197a6b775652 Mon Sep 17 00:00:00 2001 From: Flaminel Date: Mon, 26 Oct 2026 19:07:25 +0200 Subject: [PATCH] added whisparr v3 to e2e arr stack --- e2e/Makefile | 4 +- e2e/arr-seed/whisparr/config.xml | 19 +++++++ e2e/arr-seed/whisparr/whisparr3.db | Bin 0 -> 413696 bytes e2e/docker-compose.e2e.yml | 21 +++++++- e2e/scripts/seed-arr.sh | 47 ++++++++++++++---- e2e/scripts/setup-test-data.sh | 7 +-- e2e/tests/helpers/live-arr.ts | 8 ++- e2e/tests/helpers/mocks/torznab-stubs.ts | 1 + e2e/tests/helpers/seeker-live.ts | 25 ++++++++-- e2e/tests/helpers/test-config.ts | 5 +- e2e/wiremock-indexer/__files/torznab-caps.xml | 3 ++ 11 files changed, 117 insertions(+), 23 deletions(-) create mode 100644 e2e/arr-seed/whisparr/config.xml create mode 100644 e2e/arr-seed/whisparr/whisparr3.db diff --git a/e2e/Makefile b/e2e/Makefile index 53ef6c79..9f2e021e 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -13,7 +13,7 @@ APP_SERVICES = app nginx CORE_STACK = keycloak $(WIREMOCKS) CLIENTS_STACK = $(CORE_STACK) tracker qbittorrent transmission deluge utorrent rutorrent sabnews sabnzbd -ARR_STACK = $(CORE_STACK) wiremock-indexer tracker qbittorrent sonarr radarr +ARR_STACK = $(CORE_STACK) wiremock-indexer tracker qbittorrent sonarr radarr whisparr LAZYLIBRARIAN_STACK = $(CORE_STACK) wiremock-indexer tracker qbittorrent sabnews sabnzbd lazylibrarian setup: @@ -61,7 +61,7 @@ up-app: $(COMPOSE) up -d $(WAIT) $(APP_SERVICES) # Rebuilds e2e/arr-seed from scratch. -# Needs internet: Sonarr reads Skyhook and Radarr reads TMDB. +# Needs internet: each arr reads its metadata provider. seed-arr: bash ./scripts/seed-arr.sh diff --git a/e2e/arr-seed/whisparr/config.xml b/e2e/arr-seed/whisparr/config.xml new file mode 100644 index 00000000..6eba446c --- /dev/null +++ b/e2e/arr-seed/whisparr/config.xml @@ -0,0 +1,19 @@ + + * + 6970 + False + False + 0000000000000000000000000000e2e6 + External + DisabledForLocalAddresses + eros + info + + Whisparr + Docker + False + https://api.whisparr.com/v4/{route} + 9898 + + + \ No newline at end of file diff --git a/e2e/arr-seed/whisparr/whisparr3.db b/e2e/arr-seed/whisparr/whisparr3.db new file mode 100644 index 0000000000000000000000000000000000000000..532c6f651e0bf14f8738df820033bcd499c616e2 GIT binary patch literal 413696 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 literal 0 HcmV?d00001 diff --git a/e2e/docker-compose.e2e.yml b/e2e/docker-compose.e2e.yml index c58b3cd2..8d2aec54 100644 --- a/e2e/docker-compose.e2e.yml +++ b/e2e/docker-compose.e2e.yml @@ -213,7 +213,7 @@ services: retries: 12 start_period: 30s - # Real Sonarr and Radarr, restored from e2e/arr-seed by setup-test-data.sh. + # Real Sonarr, Radarr and Whisparr, restored from e2e/arr-seed by setup-test-data.sh. # Every run starts with the same library, API key, indexer and download client. # Regenerate the seed with `make seed-arr`. # The tags are pinned: a seed database is only valid for the version that wrote it. @@ -253,6 +253,25 @@ services: retries: 12 start_period: 30s + # Listens on 6970 from the seeded config.xml, since the tracker holds 6969. + whisparr: + image: ghcr.io/hotio/whisparr:v3-3.1.0.2116 + network_mode: host + environment: + PUID: "${E2E_PUID:-1000}" + PGID: "${E2E_PGID:-1000}" + TZ: "UTC" + volumes: + - ./test-data/whisparr-config:/config + - ./test-data/whisparr-movies:/movies + - ./test-data/downloads/qbittorrent:/downloads + healthcheck: + test: ["CMD-SHELL", "curl -fs -o /dev/null http://localhost:6970/ping || exit 1"] + interval: 5s + timeout: 3s + retries: 12 + start_period: 30s + # Real LazyLibrarian, restored from e2e/lazylibrarian-seed by setup-test-data.sh. # LazyLibrarian publishes no versions, so the tag names the upstream commit. # The seed database is only valid for the commit that wrote it. diff --git a/e2e/scripts/seed-arr.sh b/e2e/scripts/seed-arr.sh index 1859a3d8..a6454ad1 100644 --- a/e2e/scripts/seed-arr.sh +++ b/e2e/scripts/seed-arr.sh @@ -6,8 +6,8 @@ # Snapshots /config into e2e/arr-seed, which the e2e run restores. # CI therefore needs no network and every run sees the same library. # -# Needs internet: Sonarr reads Skyhook and Radarr reads TMDB. -# Stops the sonarr and radarr containers. +# Needs internet: each arr reads its metadata provider. +# Stops the sonarr, radarr and whisparr containers. # # Re-run it when the pinned image tags change, or when the library has to change. # @@ -20,8 +20,10 @@ COMPOSE="docker compose -f $HERE/docker-compose.e2e.yml" SONARR_URL="http://localhost:8989" RADARR_URL="http://localhost:7878" +WHISPARR_URL="http://localhost:6970" SONARR_API_KEY="0000000000000000000000000000e2e1" RADARR_API_KEY="0000000000000000000000000000e2e2" +WHISPARR_API_KEY="0000000000000000000000000000e2e6" # The mock Torznab indexer, and qBittorrent, both on the host network. INDEXER_URL="http://127.0.0.1:9500" @@ -39,6 +41,8 @@ RADARR_TMDB_ID=911430 # The Wild Robot, added second and left unmonitored. # The specs that need two candidates monitor it themselves and restore it after. RADARR_SECOND_TMDB_ID=1184918 +# Package Holiday (2004): a plain title, since the seed database is public. +WHISPARR_TMDB_ID=1253309 log() { echo "[seed-arr] $*" @@ -252,9 +256,25 @@ seed_radarr() { wait_for_items "$RADARR_URL" "$RADARR_API_KEY" '/api/v3/movie' 'radarr movies' } +seed_whisparr() { + log "configuring whisparr" + arr_post "$WHISPARR_URL" "$WHISPARR_API_KEY" '/api/v3/rootfolder' '{"path":"/movies"}' > /dev/null + add_indexer "$WHISPARR_URL" "$WHISPARR_API_KEY" '[6000]' + add_download_client "$WHISPARR_URL" "$WHISPARR_API_KEY" 'movieCategory' 'whisparr' + + log "adding the whisparr movie" + local movie + movie=$(arr_get "$WHISPARR_URL" "$WHISPARR_API_KEY" "/api/v3/movie/lookup?term=tmdb%3A$WHISPARR_TMDB_ID" \ + | merge_add_options '/movies' '{"searchForMovie":false}') + arr_post "$WHISPARR_URL" "$WHISPARR_API_KEY" '/api/v3/movie' "$movie" > /dev/null + + wait_for_items "$WHISPARR_URL" "$WHISPARR_API_KEY" '/api/v3/movie' 'whisparr movies' +} + # Keeps the database and config.xml, drops everything the arr rebuilds on start. +# Whisparr v3 names its database whisparr3.db. snapshot() { - local arr="$1" + local arr="$1" db="${2:-$1}" local src="$TEST_DATA/$arr-config" local dest="$ARR_SEED/$arr" @@ -265,15 +285,15 @@ snapshot() { # The arr leaves committed rows in the write-ahead log. # The log comes along and is folded into the database before it is dropped. - cp "$src/$arr.db" "$dest/$arr.db" + cp "$src/$db.db" "$dest/$db.db" for suffix in wal shm; do - if [[ -f "$src/$arr.db-$suffix" ]]; then - cp "$src/$arr.db-$suffix" "$dest/$arr.db-$suffix" + if [[ -f "$src/$db.db-$suffix" ]]; then + cp "$src/$db.db-$suffix" "$dest/$db.db-$suffix" fi done - sqlite3 "$dest/$arr.db" 'PRAGMA journal_mode=DELETE; VACUUM;' > /dev/null - rm -f "$dest/$arr.db-wal" "$dest/$arr.db-shm" + sqlite3 "$dest/$db.db" 'PRAGMA journal_mode=DELETE; VACUUM;' > /dev/null + rm -f "$dest/$db.db-wal" "$dest/$db.db-shm" } main() { @@ -281,23 +301,30 @@ main() { write_config_xml "$TEST_DATA/sonarr-config" 8989 "$SONARR_API_KEY" 'Sonarr' write_config_xml "$TEST_DATA/radarr-config" 7878 "$RADARR_API_KEY" 'Radarr' + write_config_xml "$TEST_DATA/whisparr-config" 6970 "$WHISPARR_API_KEY" 'Whisparr' # An arr tests the indexer and the download client before it saves them. # Both have to answer during seeding. log "starting the arrs" - $COMPOSE up -d --force-recreate wiremock-indexer qbittorrent sonarr radarr + $COMPOSE up -d --force-recreate wiremock-indexer qbittorrent sonarr radarr whisparr wait_for_arr "$SONARR_URL" 'sonarr' wait_for_arr "$RADARR_URL" 'radarr' + wait_for_arr "$WHISPARR_URL" 'whisparr' seed_sonarr seed_radarr + seed_whisparr log "stopping the arrs" - $COMPOSE stop wiremock-indexer qbittorrent sonarr radarr + $COMPOSE stop wiremock-indexer qbittorrent sonarr radarr whisparr snapshot sonarr snapshot radarr + snapshot whisparr whisparr3 + + # The seed is public, so it keeps the title and drops the synopsis, posters and cast. + sqlite3 "$ARR_SEED/whisparr/whisparr3.db" "UPDATE MovieMetadata SET Overview = '', Images = '[]', Credits = '[]'; VACUUM;" log "seed written to $ARR_SEED" } diff --git a/e2e/scripts/setup-test-data.sh b/e2e/scripts/setup-test-data.sh index 94de5241..afa29929 100644 --- a/e2e/scripts/setup-test-data.sh +++ b/e2e/scripts/setup-test-data.sh @@ -16,12 +16,13 @@ ARR_SEED="$HERE/arr-seed" rm -rf "$TEST_DATA/torznab-src" # Media an arr imports during a run, wiped so its library always starts empty. -rm -rf "$TEST_DATA/sonarr-tv" "$TEST_DATA/radarr-movies" "$TEST_DATA/lazylibrarian-books" +rm -rf "$TEST_DATA/sonarr-tv" "$TEST_DATA/radarr-movies" "$TEST_DATA/whisparr-movies" "$TEST_DATA/lazylibrarian-books" mkdir -p \ "$TEST_DATA/torznab-src" \ "$TEST_DATA/sonarr-tv" \ "$TEST_DATA/radarr-movies" \ + "$TEST_DATA/whisparr-movies" \ "$TEST_DATA/lazylibrarian-books" \ "$TEST_DATA/downloads/qbittorrent" \ "$TEST_DATA/downloads/transmission" \ @@ -100,9 +101,9 @@ script = Default priority = 0 EOF -# Restore Sonarr and Radarr from the committed seed. +# Restore Sonarr, Radarr and Whisparr from the committed seed. # The arrs write to /config on every start, so the run gets a throwaway copy. -for arr in sonarr radarr; do +for arr in sonarr radarr whisparr; do if [[ -d "$ARR_SEED/$arr" ]]; then rm -rf "$TEST_DATA/$arr-config" cp -R "$ARR_SEED/$arr" "$TEST_DATA/$arr-config" diff --git a/e2e/tests/helpers/live-arr.ts b/e2e/tests/helpers/live-arr.ts index 2750bc24..81763023 100644 --- a/e2e/tests/helpers/live-arr.ts +++ b/e2e/tests/helpers/live-arr.ts @@ -2,7 +2,7 @@ import { TEST_CONFIG } from './test-config'; import { WireMockClient } from './mocks/wiremock-client'; /** - * Direct access to the real Sonarr and Radarr containers. + * Direct access to the real Sonarr, Radarr and Whisparr v3 containers. * * The specs drive Cleanuparr through its own API, like every other spec. * This is the other side of the contract: what the arr itself ended up with. @@ -141,5 +141,11 @@ export const liveRadarr = new LiveArr( 'includeUnknownMovieItems=true&includeMovie=true', ); +export const liveWhisparr = new LiveArr( + TEST_CONFIG.liveArr.whisparrUrl, + TEST_CONFIG.liveArr.whisparrApiKey, + 'includeUnknownMovieItems=true&includeMovie=true', +); + /** The fake Torznab indexer, kept out of MockServers: other suites lack its container. */ export const indexerMock = new WireMockClient(TEST_CONFIG.mocks.indexerAdminUrl); diff --git a/e2e/tests/helpers/mocks/torznab-stubs.ts b/e2e/tests/helpers/mocks/torznab-stubs.ts index fcfb35f2..acbf3d78 100644 --- a/e2e/tests/helpers/mocks/torznab-stubs.ts +++ b/e2e/tests/helpers/mocks/torznab-stubs.ts @@ -16,6 +16,7 @@ export type TorznabSearchMode = 'tvsearch' | 'movie' | 'search'; /** Torznab category. The seeded arrs are configured for the parent categories. */ export const TORZNAB_TV_CATEGORY = 5040; export const TORZNAB_MOVIE_CATEGORY = 2040; +export const TORZNAB_XXX_CATEGORY = 6040; /** * The advertised release size. diff --git a/e2e/tests/helpers/seeker-live.ts b/e2e/tests/helpers/seeker-live.ts index 45d4f068..4d2204b5 100644 --- a/e2e/tests/helpers/seeker-live.ts +++ b/e2e/tests/helpers/seeker-live.ts @@ -6,9 +6,9 @@ import { buildSingleFileTorrent } from './torrent-fixtures'; import type { CleanuparrApi } from './api'; import { buildDownloadClientPayload } from './api/download-client'; import type { ArrType } from './api/arr'; -import { LiveArr, indexerMock, liveRadarr, liveSonarr } from './live-arr'; +import { LiveArr, indexerMock, liveRadarr, liveSonarr, liveWhisparr } from './live-arr'; import { QBittorrentDriver } from './torrent-clients/qbittorrent'; -import { TORZNAB_MOVIE_CATEGORY, TORZNAB_TV_CATEGORY } from './mocks/torznab-stubs'; +import { TORZNAB_MOVIE_CATEGORY, TORZNAB_TV_CATEGORY, TORZNAB_XXX_CATEGORY } from './mocks/torznab-stubs'; import { mkdirShared, writeFileShared } from './shared-volume'; /** @@ -97,6 +97,21 @@ export const RADARR: SeededArr = { downloadCategory: 'radarr', }; +export const WHISPARR: SeededArr = { + type: 'whisparr', + arr: liveWhisparr, + url: TEST_CONFIG.liveArr.whisparrUrl, + apiKey: TEST_CONFIG.liveArr.whisparrApiKey, + version: 3, + searchMode: 'movie', + category: TORZNAB_XXX_CATEGORY, + release: `Package.Holiday.2004.1080p.WEB-DL.x264-E2E${RUN_TAG}`, + itemId: 1, + itemPath: 'movie', + seededTitle: TEST_CONFIG.liveArr.seededWhisparrTitle, + downloadCategory: 'whisparr', +}; + export const qbittorrent = new QBittorrentDriver(); /** Outside the qBittorrent save path, so a decoy download never finds its data. */ @@ -289,15 +304,15 @@ export async function waitForArrQueue(arr: LiveArr, downloadId: string, timeoutM } /** - * Empties both arr queues and the download client. + * Empties every arr queue and the download client. * * The Seeker skips an item that is already downloading. * The client is purged first, or the arr re-adds the torrent on its next refresh. */ export async function resetDownloads(): Promise { await qbittorrent.clearAllTorrents(); - await Promise.all([liveSonarr.clearQueue(), liveRadarr.clearQueue()]); - await Promise.all([waitForEmptyQueue(liveSonarr), waitForEmptyQueue(liveRadarr)]); + await Promise.all([liveSonarr.clearQueue(), liveRadarr.clearQueue(), liveWhisparr.clearQueue()]); + await Promise.all([waitForEmptyQueue(liveSonarr), waitForEmptyQueue(liveRadarr), waitForEmptyQueue(liveWhisparr)]); } async function waitForEmptyQueue(arr: LiveArr, timeoutMs = 120_000): Promise { diff --git a/e2e/tests/helpers/test-config.ts b/e2e/tests/helpers/test-config.ts index fb8649a5..63c1539d 100644 --- a/e2e/tests/helpers/test-config.ts +++ b/e2e/tests/helpers/test-config.ts @@ -26,7 +26,7 @@ export const TEST_CONFIG = { indexerAdminUrl: 'http://localhost:9500/__admin', }, - // The real Sonarr and Radarr containers. + // The real Sonarr, Radarr and Whisparr v3 containers. // Their api keys come from the committed seed in e2e/arr-seed. liveArr: { sonarrUrl: 'http://localhost:8989', @@ -35,6 +35,9 @@ export const TEST_CONFIG = { radarrApiKey: '0000000000000000000000000000e2e2', seededSeriesTitle: 'Agatha All Along', seededMovieTitle: 'F1', + whisparrUrl: 'http://localhost:6970', + whisparrApiKey: '0000000000000000000000000000e2e6', + seededWhisparrTitle: 'Package Holiday', }, // Real qBittorrent container: the API key seeded into diff --git a/e2e/wiremock-indexer/__files/torznab-caps.xml b/e2e/wiremock-indexer/__files/torznab-caps.xml index 10125f9e..33ad55cb 100644 --- a/e2e/wiremock-indexer/__files/torznab-caps.xml +++ b/e2e/wiremock-indexer/__files/torznab-caps.xml @@ -17,5 +17,8 @@ + + +