diff --git a/.gitignore b/.gitignore index 4a9c5305..6c60a2fe 100644 --- a/.gitignore +++ b/.gitignore @@ -166,6 +166,10 @@ ENV/ env.bak/ venv.bak/ +# ...but the e2e platform test profiles live in an env/ dir and must be tracked +!tests/e2e/platform/env/ +!tests/e2e/platform/env/*.env + # Spyder project settings .spyderproject .spyproject diff --git a/tests/e2e/platform/env/baseline.env b/tests/e2e/platform/env/baseline.env new file mode 100644 index 00000000..50f2aa2c --- /dev/null +++ b/tests/e2e/platform/env/baseline.env @@ -0,0 +1,8 @@ +# Profile: baseline +# Direct connection to the fake Anna's Archive, no bypasser, no proxy, system DNS. +# Exercises clusters 2/3 (search + AA parsing) and the download happy path. +COMPOSE_PROFILES= +SM_AA_URL=http://mock-aa +SM_USE_CF_BYPASS=false +SM_PROXY_MODE=none +E2E_PROFILE=baseline diff --git a/tests/e2e/platform/env/bypasser-disabled.env b/tests/e2e/platform/env/bypasser-disabled.env new file mode 100644 index 00000000..53237a4c --- /dev/null +++ b/tests/e2e/platform/env/bypasser-disabled.env @@ -0,0 +1,9 @@ +# Profile: bypasser-disabled (cluster 1 negative control) +# AA behind the Cloudflare gate but the bypasser is OFF. Proves the gate really +# challenges: search must FAIL clearly here, which is what makes the +# bypasser-external success meaningful (and guards #202/#410 "uses AA when CF +# bypass disabled" / "config not adhered"). +COMPOSE_PROFILES=bypasser-external +SM_AA_URL=http://cf.mock.test +SM_USE_CF_BYPASS=false +E2E_PROFILE=bypasser-disabled diff --git a/tests/e2e/platform/env/bypasser-external.env b/tests/e2e/platform/env/bypasser-external.env new file mode 100644 index 00000000..9fcfba1f --- /dev/null +++ b/tests/e2e/platform/env/bypasser-external.env @@ -0,0 +1,13 @@ +# Profile: bypasser-external (cluster 1) +# AA is served behind a Cloudflare gate. The app must detect the challenge and +# route through the external bypasser (mock FlareSolverr), which returns solved +# HTML + cf_clearance. Regression surface: #284 #226 #202 #1030 #410 #369 #267. +COMPOSE_PROFILES=bypasser-external +SM_AA_URL=http://cf.mock.test +SM_USE_CF_BYPASS=true +# Must be set explicitly — shelfmark does NOT derive it from EXT_BYPASSER_URL. +# Without it the app falls back to the in-image Chrome bypasser and never calls +# FlareSolverr, so this profile would silently exercise the wrong path. +SM_USING_EXTERNAL_BYPASSER=true +SM_EXT_BYPASSER_URL=http://mock-flaresolverr +E2E_PROFILE=bypasser-external diff --git a/tests/e2e/platform/env/client-deluge.env b/tests/e2e/platform/env/client-deluge.env new file mode 100644 index 00000000..87933f19 --- /dev/null +++ b/tests/e2e/platform/env/client-deluge.env @@ -0,0 +1,13 @@ +# Profile: client-deluge (cluster 5 — real Deluge client) +# shelfmark talks to deluge-web (default WebUI password "deluge"), which connects +# to the bundled daemon — no daemon auth-file juggling. +COMPOSE_PROFILES=client-deluge +SM_PROWLARR_ENABLED=true +SM_PROWLARR_URL=http://mock-prowlarr +SM_PROWLARR_API_KEY=e2e-test-key +SM_PROWLARR_TORRENT_CLIENT=deluge +SM_DELUGE_HOST=deluge +SM_DELUGE_PORT=8112 +SM_DELUGE_PASSWORD=deluge +E2E_DOWNLOAD_TIMEOUT=180 +E2E_PROFILE=client-deluge diff --git a/tests/e2e/platform/env/client-transmission.env b/tests/e2e/platform/env/client-transmission.env new file mode 100644 index 00000000..61bbeaf4 --- /dev/null +++ b/tests/e2e/platform/env/client-transmission.env @@ -0,0 +1,13 @@ +# Profile: client-transmission (cluster 5 — real Transmission client) +# Same mock Prowlarr + webseed torrent as `full`, but the torrent goes to a real +# Transmission. Lean (no Chrome/CF/DoH), so it boots fast. +COMPOSE_PROFILES=client-transmission +SM_PROWLARR_ENABLED=true +SM_PROWLARR_URL=http://mock-prowlarr +SM_PROWLARR_API_KEY=e2e-test-key +SM_PROWLARR_TORRENT_CLIENT=transmission +SM_TRANSMISSION_URL=http://transmission:9091 +SM_TRANSMISSION_USERNAME=admin +SM_TRANSMISSION_PASSWORD=admin +E2E_DOWNLOAD_TIMEOUT=180 +E2E_PROFILE=client-transmission diff --git a/tests/e2e/platform/env/dns-blocked.env b/tests/e2e/platform/env/dns-blocked.env new file mode 100644 index 00000000..ea0bb0c7 --- /dev/null +++ b/tests/e2e/platform/env/dns-blocked.env @@ -0,0 +1,10 @@ +# Profile: dns-blocked (config cluster: ISP DNS block — #1028 regression) +# The container's *system* resolver (coredns-blocked) NXDOMAINs the AA host, +# but the app's *custom* DNS resolver (coredns) resolves it fine. A correct app +# reaches AA via custom DNS; a regressed one (system DNS in subprocess) fails. +COMPOSE_PROFILES=dns-manual,dns-blocked +SM_AA_URL=http://aa.mock.test +SM_SYSTEM_DNS=172.30.0.22 +SM_CUSTOM_DNS=manual +SM_CUSTOM_DNS_MANUAL=172.30.0.20 +E2E_PROFILE=dns-blocked diff --git a/tests/e2e/platform/env/dns-doh.env b/tests/e2e/platform/env/dns-doh.env new file mode 100644 index 00000000..ca37592a --- /dev/null +++ b/tests/e2e/platform/env/dns-doh.env @@ -0,0 +1,17 @@ +# Profile: dns-doh (config cluster — in-stack DNS-over-HTTPS, end to end) +# +# Proves the app's DoH path resolves a name over real HTTPS: +# * system resolver = coredns-blocked, which NXDOMAINs aa.mock.test, so the host +# can ONLY be resolved via DoH; +# * CUSTOM_DNS=cloudflare + USE_DOH=true => the app queries the cloudflare DoH +# endpoint, which docker-compose `extra_hosts` redirects to the in-stack +# mock-doh (HTTPS, self-signed). CERTIFICATE_VALIDATION=disabled accepts it. +# * mock-doh maps aa.mock.test -> mock-aa, so the search reaches AA — only +# possible if DoH resolution worked. +COMPOSE_PROFILES=dns-doh,dns-blocked +SM_AA_URL=http://aa.mock.test +SM_SYSTEM_DNS=172.30.0.22 +SM_CUSTOM_DNS=cloudflare +SM_USE_DOH=true +SM_CERTIFICATE_VALIDATION=disabled +E2E_PROFILE=dns-doh diff --git a/tests/e2e/platform/env/dns-manual.env b/tests/e2e/platform/env/dns-manual.env new file mode 100644 index 00000000..dd0834cf --- /dev/null +++ b/tests/e2e/platform/env/dns-manual.env @@ -0,0 +1,9 @@ +# Profile: dns-manual (config cluster: custom DNS) +# AA host only resolves via the controllable coredns server; the app is told to +# use it as a manual DNS provider. Verifies custom-DNS resolution (#108-style). +COMPOSE_PROFILES=dns-manual +SM_AA_URL=http://aa.mock.test +SM_CUSTOM_DNS=manual +SM_CUSTOM_DNS_MANUAL=172.30.0.20 +SM_SYSTEM_DNS=172.30.0.20 +E2E_PROFILE=dns-manual diff --git a/tests/e2e/platform/env/full.env b/tests/e2e/platform/env/full.env new file mode 100644 index 00000000..ad070368 --- /dev/null +++ b/tests/e2e/platform/env/full.env @@ -0,0 +1,42 @@ +# Profile: full (the "everything real" heavy profile — nightly/manual only) +# +# Test book: Moby-Dick (Herman Melville, public domain). +# +# What it exercises end to end, with NO mock bypasser: +# 1. REAL Chrome internal bypasser: AA search/detail are reachable (mock-aa), but +# the AA *slow-download* links point through the Cloudflare gate. So a real +# DOWNLOAD forces the in-image headless Chrome (seleniumbase CDP) to solve the +# JS challenge and harvest cf_clearance — the literal "spin a chrome browser" +# path. Success = Moby-Dick lands in /books, which is only possible if Chrome +# solved the gate. +# 2. DoH enabled (USE_DOH) so the DNS-over-HTTPS code path runs at boot. +# 3. REAL download client: a mock Prowlarr returns a webseed .torrent; a real +# qBittorrent downloads the payload over HTTP from mock-aa and shelfmark +# moves the completed file into /books. +# +# Heavy (Chrome + qBittorrent), so it is excluded from the PR matrix. +COMPOSE_PROFILES=full + +# AA search/detail reachable directly; the DOWNLOAD goes through the CF gate so the +# internal Chrome bypasser must solve it (search uses no bypasser, by design). +SM_AA_URL=http://mock-aa +SM_SLOW_DOWNLOAD_BASE=http://cf.mock.test +SM_USE_CF_BYPASS=true +SM_USING_EXTERNAL_BYPASSER=false + +# DNS over HTTPS on. +SM_USE_DOH=true + +# Prowlarr indexer + real qBittorrent client. +SM_PROWLARR_ENABLED=true +SM_PROWLARR_URL=http://mock-prowlarr +SM_PROWLARR_API_KEY=e2e-test-key +SM_PROWLARR_TORRENT_CLIENT=qbittorrent +SM_QBITTORRENT_URL=http://qbittorrent:8080 +SM_QBITTORRENT_USERNAME=admin +SM_QBITTORRENT_PASSWORD=adminadmin +SM_QBITTORRENT_CATEGORY=books + +# The real client download needs more headroom than a direct HTTP fetch. +E2E_DOWNLOAD_TIMEOUT=300 +E2E_PROFILE=full diff --git a/tests/e2e/platform/env/proxy-http.env b/tests/e2e/platform/env/proxy-http.env new file mode 100644 index 00000000..657bb5e0 --- /dev/null +++ b/tests/e2e/platform/env/proxy-http.env @@ -0,0 +1,8 @@ +# Profile: proxy-http (config cluster: HTTP proxy — #956 proxy-ignored regressions) +# All app egress routes through tinyproxy. Search/download must still succeed, +# and traffic must actually traverse the proxy. +COMPOSE_PROFILES=proxy-http +SM_AA_URL=http://mock-aa +SM_PROXY_MODE=http +SM_HTTP_PROXY=http://tinyproxy:8888 +E2E_PROFILE=proxy-http diff --git a/tests/e2e/platform/env/proxy-socks.env b/tests/e2e/platform/env/proxy-socks.env new file mode 100644 index 00000000..295a6d48 --- /dev/null +++ b/tests/e2e/platform/env/proxy-socks.env @@ -0,0 +1,7 @@ +# Profile: proxy-socks (config cluster: SOCKS5 proxy) +# All app egress routes through a SOCKS5 proxy (microsocks). +COMPOSE_PROFILES=proxy-socks +SM_AA_URL=http://mock-aa +SM_PROXY_MODE=socks5 +SM_SOCKS5_PROXY=socks5://microsocks:1080 +E2E_PROFILE=proxy-socks diff --git a/tests/e2e/platform/env/tor.env b/tests/e2e/platform/env/tor.env new file mode 100644 index 00000000..4cc1a34a --- /dev/null +++ b/tests/e2e/platform/env/tor.env @@ -0,0 +1,9 @@ +# Profile: tor (cluster 1/6: Tor boot — #1021 #940 #801 regressions) +# Boots the app with transparent Tor routing. The platform asserts the container +# reaches a healthy /api/health WITHOUT boot-looping or pegging CPU, and that the +# app reports DNS/proxy as Tor-managed. (Real Tor egress is slow/flaky in CI, so +# this profile is startup-correctness focused; see tor-full for real egress.) +COMPOSE_PROFILES= +SM_USING_TOR=true +SM_AA_URL=http://mock-aa +E2E_PROFILE=tor