summaryrefslogtreecommitdiff
path: root/tests/integration/cli_cleanup.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/integration/cli_cleanup.rs')
-rw-r--r--tests/integration/cli_cleanup.rs341
1 files changed, 341 insertions, 0 deletions
diff --git a/tests/integration/cli_cleanup.rs b/tests/integration/cli_cleanup.rs
new file mode 100644
index 0000000..822c7bc
--- /dev/null
+++ b/tests/integration/cli_cleanup.rs
@@ -0,0 +1,341 @@
+use std::fmt::Write as _;
+use std::process::Stdio;
+use tempfile::TempDir;
+use tokio::process::Command;
+
+fn witryna_bin() -> std::path::PathBuf {
+ let mut path = std::path::PathBuf::from(env!("CARGO_BIN_EXE_witryna"));
+ if !path.exists() {
+ path = std::path::PathBuf::from("target/debug/witryna");
+ }
+ path
+}
+
+async fn write_cleanup_config(
+ dir: &std::path::Path,
+ sites: &[&str],
+ max_builds: u32,
+) -> (std::path::PathBuf, std::path::PathBuf, std::path::PathBuf) {
+ let base_dir = dir.join("data");
+ let log_dir = dir.join("logs");
+ tokio::fs::create_dir_all(&base_dir).await.unwrap();
+ tokio::fs::create_dir_all(&log_dir).await.unwrap();
+
+ let mut sites_toml = String::new();
+ for name in sites {
+ write!(
+ sites_toml,
+ r#"
+[[sites]]
+name = "{name}"
+repo_url = "https://example.com/{name}.git"
+branch = "main"
+"#
+ )
+ .unwrap();
+ }
+
+ let config_path = dir.join("witryna.toml");
+ let config = format!(
+ r#"listen_address = "127.0.0.1:0"
+container_runtime = "podman"
+base_dir = "{base_dir}"
+log_dir = "{log_dir}"
+log_level = "info"
+max_builds_to_keep = {max_builds}
+{sites_toml}"#,
+ base_dir = base_dir.display(),
+ log_dir = log_dir.display(),
+ );
+ tokio::fs::write(&config_path, config).await.unwrap();
+ (config_path, base_dir, log_dir)
+}
+
+async fn create_fake_builds(
+ base_dir: &std::path::Path,
+ log_dir: &std::path::Path,
+ site: &str,
+ timestamps: &[&str],
+) {
+ let builds_dir = base_dir.join("builds").join(site);
+ let site_log_dir = log_dir.join(site);
+ tokio::fs::create_dir_all(&builds_dir).await.unwrap();
+ tokio::fs::create_dir_all(&site_log_dir).await.unwrap();
+
+ for ts in timestamps {
+ tokio::fs::create_dir_all(builds_dir.join(ts))
+ .await
+ .unwrap();
+ tokio::fs::write(site_log_dir.join(format!("{ts}.log")), "build log")
+ .await
+ .unwrap();
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Tier 1: no container runtime / git needed
+// ---------------------------------------------------------------------------
+
+#[tokio::test]
+async fn cli_cleanup_unknown_site() {
+ let tempdir = TempDir::new().unwrap();
+ let (config_path, _, _) = write_cleanup_config(tempdir.path(), &["real-site"], 5).await;
+
+ let output = Command::new(witryna_bin())
+ .args([
+ "cleanup",
+ "--config",
+ config_path.to_str().unwrap(),
+ "nonexistent",
+ ])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ assert!(!output.status.success(), "should exit non-zero");
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ assert!(
+ stderr.contains("not found"),
+ "should mention 'not found', got: {stderr}"
+ );
+}
+
+#[tokio::test]
+async fn cli_cleanup_keep_zero_refused() {
+ let tempdir = TempDir::new().unwrap();
+ let (config_path, _, _) = write_cleanup_config(tempdir.path(), &["my-site"], 5).await;
+
+ let output = Command::new(witryna_bin())
+ .args([
+ "cleanup",
+ "--config",
+ config_path.to_str().unwrap(),
+ "--keep",
+ "0",
+ ])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ assert!(!output.status.success(), "should exit non-zero");
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ assert!(
+ stderr.contains("--keep 0 would delete all builds"),
+ "should refuse --keep 0, got: {stderr}"
+ );
+}
+
+#[tokio::test]
+async fn cli_cleanup_disabled_when_max_zero() {
+ let tempdir = TempDir::new().unwrap();
+ let (config_path, _, _) = write_cleanup_config(tempdir.path(), &["my-site"], 0).await;
+
+ let output = Command::new(witryna_bin())
+ .args(["cleanup", "--config", config_path.to_str().unwrap()])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ assert!(
+ output.status.success(),
+ "should exit 0, stderr: {}",
+ String::from_utf8_lossy(&output.stderr)
+ );
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ assert!(
+ stderr.contains("cleanup disabled"),
+ "should say 'cleanup disabled', got: {stderr}"
+ );
+}
+
+#[tokio::test]
+async fn cli_cleanup_removes_old_builds() {
+ let tempdir = TempDir::new().unwrap();
+ let (config_path, base_dir, log_dir) =
+ write_cleanup_config(tempdir.path(), &["site-a", "site-b"], 5).await;
+
+ let timestamps = &[
+ "20260126-100000-000001",
+ "20260126-100000-000002",
+ "20260126-100000-000003",
+ "20260126-100000-000004",
+ ];
+
+ create_fake_builds(&base_dir, &log_dir, "site-a", timestamps).await;
+ create_fake_builds(&base_dir, &log_dir, "site-b", timestamps).await;
+
+ let output = Command::new(witryna_bin())
+ .args([
+ "cleanup",
+ "--config",
+ config_path.to_str().unwrap(),
+ "--keep",
+ "2",
+ ])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ assert!(
+ output.status.success(),
+ "should exit 0, stderr: {}",
+ String::from_utf8_lossy(&output.stderr)
+ );
+
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ assert!(
+ stderr.contains("site-a: removed"),
+ "should report site-a removals, got: {stderr}"
+ );
+ assert!(
+ stderr.contains("site-b: removed"),
+ "should report site-b removals, got: {stderr}"
+ );
+ assert!(
+ stderr.contains("total:"),
+ "should print total summary for multi-site, got: {stderr}"
+ );
+
+ // Verify filesystem: oldest 2 gone, newest 2 remain for each site
+ for site in &["site-a", "site-b"] {
+ let builds = base_dir.join("builds").join(site);
+ assert!(!builds.join("20260126-100000-000001").exists());
+ assert!(!builds.join("20260126-100000-000002").exists());
+ assert!(builds.join("20260126-100000-000003").exists());
+ assert!(builds.join("20260126-100000-000004").exists());
+
+ let logs = log_dir.join(site);
+ assert!(!logs.join("20260126-100000-000001.log").exists());
+ assert!(!logs.join("20260126-100000-000002.log").exists());
+ assert!(logs.join("20260126-100000-000003.log").exists());
+ assert!(logs.join("20260126-100000-000004.log").exists());
+ }
+}
+
+#[tokio::test]
+async fn cli_cleanup_single_site_filter() {
+ let tempdir = TempDir::new().unwrap();
+ let (config_path, base_dir, log_dir) =
+ write_cleanup_config(tempdir.path(), &["site-a", "site-b"], 5).await;
+
+ let timestamps = &[
+ "20260126-100000-000001",
+ "20260126-100000-000002",
+ "20260126-100000-000003",
+ "20260126-100000-000004",
+ ];
+
+ create_fake_builds(&base_dir, &log_dir, "site-a", timestamps).await;
+ create_fake_builds(&base_dir, &log_dir, "site-b", timestamps).await;
+
+ let output = Command::new(witryna_bin())
+ .args([
+ "cleanup",
+ "--config",
+ config_path.to_str().unwrap(),
+ "--keep",
+ "2",
+ "site-a",
+ ])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ assert!(
+ output.status.success(),
+ "should exit 0, stderr: {}",
+ String::from_utf8_lossy(&output.stderr)
+ );
+
+ let stderr = String::from_utf8_lossy(&output.stderr);
+
+ // site-a should be cleaned
+ assert!(
+ stderr.contains("site-a: removed"),
+ "should report site-a removals, got: {stderr}"
+ );
+
+ // site-b should be untouched — not mentioned in output
+ assert!(
+ !stderr.contains("site-b"),
+ "site-b should not appear in output, got: {stderr}"
+ );
+
+ // No total line for single-site cleanup
+ assert!(
+ !stderr.contains("total:"),
+ "should not print total for single site, got: {stderr}"
+ );
+
+ // Verify site-b filesystem is untouched
+ let site_b_builds = base_dir.join("builds").join("site-b");
+ assert!(site_b_builds.join("20260126-100000-000001").exists());
+ assert!(site_b_builds.join("20260126-100000-000004").exists());
+}
+
+#[tokio::test]
+async fn cli_cleanup_keep_overrides_config() {
+ let tempdir = TempDir::new().unwrap();
+ // Config says max_builds_to_keep = 1
+ let (config_path, base_dir, log_dir) =
+ write_cleanup_config(tempdir.path(), &["my-site"], 1).await;
+
+ let timestamps = &[
+ "20260126-100000-000001",
+ "20260126-100000-000002",
+ "20260126-100000-000003",
+ "20260126-100000-000004",
+ ];
+
+ create_fake_builds(&base_dir, &log_dir, "my-site", timestamps).await;
+
+ // --keep 3 should override config's max_builds_to_keep=1
+ let output = Command::new(witryna_bin())
+ .args([
+ "cleanup",
+ "--config",
+ config_path.to_str().unwrap(),
+ "--keep",
+ "3",
+ ])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ assert!(
+ output.status.success(),
+ "should exit 0, stderr: {}",
+ String::from_utf8_lossy(&output.stderr)
+ );
+
+ // With --keep 3 and 4 builds: only 1 should be removed
+ let builds = base_dir.join("builds").join("my-site");
+ assert!(
+ !builds.join("20260126-100000-000001").exists(),
+ "oldest should be removed"
+ );
+ assert!(
+ builds.join("20260126-100000-000002").exists(),
+ "second should remain"
+ );
+ assert!(
+ builds.join("20260126-100000-000003").exists(),
+ "third should remain"
+ );
+ assert!(
+ builds.join("20260126-100000-000004").exists(),
+ "newest should remain"
+ );
+}