From ce0dbf6b249956700c6a1705bf4ad85a09d53e8c Mon Sep 17 00:00:00 2001 From: Dawid Rycerz Date: Sun, 15 Feb 2026 21:27:00 +0100 Subject: feat: witryna 0.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch, cleanup, and status CLI commands. Persistent build state via state.json. Post-deploy hooks on success and failure with WITRYNA_BUILD_STATUS. Dependency diet (axum→tiny_http, clap→argh, tracing→log). Drop built-in rate limiting. Nix flake with NixOS module. Arch Linux PKGBUILD. Centralized version management. Co-Authored-By: Claude Opus 4.6 --- tests/integration/harness.rs | 77 +++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 37 deletions(-) (limited to 'tests/integration/harness.rs') diff --git a/tests/integration/harness.rs b/tests/integration/harness.rs index c015fa8..b985971 100644 --- a/tests/integration/harness.rs +++ b/tests/integration/harness.rs @@ -1,11 +1,8 @@ -use governor::{Quota, RateLimiter}; use std::collections::HashMap; -use std::num::NonZeroU32; use std::path::PathBuf; -use std::sync::Arc; +use std::sync::{Arc, RwLock}; use tempfile::TempDir; -use tokio::net::TcpListener; -use tokio::sync::{RwLock, oneshot}; +use tiny_http::Server; use witryna::build_guard::BuildScheduler; use witryna::config::{BuildOverrides, Config, SiteConfig}; use witryna::polling::PollingManager; @@ -18,18 +15,14 @@ pub struct TestServer { /// Kept alive for RAII cleanup of the config file written during startup. #[allow(dead_code)] pub tempdir: TempDir, - shutdown_tx: Option>, + server: Arc, + server_thread: Option>, } impl TestServer { /// Start a new test server with the given config. /// Binds to `127.0.0.1:0` (OS-assigned port). - pub async fn start(config: Config) -> Self { - Self::start_with_rate_limit(config, 1000).await - } - - /// Start a new test server with a specific rate limit. - pub async fn start_with_rate_limit(mut config: Config, rate_limit: u32) -> Self { + pub async fn start(mut config: Config) -> Self { let tempdir = TempDir::new().expect("failed to create temp dir"); let config_path = tempdir.path().join("witryna.toml"); @@ -44,38 +37,50 @@ impl TestServer { .await .expect("failed to resolve secrets"); - let quota = Quota::per_minute(NonZeroU32::new(rate_limit).expect("rate limit must be > 0")); - let state = AppState { config: Arc::new(RwLock::new(config)), config_path: Arc::new(config_path), build_scheduler: Arc::new(BuildScheduler::new()), - rate_limiter: Arc::new(RateLimiter::dashmap(quota)), polling_manager: Arc::new(PollingManager::new()), }; - let listener = TcpListener::bind("127.0.0.1:0") - .await - .expect("failed to bind to random port"); - let port = listener.local_addr().unwrap().port(); + let server = Arc::new(Server::http("127.0.0.1:0").expect("failed to bind")); + let port = match server.server_addr() { + tiny_http::ListenAddr::IP(addr) => addr.port(), + _ => unreachable!("expected IP address"), + }; let base_url = format!("http://127.0.0.1:{port}"); - let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>(); - - let server_state = state.clone(); - tokio::spawn(async move { - witryna::test_support::run_server(server_state, listener, async { - let _ = shutdown_rx.await; - }) - .await - .expect("server failed"); - }); + // Shutdown uses server.unblock() directly — no oneshot needed. + // We pass a future that never resolves; shutdown is triggered + // by calling server.unblock() from TestServer::shutdown(). + let server_thread = witryna::test_support::run_server( + state.clone(), + server.clone(), + std::future::pending(), + ); + + // Readiness probe: wait for server to accept connections + let client = reqwest::Client::new(); + for _ in 0..50 { + if client + .get(format!("{base_url}/health")) + .send() + .await + .map(|r| r.status().as_u16() == 200) + .unwrap_or(false) + { + break; + } + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + } Self { base_url, state, tempdir, - shutdown_tx: Some(shutdown_tx), + server, + server_thread: Some(server_thread), } } @@ -91,8 +96,11 @@ impl TestServer { /// Shut down the server gracefully. pub fn shutdown(&mut self) { - if let Some(tx) = self.shutdown_tx.take() { - let _ = tx.send(()); + // Unblock the HTTP request loop directly — no async channel needed + self.server.unblock(); + // Join the HTTP thread to ensure clean teardown + if let Some(handle) = self.server_thread.take() { + let _ = handle.join(); } } } @@ -112,7 +120,6 @@ pub fn test_config(base_dir: PathBuf) -> Config { base_dir, log_dir, log_level: "debug".to_owned(), - rate_limit_per_minute: 10, max_builds_to_keep: 5, git_timeout: None, sites: vec![], @@ -128,7 +135,6 @@ pub fn test_config_with_site(base_dir: PathBuf, site: SiteConfig) -> Config { base_dir, log_dir, log_level: "debug".to_owned(), - rate_limit_per_minute: 10, max_builds_to_keep: 5, git_timeout: None, sites: vec![site], @@ -144,7 +150,6 @@ pub fn test_config_with_sites(base_dir: PathBuf, sites: Vec) -> Conf base_dir, log_dir, log_level: "debug".to_owned(), - rate_limit_per_minute: 10, max_builds_to_keep: 5, git_timeout: None, sites, @@ -286,7 +291,6 @@ fn build_config_toml(config: &Config) -> String { {}base_dir = "{}" log_dir = "{}" log_level = "{}" -rate_limit_per_minute = {} max_builds_to_keep = {} "#, config.listen_address, @@ -294,7 +298,6 @@ max_builds_to_keep = {} config.base_dir.display(), config.log_dir.display(), config.log_level, - config.rate_limit_per_minute, config.max_builds_to_keep, ); -- cgit v1.2.3