diff --git a/crates/buzz-relay/src/api/admin/mod.rs b/crates/buzz-relay/src/api/admin/mod.rs index 21f30065f0..19b1c97e9e 100644 --- a/crates/buzz-relay/src/api/admin/mod.rs +++ b/crates/buzz-relay/src/api/admin/mod.rs @@ -332,44 +332,13 @@ mod tests { use tower::ServiceExt; async fn test_state() -> Arc { - let mut config = crate::config::Config::from_env().expect("default config loads"); - config.require_relay_membership = false; - config.redis_url = "redis://127.0.0.1:1".to_string(); - config.admin = Some(crate::config::AdminConfig { - host: "admin.example".to_string(), - web_dir: None, - }); - let pool = sqlx::PgPool::connect_lazy(&config.database_url).expect("lazy pg pool"); - let db = buzz_db::Db::from_pool(pool.clone()); - let redis_pool = deadpool_redis::Config::from_url(&config.redis_url) - .create_pool(Some(deadpool_redis::Runtime::Tokio1)) - .expect("redis pool"); - let pubsub = Arc::new( - buzz_pubsub::PubSubManager::new(&config.redis_url, redis_pool.clone()) - .await - .expect("pubsub manager"), - ); - let audit = buzz_audit::AuditService::new(pool.clone()); - let auth = buzz_auth::AuthService::new(config.auth.clone()); - let search = buzz_search::SearchService::new(pool.clone()); - let workflow_engine = Arc::new(buzz_workflow::WorkflowEngine::new( - db.clone(), - buzz_workflow::WorkflowConfig::default(), - )); - let media_storage = buzz_media::MediaStorage::new(&config.media).expect("media storage"); - let (state, _audit_shutdown) = crate::state::AppState::new( - config, - db, - redis_pool, - audit, - pubsub, - auth, - search, - workflow_engine, - nostr::Keys::generate(), - media_storage, - ); - Arc::new(state) + crate::test_support::test_state_with_config(|config| { + config.admin = Some(crate::config::AdminConfig { + host: "admin.example".to_string(), + web_dir: None, + }); + }) + .await } const HASH: &str = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"; diff --git a/crates/buzz-relay/src/api/media.rs b/crates/buzz-relay/src/api/media.rs index a2f3640bde..c59813ccf3 100644 --- a/crates/buzz-relay/src/api/media.rs +++ b/crates/buzz-relay/src/api/media.rs @@ -937,47 +937,18 @@ mod tests { } async fn test_state() -> Arc { - let mut config = crate::config::Config::from_env().expect("default config loads"); - config.require_relay_membership = false; - config.redis_url = "redis://127.0.0.1:1".to_string(); - config.media_uploads_per_minute = 1; - config.media_max_concurrent_uploads = 2; - config.media_max_concurrent_uploads_per_pubkey = 1; - - let pool = sqlx::PgPool::connect_lazy(&config.database_url).expect("lazy pg pool"); - let db = buzz_db::Db::from_pool(pool.clone()); - db.ensure_configured_community("relay.example") + let state = crate::test_support::test_state_with_config(|config| { + config.media_uploads_per_minute = 1; + config.media_max_concurrent_uploads = 2; + config.media_max_concurrent_uploads_per_pubkey = 1; + }) + .await; + state + .db + .ensure_configured_community("relay.example") .await .expect("seed relay.example community for host-bound media tests"); - let redis_pool = deadpool_redis::Config::from_url(&config.redis_url) - .create_pool(Some(deadpool_redis::Runtime::Tokio1)) - .expect("redis pool"); - let pubsub = Arc::new( - buzz_pubsub::PubSubManager::new(&config.redis_url, redis_pool.clone()) - .await - .expect("pubsub manager"), - ); - let audit = buzz_audit::AuditService::new(pool.clone()); - let auth = buzz_auth::AuthService::new(config.auth.clone()); - let search = buzz_search::SearchService::new(pool.clone()); - let workflow_engine = Arc::new(buzz_workflow::WorkflowEngine::new( - db.clone(), - buzz_workflow::WorkflowConfig::default(), - )); - let media_storage = buzz_media::MediaStorage::new(&config.media).expect("media storage"); - let (state, _audit_shutdown) = AppState::new( - config, - db, - redis_pool, - audit, - pubsub, - auth, - search, - workflow_engine, - nostr::Keys::generate(), - media_storage, - ); - Arc::new(state) + state } async fn media_get_auth_router() -> axum::Router { diff --git a/crates/buzz-relay/src/handlers/event.rs b/crates/buzz-relay/src/handlers/event.rs index cf295bdfa3..8f94d3696c 100644 --- a/crates/buzz-relay/src/handlers/event.rs +++ b/crates/buzz-relay/src/handlers/event.rs @@ -2004,44 +2004,14 @@ mod tests { } pub(super) async fn test_state_with_redis_url(redis_url: &str) -> Arc { - let mut config = test_config(); - config.redis_url = redis_url.to_string(); - let pool = sqlx::PgPool::connect_lazy(&config.database_url).expect("lazy pg pool"); - let db = buzz_db::Db::from_pool(pool.clone()); - let redis_pool = deadpool_redis::Config::from_url(&config.redis_url) - .create_pool(Some(deadpool_redis::Runtime::Tokio1)) - .expect("redis pool"); - let pubsub = Arc::new( - buzz_pubsub::PubSubManager::new(&config.redis_url, redis_pool.clone()) - .await - .expect("pubsub manager"), - ); - let audit = buzz_audit::AuditService::new(pool.clone()); - let auth = buzz_auth::AuthService::new(config.auth.clone()); - let search = buzz_search::SearchService::new(pool.clone()); - let workflow_engine = Arc::new(buzz_workflow::WorkflowEngine::new( - db.clone(), - buzz_workflow::WorkflowConfig::default(), - )); - let media_storage = - buzz_media::MediaStorage::new(&config.media).expect("media storage"); - let (state, _audit_shutdown) = AppState::new( - config, - db, - redis_pool, - audit, - pubsub, - auth, - search, - workflow_engine, - Keys::generate(), - media_storage, - ); - Arc::new(state) + crate::test_support::test_state_with_config(|config| { + config.redis_url = redis_url.to_string(); + }) + .await } pub(super) async fn test_state() -> Arc { - test_state_with_redis_url("redis://127.0.0.1:1").await + crate::test_support::test_state().await } /// Real-PG, real-Redis state that hands back the audit shutdown handle so diff --git a/crates/buzz-relay/src/lib.rs b/crates/buzz-relay/src/lib.rs index 314adad92e..2d368a571c 100644 --- a/crates/buzz-relay/src/lib.rs +++ b/crates/buzz-relay/src/lib.rs @@ -44,6 +44,8 @@ pub mod subscription; pub mod telemetry; /// Row-zero host binding: resolve the request community from the connection host. pub mod tenant; +#[cfg(test)] +mod test_support; /// Relay-side tunnel session directory and routing. pub mod tunnel; /// Webhook secret generation and constant-time comparison. diff --git a/crates/buzz-relay/src/state.rs b/crates/buzz-relay/src/state.rs index 9fc62f5ac4..0bdcafc80c 100644 --- a/crates/buzz-relay/src/state.rs +++ b/crates/buzz-relay/src/state.rs @@ -1411,40 +1411,7 @@ mod tests { } async fn test_state() -> Arc { - let mut config = crate::config::Config::from_env().expect("default config loads"); - config.require_relay_membership = false; - config.redis_url = "redis://127.0.0.1:1".to_string(); - let pool = sqlx::PgPool::connect_lazy(&config.database_url).expect("lazy pg pool"); - let db = buzz_db::Db::from_pool(pool.clone()); - let redis_pool = deadpool_redis::Config::from_url(&config.redis_url) - .create_pool(Some(deadpool_redis::Runtime::Tokio1)) - .expect("redis pool"); - let pubsub = Arc::new( - buzz_pubsub::PubSubManager::new(&config.redis_url, redis_pool.clone()) - .await - .expect("pubsub manager"), - ); - let audit = buzz_audit::AuditService::new(pool.clone()); - let auth = buzz_auth::AuthService::new(config.auth.clone()); - let search = buzz_search::SearchService::new(pool.clone()); - let workflow_engine = Arc::new(buzz_workflow::WorkflowEngine::new( - db.clone(), - buzz_workflow::WorkflowConfig::default(), - )); - let media_storage = buzz_media::MediaStorage::new(&config.media).expect("media storage"); - let (state, _audit_shutdown) = AppState::new( - config, - db, - redis_pool, - audit, - pubsub, - auth, - search, - workflow_engine, - nostr::Keys::generate(), - media_storage, - ); - Arc::new(state) + crate::test_support::test_state().await } #[test] diff --git a/crates/buzz-relay/src/test_support.rs b/crates/buzz-relay/src/test_support.rs new file mode 100644 index 0000000000..ec8cc650a0 --- /dev/null +++ b/crates/buzz-relay/src/test_support.rs @@ -0,0 +1,95 @@ +//! Shared relay test state for backend-conformance tests. + +use std::sync::Arc; + +use crate::config::Config; +use crate::state::AppState; + +const SQLITE_TEST_URL: &str = "sqlite::memory:"; +const TEST_COMMUNITY_HOST: &str = "relay.example"; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum TestBackend { + Postgres, + Sqlite, +} + +impl TestBackend { + fn from_env() -> Self { + match std::env::var("BUZZ_TEST_BACKEND") { + Err(_) => Self::Postgres, + Ok(value) if value.eq_ignore_ascii_case("postgres") => Self::Postgres, + Ok(value) if value.eq_ignore_ascii_case("sqlite") => Self::Sqlite, + Ok(value) => panic!("BUZZ_TEST_BACKEND must be 'postgres' or 'sqlite', got {value:?}"), + } + } +} + +/// Build the standard relay test state for `BUZZ_TEST_BACKEND`. +pub(crate) async fn test_state() -> Arc { + test_state_with_config(|_| {}).await +} + +/// Build relay test state after applying test-specific configuration. +pub(crate) async fn test_state_with_config(configure: impl FnOnce(&mut Config)) -> Arc { + let mut config = Config::from_env().expect("default config loads"); + config.require_relay_membership = false; + config.redis_url = "redis://127.0.0.1:1".to_string(); + configure(&mut config); + + let auxiliary_pg_pool = sqlx::PgPool::connect_lazy(&config.database_url).expect("lazy pg pool"); + let db = match TestBackend::from_env() { + TestBackend::Postgres => buzz_db::Db::from_pool(auxiliary_pg_pool.clone()), + TestBackend::Sqlite => { + let db = buzz_db::Db::new_sqlite(SQLITE_TEST_URL) + .await + .expect("in-memory sqlite database"); + db.migrate().await.expect("sqlite migrations"); + db.ensure_configured_community(TEST_COMMUNITY_HOST) + .await + .expect("configured sqlite test community"); + db + } + }; + + let redis_pool = deadpool_redis::Config::from_url(&config.redis_url) + .create_pool(Some(deadpool_redis::Runtime::Tokio1)) + .expect("redis pool"); + let pubsub = Arc::new( + buzz_pubsub::PubSubManager::new(&config.redis_url, redis_pool.clone()) + .await + .expect("pubsub manager"), + ); + let audit = buzz_audit::AuditService::new(auxiliary_pg_pool.clone()); + let auth = buzz_auth::AuthService::new(config.auth.clone()); + let search = buzz_search::SearchService::new(auxiliary_pg_pool); + let workflow_engine = Arc::new(buzz_workflow::WorkflowEngine::new( + db.clone(), + buzz_workflow::WorkflowConfig::default(), + )); + let media_storage = buzz_media::MediaStorage::new(&config.media).expect("media storage"); + let (state, _audit_shutdown) = AppState::new( + config, + db, + redis_pool, + audit, + pubsub, + auth, + search, + workflow_engine, + nostr::Keys::generate(), + media_storage, + ); + Arc::new(state) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn backend_names_are_explicit() { + assert_eq!(TestBackend::Postgres, TestBackend::Postgres); + assert_ne!(TestBackend::Postgres, TestBackend::Sqlite); + } +} diff --git a/crates/buzz-relay/src/workflow_sink.rs b/crates/buzz-relay/src/workflow_sink.rs index 97c31c2561..218f90ce97 100644 --- a/crates/buzz-relay/src/workflow_sink.rs +++ b/crates/buzz-relay/src/workflow_sink.rs @@ -571,42 +571,8 @@ mod integration_tests { use buzz_db::CreateCommunityWithOwnerResult; use std::sync::Arc; - /// Real-PG state mirroring `handlers::event::tests::test_state_with_redis_url`. async fn test_state() -> Arc { - let mut config = crate::config::Config::from_env().expect("default config loads"); - config.require_relay_membership = false; - config.redis_url = "redis://127.0.0.1:1".to_string(); - let pool = sqlx::PgPool::connect_lazy(&config.database_url).expect("lazy pg pool"); - let db = buzz_db::Db::from_pool(pool.clone()); - let redis_pool = deadpool_redis::Config::from_url(&config.redis_url) - .create_pool(Some(deadpool_redis::Runtime::Tokio1)) - .expect("redis pool"); - let pubsub = Arc::new( - buzz_pubsub::PubSubManager::new(&config.redis_url, redis_pool.clone()) - .await - .expect("pubsub manager"), - ); - let audit = buzz_audit::AuditService::new(pool.clone()); - let auth = buzz_auth::AuthService::new(config.auth.clone()); - let search = buzz_search::SearchService::new(pool.clone()); - let workflow_engine = Arc::new(buzz_workflow::WorkflowEngine::new( - db.clone(), - buzz_workflow::WorkflowConfig::default(), - )); - let media_storage = buzz_media::MediaStorage::new(&config.media).expect("media storage"); - let (state, _audit_shutdown) = AppState::new( - config, - db, - redis_pool, - audit, - pubsub, - auth, - search, - workflow_engine, - nostr::Keys::generate(), - media_storage, - ); - Arc::new(state) + crate::test_support::test_state().await } #[tokio::test]