diff --git a/src/http/handler.rs b/src/http/handler.rs index 72941b0..27388df 100644 --- a/src/http/handler.rs +++ b/src/http/handler.rs @@ -34,20 +34,15 @@ mod tests { use super::update::update_subscription_inner; use crate::domain::{BranchName, EventType, RepoUrl, TargetRepo}; use crate::http::error::HandlerError; - use crate::http::state::AppState; use crate::model::{CreateSubscription, UpdateSubscription}; - use crate::test_utils::create_test_db; + use crate::test_utils::{create_test_db, create_test_state}; use axum::Json; use axum::extract::{Path, Query, State}; #[tokio::test] async fn test_crud_subscription() { let pool = create_test_db().await; - let config = crate::test_utils::create_test_config(); - let state = AppState { - config: std::sync::Arc::new(config), - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new(pool.clone())), - }; + let state = create_test_state(pool.clone()); let payload = CreateSubscription { source_repo_url: RepoUrl::new("https://github.com/org/repo".to_string()).unwrap(), source_branch_name: BranchName::new("main".to_string()).unwrap(), @@ -120,11 +115,7 @@ mod tests { #[tokio::test] async fn test_non_existent_subscription_returns_not_found() { let pool = create_test_db().await; - let config = crate::test_utils::create_test_config(); - let state = AppState { - config: std::sync::Arc::new(config), - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new(pool.clone())), - }; + let state = create_test_state(pool.clone()); // Try getting a non-existent subscription let get_res = get_subscription_inner(State(state.clone()), Path(999)).await; @@ -148,11 +139,7 @@ mod tests { #[tokio::test] async fn test_list_subscriptions_pagination() { let pool = create_test_db().await; - let config = crate::test_utils::create_test_config(); - let state = AppState { - config: std::sync::Arc::new(config), - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new(pool.clone())), - }; + let state = create_test_state(pool.clone()); // Create 3 subscriptions // @@ -205,11 +192,7 @@ mod tests { #[tokio::test] async fn test_cascading_branch_cleanup() { let pool = create_test_db().await; - let config = crate::test_utils::create_test_config(); - let state = AppState { - config: std::sync::Arc::new(config), - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new(pool.clone())), - }; + let state = create_test_state(pool.clone()); let payload = CreateSubscription { source_repo_url: RepoUrl::new("https://github.com/org/repo".to_string()).unwrap(), source_branch_name: BranchName::new("main".to_string()).unwrap(), diff --git a/src/polling/mod.rs b/src/polling/mod.rs index 8ff6285..60960d4 100644 --- a/src/polling/mod.rs +++ b/src/polling/mod.rs @@ -222,12 +222,9 @@ async fn followup_poll(res: Result<(), PollingError>, ctx: &SharedContext) { clippy::indexing_slicing )] mod tests { - use crate::context::SharedContext; use crate::domain::CommitHash; use crate::polling::poll_branches; - use crate::test_utils::MockGitFetcher; - use std::sync::Arc; - use tokio_util::sync::CancellationToken; + use crate::test_utils::create_test_context; #[tokio::test] async fn test_poll_branches_updates_db_and_queues_trigger() { @@ -255,16 +252,7 @@ mod tests { .await .unwrap(); - let mock_fetcher = Arc::new(MockGitFetcher { - hash: CommitHash::new("b".repeat(40)).unwrap(), - }); - - let ctx = SharedContext { - config: crate::test_utils::create_test_config(), - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new(pool.clone())), - git_fetcher: mock_fetcher, - token: CancellationToken::new(), - }; + let ctx = create_test_context(pool.clone(), CommitHash::new("b".repeat(40)).unwrap()); poll_branches(&ctx).await.unwrap(); @@ -311,29 +299,14 @@ mod tests { .await .unwrap(); - let ctx = SharedContext { - config: crate::test_utils::create_test_config(), - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new(pool.clone())), - git_fetcher: Arc::new(crate::test_utils::MockGitFetcher { - hash: CommitHash::new("b".repeat(40)).unwrap(), - }), - token: CancellationToken::new(), - }; + let ctx = create_test_context(pool.clone(), CommitHash::new("b".repeat(40)).unwrap()); // First update poll_branches(&ctx).await.unwrap(); // Second update (coalescing) // Manually update the mock fetcher to a new hash - let mock_fetcher = Arc::new(crate::test_utils::MockGitFetcher { - hash: CommitHash::new("c".repeat(40)).unwrap(), - }); - let ctx = SharedContext { - config: ctx.config, - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new(pool.clone())), - git_fetcher: mock_fetcher, - token: ctx.token, - }; + let ctx = create_test_context(pool.clone(), CommitHash::new("c".repeat(40)).unwrap()); poll_branches(&ctx).await.unwrap(); // Verify only one entry in queue @@ -397,14 +370,7 @@ mod tests { .await .unwrap(); - let ctx = SharedContext { - config: crate::test_utils::create_test_config(), - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new(pool.clone())), - git_fetcher: std::sync::Arc::new(crate::test_utils::MockGitFetcher { - hash: CommitHash::new("c".repeat(40)).unwrap(), - }), - token: tokio_util::sync::CancellationToken::new(), - }; + let ctx = create_test_context(pool.clone(), CommitHash::new("c".repeat(40)).unwrap()); // Poll for both branches. The first branch updates to 'c' // The second branch updates to 'c' and coalesces with the first one diff --git a/src/test_utils.rs b/src/test_utils.rs index 34675fb..aedb38f 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -6,15 +6,22 @@ clippy::indexing_slicing )] +use std::path::PathBuf; +use std::sync::Arc; + +use async_trait::async_trait; +use sqlx::{SqlitePool, sqlite::SqlitePoolOptions}; +use tokio_util::sync::CancellationToken; +use url::Url; + use crate::{ + context::SharedContext, domain::{AcceptHeader, ApiVersion, CommitHash, NonEmptyString}, + http::state::AppState, polling::git::GitFetcher, - trigger::{Authenticator, error::AuthError}, + repository::SqliteRepository, + trigger::{Authenticator, TriggerEngine, error::AuthError}, }; -use async_trait::async_trait; -use sqlx::{SqlitePool, sqlite::SqlitePoolOptions}; -use std::path::PathBuf; -use url::Url; pub struct MockGitFetcher { pub hash: CommitHash, @@ -103,3 +110,35 @@ pub fn create_test_config() -> crate::config::Config { }, } } + +/// Creates an [`AppState`] with the default test configuration, +/// backed by `pool`. +pub fn create_test_state(pool: SqlitePool) -> AppState { + AppState { + config: Arc::new(create_test_config()), + repository: Arc::new(SqliteRepository::new(pool)), + } +} + +/// Creates a [`SharedContext`] backed by `pool`, +/// whose git fetcher always reports `hash`. +pub fn create_test_context(pool: SqlitePool, hash: CommitHash) -> SharedContext { + SharedContext { + config: create_test_config(), + repository: Arc::new(SqliteRepository::new(pool)), + token: CancellationToken::new(), + git_fetcher: Arc::new(MockGitFetcher { hash }), + } +} + +/// Creates a [`TriggerEngine`] with the default test mocks, +/// backed by `pool`. +pub fn create_test_engine(pool: SqlitePool) -> TriggerEngine { + TriggerEngine { + ctx: create_test_context(pool, CommitHash::new("a".repeat(40)).expect("valid hash")), + http_client: reqwest::Client::new(), + authenticator: Box::new(MockAuthenticator { + iat: "token".to_string(), + }), + } +} diff --git a/src/trigger/process.rs b/src/trigger/process.rs index 9c0ca27..8152c56 100644 --- a/src/trigger/process.rs +++ b/src/trigger/process.rs @@ -168,16 +168,11 @@ mod tests { use super::recover_stuck_tasks; use super::schedule_retry; - use crate::context::SharedContext; use crate::domain::{CommitHash, EventType, TargetRepo}; use crate::model::TriggerQueueItem; use crate::repository::trigger::TriggerRepository; - use crate::test_utils::{MockAuthenticator, MockGitFetcher}; use crate::trigger::error::{RequestError, WorkflowTriggerError}; - use crate::trigger::{TriggerEngine, process_queue}; - use std::sync::Arc; - - use tokio_util::sync::CancellationToken; + use crate::trigger::process_queue; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; @@ -335,22 +330,7 @@ mod tests { span_context: None, }; - let engine = TriggerEngine { - ctx: SharedContext { - config: crate::test_utils::create_test_config(), - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new( - pool.clone(), - )), - token: CancellationToken::new(), - git_fetcher: Arc::new(MockGitFetcher { - hash: CommitHash::new("a".repeat(40)).unwrap(), - }), - }, - http_client: reqwest::Client::new(), - authenticator: Box::new(MockAuthenticator { - iat: "token".to_string(), - }), - }; + let engine = crate::test_utils::create_test_engine(pool.clone()); schedule_retry( &engine, @@ -421,22 +401,7 @@ mod tests { .await .unwrap(); - let engine = TriggerEngine { - ctx: SharedContext { - config: crate::test_utils::create_test_config(), - repository: std::sync::Arc::new(crate::repository::SqliteRepository::new( - pool.clone(), - )), - token: CancellationToken::new(), - git_fetcher: Arc::new(MockGitFetcher { - hash: CommitHash::new("a".repeat(40)).unwrap(), - }), - }, - http_client: reqwest::Client::new(), - authenticator: Box::new(MockAuthenticator { - iat: "token".to_string(), - }), - }; + let engine = crate::test_utils::create_test_engine(pool.clone()); process_queue(&engine).await.unwrap();