use std::{fmt::Debug, sync::Arc}; use once_cell::sync::OnceCell; use typed_builder::TypedBuilder; use crate::{ app::AppContextTrait, errors::RecorderResult, test_utils::{ crypto::build_testing_crypto_service, database::{TestingDatabaseServiceConfig, build_testing_database_service}, media::build_testing_media_service, mikan::{MikanMockServer, build_testing_mikan_client}, storage::build_testing_storage_service, task::build_testing_task_service, }, }; #[derive(TypedBuilder)] #[builder(field_defaults(default, setter(strip_option)))] pub struct TestingAppContext { logger: Option, db: Option, config: Option, cache: Option, mikan: Option, auth: Option, graphql: Option, storage: Option, crypto: Option, media: Option, #[builder(default = Arc::new(OnceCell::new()), setter(!strip_option))] task: Arc>, message: Option, #[builder(default = Some(String::from(env!("CARGO_MANIFEST_DIR"))))] working_dir: Option, #[builder(default = crate::app::Environment::Testing, setter(!strip_option))] environment: crate::app::Environment, } impl TestingAppContext { pub fn set_task(&self, task: crate::task::TaskService) { self.task.get_or_init(|| task); } pub async fn from_preset(preset: TestingAppContextPreset) -> RecorderResult> { let mikan_client = build_testing_mikan_client(preset.mikan_base_url).await?; let db_service = build_testing_database_service(preset.database_config.unwrap_or_default()).await?; let crypto_service = build_testing_crypto_service().await?; let storage_service = build_testing_storage_service().await?; let media_service = build_testing_media_service().await?; let app_ctx = Arc::new( TestingAppContext::builder() .mikan(mikan_client) .db(db_service) .crypto(crypto_service) .storage(storage_service) .media(media_service) .build(), ); let task_service = build_testing_task_service(app_ctx.clone()).await?; app_ctx.set_task(task_service); Ok(app_ctx) } } impl Debug for TestingAppContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "UnitTestAppContext") } } impl AppContextTrait for TestingAppContext { fn logger(&self) -> &crate::logger::LoggerService { self.logger.as_ref().expect("should set logger") } fn db(&self) -> &crate::database::DatabaseService { self.db.as_ref().expect("should set db") } fn config(&self) -> &crate::app::AppConfig { self.config.as_ref().expect("should set config") } fn cache(&self) -> &crate::cache::CacheService { self.cache.as_ref().expect("should set cache") } fn mikan(&self) -> &crate::extract::mikan::MikanClient { self.mikan.as_ref().expect("should set mikan") } fn auth(&self) -> &crate::auth::AuthService { self.auth.as_ref().expect("should set auth") } fn graphql(&self) -> &crate::graphql::GraphQLService { self.graphql.as_ref().expect("should set graphql") } fn storage(&self) -> &crate::storage::StorageService { self.storage.as_ref().expect("should set storage") } fn environment(&self) -> &crate::app::Environment { &self.environment } fn working_dir(&self) -> &String { self.working_dir.as_ref().expect("should set working_dir") } fn crypto(&self) -> &crate::crypto::CryptoService { self.crypto.as_ref().expect("should set crypto") } fn task(&self) -> &crate::task::TaskService { self.task.get().expect("should set task") } fn message(&self) -> &crate::message::MessageService { self.message.as_ref().expect("should set message") } fn media(&self) -> &crate::media::MediaService { self.media.as_ref().expect("should set media") } } pub struct TestingAppContextPreset { pub mikan_base_url: String, pub database_config: Option, } #[derive(TypedBuilder)] pub struct TestingPreset { pub mikan_server: MikanMockServer, pub app_ctx: Arc, } impl TestingPreset { pub async fn default() -> RecorderResult { let mikan_server = MikanMockServer::new().await?; let database_config = TestingDatabaseServiceConfig::default(); let app_ctx = TestingAppContext::from_preset(TestingAppContextPreset { mikan_base_url: mikan_server.base_url().to_string(), database_config: Some(database_config), }) .await?; let preset = Self::builder() .mikan_server(mikan_server) .app_ctx(app_ctx) .build(); Ok(preset) } }