test(e2e): cross-process harness over the §12 seams (#66)
ci / rust (push) Failing after 1m36s
ci / web (push) Successful in 26s
e2e / e2e (push) Failing after 59s

This commit was merged in pull request #66.
This commit is contained in:
2026-08-22 21:26:16 +01:00
parent c7b57b13e6
commit e9c055675b
14 changed files with 681 additions and 61 deletions
+32
View File
@@ -21,6 +21,7 @@ pub const ENV_PROWLARR_URL: &str = "ARR_PROWLARR_URL";
pub const ENV_PROWLARR_API_KEY: &str = "ARR_PROWLARR_API_KEY";
pub const ENV_TRANSMISSION_URL: &str = "ARR_TRANSMISSION_URL";
pub const ENV_TMDB_API_KEY: &str = "ARR_TMDB_API_KEY";
pub const ENV_TMDB_URL: &str = "ARR_TMDB_URL";
pub const ENV_JELLYFIN_URL: &str = "ARR_JELLYFIN_URL";
pub const ENV_JELLYFIN_API_KEY: &str = "ARR_JELLYFIN_API_KEY";
pub const ENV_NTFY_URL: &str = "ARR_NTFY_URL";
@@ -89,6 +90,7 @@ pub struct EnvOverrides {
pub prowlarr_api_key: Option<String>,
pub transmission_url: Option<String>,
pub tmdb_api_key: Option<String>,
pub tmdb_url: Option<String>,
pub jellyfin_url: Option<String>,
pub jellyfin_api_key: Option<String>,
pub ntfy_url: Option<String>,
@@ -105,6 +107,7 @@ impl EnvOverrides {
prowlarr_api_key: std::env::var(ENV_PROWLARR_API_KEY).ok(),
transmission_url: std::env::var(ENV_TRANSMISSION_URL).ok(),
tmdb_api_key: std::env::var(ENV_TMDB_API_KEY).ok(),
tmdb_url: std::env::var(ENV_TMDB_URL).ok(),
jellyfin_url: std::env::var(ENV_JELLYFIN_URL).ok(),
jellyfin_api_key: std::env::var(ENV_JELLYFIN_API_KEY).ok(),
ntfy_url: std::env::var(ENV_NTFY_URL).ok(),
@@ -122,6 +125,9 @@ pub struct Config {
pub prowlarr_api_key: Option<String>,
pub transmission_url: String,
pub tmdb_api_key: Option<String>,
/// E2E seam only, env-only. `None` means the client's built-in TMDB
/// address; DESIGN.md §10 keeps the real URL out of configuration.
pub tmdb_url: Option<String>,
pub jellyfin_url: String,
pub jellyfin_api_key: Option<String>,
pub ntfy_url: String,
@@ -170,6 +176,7 @@ impl Config {
.or(file.transmission_url)
.unwrap_or_else(|| DEFAULT_TRANSMISSION_URL.to_string()),
tmdb_api_key: env.tmdb_api_key,
tmdb_url: env.tmdb_url,
jellyfin_url: env
.jellyfin_url
.or(file.jellyfin_url)
@@ -277,6 +284,31 @@ prowlarr_url = "http://prowlarr.internal:9696"
assert_eq!(config.jellyfin_api_key.as_deref(), Some("secret-3"));
}
#[test]
fn tmdb_url_is_an_env_only_seam() {
let config = Config::resolve(EnvOverrides::default()).unwrap();
assert_eq!(config.tmdb_url, None);
let env = EnvOverrides {
tmdb_url: Some("http://127.0.0.1:9/3".into()),
..EnvOverrides::default()
};
let config = Config::resolve(env).unwrap();
assert_eq!(config.tmdb_url.as_deref(), Some("http://127.0.0.1:9/3"));
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("arr.toml");
std::fs::write(&path, "tmdb_url = \"http://127.0.0.1:9/3\"\n").unwrap();
let env = EnvOverrides {
config_file: Some(path.to_string_lossy().into_owned()),
..EnvOverrides::default()
};
assert!(matches!(
Config::resolve(env),
Err(ConfigError::TomlDecode(_))
));
}
#[test]
fn a_secret_in_the_config_file_is_a_parse_error() {
let dir = tempfile::tempdir().unwrap();
+12 -7
View File
@@ -83,15 +83,20 @@ async fn run() -> Result<(), Error> {
// needs its own TMDB client for `movie/lookup`.
let mut compat = CompatState::new(database.clone());
if let Some(key) = &config.tmdb_api_key {
compat = compat.with_tmdb(Arc::new(TmdbClient::new(key)?));
let mut tmdb = TmdbClient::builder(key.clone());
if let Some(tmdb_url) = &config.tmdb_url {
tmdb = tmdb.base_url(tmdb_url.clone());
}
compat = compat.with_tmdb(Arc::new(tmdb.build()?));
}
let state = AppState::new(
Upstreams::new(config.prowlarr_url, config.transmission_url)
.with_prowlarr_api_key(config.prowlarr_api_key)
.with_tmdb_api_key(config.tmdb_api_key),
)?
.with_database(database);
let mut upstreams = Upstreams::new(config.prowlarr_url, config.transmission_url)
.with_prowlarr_api_key(config.prowlarr_api_key)
.with_tmdb_api_key(config.tmdb_api_key);
if let Some(tmdb_url) = config.tmdb_url {
upstreams = upstreams.with_tmdb_url(tmdb_url);
}
let state = AppState::new(upstreams)?.with_database(database);
let app = arr_api::router(state)
.merge(arr_compat::router(compat))