//! Shared fixtures for the in-crate router and state tests. Compiled only under `cfg(test)`. //! //! Every test that needs a `Config`, an `AppState` or a router builds them here, so a new config //! or state field is added in one place. use std::path::Path; use std::sync::Arc; use std::time::Duration; use forge_core::time::stamp_after; use forge_core::token::hash_token; use forge_core::{Config, Registration, Store}; use forge_git::GitEnv; use tokio::sync::Notify; use topcoat::cookie::RouterBuilderCookieExt; use topcoat::router::tower::TowerLayer; use topcoat::router::{Compression, Router, RouterBuilderDiscoverExt}; use crate::pages::repo_ctx::RepoMovedLayer; use crate::pages::settings_profile::avatar_body_limit; use crate::state::AppState; use crate::throttle::Throttle; /// Fixed instance key shared by every test state. pub const KEY: [u8; 32] = [7u8; 32]; /// Fixed internal API token shared by every test state. pub const INTERNAL_TOKEN: &str = "test-token"; /// Fixed admin token shared by every test state. pub const ADMIN_TOKEN: &str = "test-admin-token"; /// The canonical test configuration. Data lives under `dir`, the database at `dir/forge.db`. pub fn test_config(dir: &Path) -> Config { Config { database: dir.join("forge.db"), data_dir: dir.to_path_buf(), listen: "127.0.0.1:7660".parse().unwrap(), base_url: "https://forge.test/".into(), user_content_base_url: "https://forge.test".into(), ssh_listen: "127.0.0.1:2222".parse().unwrap(), user_content_listen: "127.0.0.1:7661".parse().unwrap(), ssh_public_host: "forge.test".into(), ssh_public_port: 22, registration: Registration::Closed, trusted_proxies: Vec::new(), webhooks: Default::default(), backup: Default::default(), releases: Default::default(), replication: Default::default(), lfs: Default::default(), ci: Default::default(), advisories: Default::default(), mail: Default::default(), orgs: Default::default(), self_update: Default::default(), } } /// Opens (and migrates) the store at `dir/forge.db` and builds the shared state around it. pub async fn test_state(dir: &Path) -> AppState { let store = Store::open(&dir.join("forge.db")).await.unwrap(); store.set_secret_key(KEY); AppState { config: Arc::new(test_config(dir)), store, git: Arc::new(GitEnv { git_bin: "git".into(), data_dir: dir.to_path_buf(), }), key: Arc::new(KEY), internal_token: Arc::from(INTERNAL_TOKEN), admin_token: Arc::from(ADMIN_TOKEN), jobs: Arc::new(crate::backup::Jobs::default()), internal_socket: dir.join("internal.sock"), throttle: Arc::new(Throttle::default()), webhook_waker: Arc::new(Notify::new()), mirror_waker: Arc::new(Notify::new()), blobs: crate::blobs::BlobStore::open(&dir.join("blobs")).expect("blob store"), replication: Arc::new(crate::replication::ReplicationQueue::default()), ci_waker: Arc::new(Notify::new()), mailer: Arc::new(crate::mail::FakeMailer::default()), mail_waker: Arc::new(Notify::new()), notify_hub: Arc::new(crate::notify_hub::NotifyHub::new()), ci_live: Arc::new(crate::ci::CiLive::default()), } } /// The public router as boot.rs builds it, minus the listeners. pub fn app_router(state: AppState) -> Router { Router::builder() .discover() .app_context(state) .cookies() .layer(avatar_body_limit()) .layer(RepoMovedLayer) .layer(TowerLayer::new(crate::compression::ResponseCompression)) .compression(Compression::off()) .build() } /// Creates a session for `user_id` that expires in an hour and has no user agent. pub async fn create_session(store: &Store, user_id: &str, token: &str, csrf: &str) { store .create_session( user_id, &hash_token(token), csrf, &stamp_after(Duration::from_secs(3600)), None, ) .await .expect("session"); }