From 064a1d01c5c14f5ecc032fa9b8346a4a88b893f6 Mon Sep 17 00:00:00 2001 From: Dawid Rycerz Date: Thu, 22 Jan 2026 22:07:32 +0100 Subject: witryna 0.1.0 — initial release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minimalist Git-based static site deployment orchestrator. Webhook-triggered builds in Podman/Docker containers with atomic symlink publishing, SIGHUP hot-reload, and zero-downtime deploys. See README.md for usage, CHANGELOG.md for details. --- tests/integration/sighup.rs | 149 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 tests/integration/sighup.rs (limited to 'tests/integration/sighup.rs') diff --git a/tests/integration/sighup.rs b/tests/integration/sighup.rs new file mode 100644 index 0000000..23c0dfd --- /dev/null +++ b/tests/integration/sighup.rs @@ -0,0 +1,149 @@ +use crate::harness::{SiteBuilder, TestServer, test_config_with_site}; +use serial_test::serial; +use std::time::Duration; + +/// Send SIGHUP to the current process. +fn send_sighup_to_self() { + use nix::sys::signal::{Signal, kill}; + use nix::unistd::Pid; + + kill(Pid::this(), Signal::SIGHUP).expect("failed to send SIGHUP"); +} + +/// Install the SIGHUP handler and wait for it to be registered. +async fn install_sighup_handler(server: &TestServer) { + witryna::test_support::setup_sighup_handler(&server.state); + // Yield to allow the spawned handler task to register the signal listener + tokio::task::yield_now().await; + tokio::time::sleep(std::time::Duration::from_millis(50)).await; +} + +#[tokio::test] +#[serial] +async fn sighup_reload_keeps_server_healthy() { + let dir = tempfile::tempdir().unwrap().keep(); + let site = SiteBuilder::new("my-site", "https://example.com/repo.git", "test-token").build(); + let server = TestServer::start(test_config_with_site(dir, site)).await; + + install_sighup_handler(&server).await; + + // Verify server is healthy before SIGHUP + let resp = TestServer::client() + .get(server.url("/health")) + .send() + .await + .unwrap(); + assert_eq!(resp.status().as_u16(), 200); + + // Send SIGHUP (reload config) + send_sighup_to_self(); + + // Give the handler time to process + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + + // Server should still be healthy + let resp = TestServer::client() + .get(server.url("/health")) + .send() + .await + .unwrap(); + assert_eq!(resp.status().as_u16(), 200); +} + +#[tokio::test] +#[serial] +async fn rapid_sighup_does_not_crash() { + let dir = tempfile::tempdir().unwrap().keep(); + let site = SiteBuilder::new("my-site", "https://example.com/repo.git", "test-token").build(); + let server = TestServer::start(test_config_with_site(dir, site)).await; + + install_sighup_handler(&server).await; + + // Send multiple SIGHUPs in quick succession + for _ in 0..3 { + send_sighup_to_self(); + tokio::time::sleep(std::time::Duration::from_millis(50)).await; + } + + // Wait for stabilization + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + + // Server should survive + let resp = TestServer::client() + .get(server.url("/health")) + .send() + .await + .unwrap(); + assert_eq!(resp.status().as_u16(), 200); +} + +#[tokio::test] +#[serial] +async fn sighup_preserves_listen_address() { + let dir = tempfile::tempdir().unwrap().keep(); + let site = SiteBuilder::new("my-site", "https://example.com/repo.git", "test-token").build(); + let server = TestServer::start(test_config_with_site(dir, site)).await; + + install_sighup_handler(&server).await; + + // Verify server is healthy before SIGHUP + let resp = TestServer::client() + .get(server.url("/health")) + .send() + .await + .unwrap(); + assert_eq!(resp.status().as_u16(), 200); + + // Rewrite the on-disk config with a different listen_address (unreachable port) + // and an additional site to verify reloadable fields are updated + let config_path = server.state.config_path.as_ref(); + let new_toml = format!( + r#"listen_address = "127.0.0.1:19999" +container_runtime = "podman" +base_dir = "{}" +log_dir = "{}" +log_level = "debug" + +[[sites]] +name = "my-site" +repo_url = "https://example.com/repo.git" +branch = "main" +webhook_token = "test-token" + +[[sites]] +name = "new-site" +repo_url = "https://example.com/new.git" +branch = "main" +webhook_token = "new-token" +"#, + server.state.config.read().await.base_dir.display(), + server.state.config.read().await.log_dir.display(), + ); + tokio::fs::write(config_path, &new_toml).await.unwrap(); + + // Send SIGHUP to reload + send_sighup_to_self(); + tokio::time::sleep(Duration::from_millis(500)).await; + + // Server should still respond on the original port (listen_address preserved) + let resp = TestServer::client() + .get(server.url("/health")) + .send() + .await + .unwrap(); + assert_eq!(resp.status().as_u16(), 200); + + // Verify the reloadable field (sites) was updated + let config = server.state.config.read().await; + assert_eq!(config.sites.len(), 2, "sites should have been reloaded"); + assert!( + config.find_site("new-site").is_some(), + "new-site should exist after reload" + ); + + // Verify non-reloadable field was preserved (not overwritten with "127.0.0.1:19999") + assert_ne!( + config.listen_address, "127.0.0.1:19999", + "listen_address should be preserved from original config" + ); +} -- cgit v1.2.3