Spaces:
Paused
Paused
Download api/tests/common/mod.rs from stremio-db/a: direct link, hf CLI and curl.
- Browser
- Download file 10.1 kB
-
https://huggingface.co/spaces/stremio-db/a/resolve/main/api/tests/common/mod.rs
- Command line
-
hf download hf://spaces/stremio-db/a/api/tests/common/mod.rs
-
curl -L -o mod.rs https://huggingface.co/spaces/stremio-db/a/resolve/main/api/tests/common/mod.rs
10.1 kB
| use std::sync::Arc; | |
| use std::time::Duration; | |
| use ab_glyph::FontArc; | |
| use dashmap::DashMap; | |
| use sea_orm::{ConnectionTrait, DatabaseConnection, SqlxSqliteConnector}; | |
| use zeroize::Zeroizing; | |
| use openposterdb_api::config::Config; | |
| use openposterdb_api::services::fanart::FanartClient; | |
| use openposterdb_api::services::tmdb::TmdbClient; | |
| use openposterdb_api::{build_app, AppState, FONT_BYTES, MIGRATIONS, SCHEMA_SQL}; | |
| pub struct TestAppOptions { | |
| pub cors_origin: Option<String>, | |
| pub secure_cookies: bool, | |
| pub enable_cdn_redirects: bool, | |
| pub external_cache_only: bool, | |
| pub cache_dir_override: Option<String>, | |
| pub free_key_enabled: Option<bool>, | |
| pub disable_fanart: bool, | |
| } | |
| impl Default for TestAppOptions { | |
| fn default() -> Self { | |
| Self { | |
| cors_origin: None, | |
| secure_cookies: false, | |
| enable_cdn_redirects: false, | |
| external_cache_only: false, | |
| cache_dir_override: None, | |
| free_key_enabled: None, | |
| disable_fanart: false, | |
| } | |
| } | |
| } | |
| pub async fn setup_test_app_with_options(opts: TestAppOptions) -> (axum::Router, Arc<AppState>) { | |
| let cors_origin = opts.cors_origin; | |
| let secure_cookies = opts.secure_cookies; | |
| let enable_cdn_redirects = opts.enable_cdn_redirects; | |
| let external_cache_only = opts.external_cache_only; | |
| let cache_dir_override = opts.cache_dir_override; | |
| let free_key_enabled = opts.free_key_enabled; | |
| let disable_fanart = opts.disable_fanart; | |
| _setup_test_app(cors_origin, secure_cookies, enable_cdn_redirects, external_cache_only, cache_dir_override, free_key_enabled, disable_fanart).await | |
| } | |
| pub async fn setup_test_app_with_cors(cors_origin: Option<String>) -> (axum::Router, Arc<AppState>) { | |
| _setup_test_app(cors_origin, false, false, false, None, None, false).await | |
| } | |
| async fn _setup_test_app(cors_origin: Option<String>, secure_cookies: bool, enable_cdn_redirects: bool, external_cache_only: bool, cache_dir_override: Option<String>, free_key_enabled: Option<bool>, disable_fanart: bool) -> (axum::Router, Arc<AppState>) { | |
| let sqlite_opts = sqlx::sqlite::SqliteConnectOptions::new() | |
| .filename(":memory:") | |
| .create_if_missing(true) | |
| .pragma("foreign_keys", "ON"); | |
| let pool = sqlx::sqlite::SqlitePoolOptions::new() | |
| .max_connections(1) | |
| .connect_with(sqlite_opts) | |
| .await | |
| .expect("failed to connect to test database"); | |
| let db: DatabaseConnection = SqlxSqliteConnector::from_sqlx_sqlite_pool(pool); | |
| for sql in SCHEMA_SQL { | |
| db.execute_unprepared(sql) | |
| .await | |
| .expect("failed to create table"); | |
| } | |
| for (sql, expected_err) in MIGRATIONS { | |
| match db.execute_unprepared(sql).await { | |
| Ok(_) => {} | |
| Err(e) if e.to_string().to_lowercase().contains(expected_err) => {} | |
| Err(e) => panic!("migration failed: {e}\n SQL: {sql}"), | |
| } | |
| } | |
| let jwt_secret = Zeroizing::new(vec![0xAB; 32]); | |
| let http = reqwest::Client::new(); | |
| let font = FontArc::try_from_slice(FONT_BYTES).expect("failed to load font"); | |
| let api_key_cache = moka::future::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(300)) | |
| .build(); | |
| let image_inflight = moka::future::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(30)) | |
| .build(); | |
| let id_cache = moka::future::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(3600)) | |
| .build(); | |
| let ratings_cache = moka::future::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(1800)) | |
| .build(); | |
| let image_mem_cache = moka::future::Cache::builder() | |
| .max_capacity(1024 * 1024) | |
| .time_to_live(Duration::from_secs(3600)) | |
| .build(); | |
| let refresh_locks = moka::sync::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(300)) | |
| .build(); | |
| let state = Arc::new(AppState { | |
| config: Config { | |
| tmdb_api_key: "test".into(), | |
| omdb_api_key: None, | |
| cache_dir: cache_dir_override.unwrap_or_else(|| "/tmp/openposterdb-test".into()), | |
| db_dir: "/tmp/openposterdb-test".into(), | |
| listen_addr: "127.0.0.1:0".into(), | |
| ratings_min_stale_secs: 86400, | |
| ratings_max_age_secs: 31_536_000, | |
| image_stale_secs: 0, | |
| image_quality: 85, | |
| mdblist_api_key: None, | |
| image_mem_cache_mb: 1, | |
| static_dir: None, | |
| cors_origin, | |
| fanart_api_key: if disable_fanart { None } else { Some("test".into()) }, | |
| trakt_client_id: None, | |
| enable_cdn_redirects, | |
| external_cache_only, | |
| free_key_enabled, | |
| disable_public_pages: false, | |
| }, | |
| tmdb: TmdbClient::new("test".into(), http.clone()), | |
| omdb: None, | |
| mdblist: None, | |
| trakt: None, | |
| font, | |
| refresh_locks, | |
| db, | |
| jwt_secret, | |
| secure_cookies, | |
| api_key_cache, | |
| image_inflight, | |
| id_cache, | |
| ratings_cache, | |
| image_mem_cache, | |
| pending_last_used: Arc::new(DashMap::new()), | |
| fanart: if disable_fanart { None } else { Some(FanartClient::new("test".into(), http)) }, | |
| fanart_cache: moka::future::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(3600)) | |
| .build(), | |
| fanart_negative: moka::future::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(3600)) | |
| .build(), | |
| tmdb_images_cache: moka::future::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(1800)) | |
| .build(), | |
| settings_cache: moka::future::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(300)) | |
| .build(), | |
| global_settings_cache: moka::future::Cache::builder() | |
| .max_capacity(1) | |
| .time_to_live(Duration::from_secs(300)) | |
| .build(), | |
| preview_cache: moka::future::Cache::builder() | |
| .max_capacity(500) | |
| .time_to_live(Duration::from_secs(300)) | |
| .build(), | |
| free_api_key_cache: moka::future::Cache::builder() | |
| .max_capacity(1) | |
| .time_to_live(Duration::from_secs(60)) | |
| .build(), | |
| render_semaphore: Arc::new(tokio::sync::Semaphore::new(4)), | |
| cross_id_semaphore: Arc::new(tokio::sync::Semaphore::new(8)), | |
| settings_hash_registry: moka::future::Cache::builder() | |
| .max_capacity(100) | |
| .time_to_live(Duration::from_secs(300)) | |
| .build(), | |
| available_ratings_cache: moka::future::Cache::builder() | |
| .max_capacity(1000) | |
| .time_to_live(Duration::from_secs(300)) | |
| .build(), | |
| }); | |
| let app = build_app(state.clone()); | |
| (app, state) | |
| } | |
| pub async fn setup_test_app() -> (axum::Router, Arc<AppState>) { | |
| setup_test_app_with_cors(None).await | |
| } | |
| /// Helper to perform setup and get back an access token. | |
| pub async fn setup_admin(app: &axum::Router) -> String { | |
| use axum::body::Body; | |
| use axum::http::{Request, StatusCode}; | |
| use http_body_util::BodyExt; | |
| use tower::ServiceExt; | |
| let req = Request::builder() | |
| .method("POST") | |
| .uri("/api/auth/setup") | |
| .header("content-type", "application/json") | |
| .body(Body::from( | |
| serde_json::json!({"username": "admin", "password": "testpassword123"}).to_string(), | |
| )) | |
| .unwrap(); | |
| let res = app.clone().oneshot(req).await.unwrap(); | |
| assert_eq!(res.status(), StatusCode::OK); | |
| let body = res.into_body().collect().await.unwrap().to_bytes(); | |
| let json: serde_json::Value = serde_json::from_slice(&body).unwrap(); | |
| json["token"].as_str().unwrap().to_string() | |
| } | |
| /// Helper: set up admin, create an API key, log in with it, and return the session JWT. | |
| pub async fn setup_api_key_session(app: &axum::Router) -> String { | |
| use axum::body::Body; | |
| use axum::http::{Request, StatusCode}; | |
| use http_body_util::BodyExt; | |
| use tower::ServiceExt; | |
| let admin_token = setup_admin(app).await; | |
| // Create an API key | |
| let req = Request::builder() | |
| .method("POST") | |
| .uri("/api/keys") | |
| .header("content-type", "application/json") | |
| .header("authorization", format!("Bearer {admin_token}")) | |
| .body(Body::from( | |
| serde_json::json!({"name": "preview-test-key"}).to_string(), | |
| )) | |
| .unwrap(); | |
| let res = app.clone().oneshot(req).await.unwrap(); | |
| assert_eq!(res.status(), StatusCode::OK); | |
| let body = res.into_body().collect().await.unwrap().to_bytes(); | |
| let json: serde_json::Value = serde_json::from_slice(&body).unwrap(); | |
| let raw_key = json["key"].as_str().unwrap().to_string(); | |
| // Log in with the API key | |
| let req = Request::builder() | |
| .method("POST") | |
| .uri("/api/auth/key-login") | |
| .header("content-type", "application/json") | |
| .body(Body::from( | |
| serde_json::json!({"api_key": raw_key}).to_string(), | |
| )) | |
| .unwrap(); | |
| let res = app.clone().oneshot(req).await.unwrap(); | |
| assert_eq!(res.status(), StatusCode::OK); | |
| let body = res.into_body().collect().await.unwrap().to_bytes(); | |
| let json: serde_json::Value = serde_json::from_slice(&body).unwrap(); | |
| json["token"].as_str().unwrap().to_string() | |
| } | |
| /// Extract Set-Cookie header value from a response. | |
| pub fn extract_set_cookie(headers: &axum::http::HeaderMap) -> Option<String> { | |
| headers | |
| .get(axum::http::header::SET_COOKIE) | |
| .and_then(|v| v.to_str().ok()) | |
| .map(|s| s.to_string()) | |
| } | |
| /// Extract the refresh_token value from a Set-Cookie header. | |
| pub fn extract_refresh_token(set_cookie: &str) -> Option<String> { | |
| set_cookie | |
| .split(';') | |
| .next() | |
| .and_then(|s| s.strip_prefix("refresh_token=")) | |
| .map(|s| s.to_string()) | |
| } | |