summaryrefslogtreecommitdiff
path: root/tests/integration/cli_run.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/integration/cli_run.rs')
-rw-r--r--tests/integration/cli_run.rs277
1 files changed, 277 insertions, 0 deletions
diff --git a/tests/integration/cli_run.rs b/tests/integration/cli_run.rs
new file mode 100644
index 0000000..0ea8d20
--- /dev/null
+++ b/tests/integration/cli_run.rs
@@ -0,0 +1,277 @@
+use crate::git_helpers::create_bare_repo;
+use crate::runtime::{detect_container_runtime, skip_without_git, skip_without_runtime};
+use std::process::Stdio;
+use tempfile::TempDir;
+use tokio::process::Command;
+
+/// Build the binary path for the witryna executable.
+fn witryna_bin() -> std::path::PathBuf {
+ // cargo test sets CARGO_BIN_EXE_witryna when the binary exists,
+ // but for integration tests we use the debug build path directly.
+ let mut path = std::path::PathBuf::from(env!("CARGO_BIN_EXE_witryna"));
+ if !path.exists() {
+ // Fallback to target/debug/witryna
+ path = std::path::PathBuf::from("target/debug/witryna");
+ }
+ path
+}
+
+/// Write a minimal witryna.toml config file.
+async fn write_config(
+ dir: &std::path::Path,
+ site_name: &str,
+ repo_url: &str,
+ base_dir: &std::path::Path,
+ log_dir: &std::path::Path,
+ command: &str,
+ public: &str,
+) -> std::path::PathBuf {
+ let config_path = dir.join("witryna.toml");
+ let runtime = detect_container_runtime();
+ let config = format!(
+ r#"listen_address = "127.0.0.1:0"
+container_runtime = "{runtime}"
+base_dir = "{base_dir}"
+log_dir = "{log_dir}"
+log_level = "debug"
+
+[[sites]]
+name = "{site_name}"
+repo_url = "{repo_url}"
+branch = "main"
+webhook_token = "unused"
+image = "alpine:latest"
+command = "{command}"
+public = "{public}"
+"#,
+ base_dir = base_dir.display(),
+ log_dir = log_dir.display(),
+ );
+ tokio::fs::write(&config_path, config).await.unwrap();
+ config_path
+}
+
+// ---------------------------------------------------------------------------
+// Tier 1: no container runtime needed
+// ---------------------------------------------------------------------------
+
+#[tokio::test]
+async fn cli_run_site_not_found_exits_nonzero() {
+ let tempdir = TempDir::new().unwrap();
+ let base_dir = tempdir.path().join("data");
+ let log_dir = tempdir.path().join("logs");
+ tokio::fs::create_dir_all(&base_dir).await.unwrap();
+ tokio::fs::create_dir_all(&log_dir).await.unwrap();
+
+ // Write config with no sites matching "nonexistent"
+ let config_path = tempdir.path().join("witryna.toml");
+ let config = format!(
+ r#"listen_address = "127.0.0.1:0"
+container_runtime = "podman"
+base_dir = "{}"
+log_dir = "{}"
+log_level = "info"
+sites = []
+"#,
+ base_dir.display(),
+ log_dir.display(),
+ );
+ tokio::fs::write(&config_path, config).await.unwrap();
+
+ let output = Command::new(witryna_bin())
+ .args([
+ "--config",
+ config_path.to_str().unwrap(),
+ "run",
+ "nonexistent",
+ ])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ assert!(
+ !output.status.success(),
+ "should exit non-zero for unknown site"
+ );
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ assert!(
+ stderr.contains("not found"),
+ "stderr should mention site not found, got: {stderr}"
+ );
+}
+
+#[tokio::test]
+async fn cli_run_build_failure_exits_nonzero() {
+ skip_without_git!();
+ skip_without_runtime!();
+
+ let tempdir = TempDir::new().unwrap();
+ let base_dir = tempdir.path().join("data");
+ let log_dir = tempdir.path().join("logs");
+ tokio::fs::create_dir_all(&base_dir).await.unwrap();
+ tokio::fs::create_dir_all(&log_dir).await.unwrap();
+
+ let repo_dir = tempdir.path().join("repos");
+ tokio::fs::create_dir_all(&repo_dir).await.unwrap();
+ let repo_url = create_bare_repo(&repo_dir, "main").await;
+
+ let config_path = write_config(
+ tempdir.path(),
+ "fail-site",
+ &repo_url,
+ &base_dir,
+ &log_dir,
+ "exit 42",
+ "dist",
+ )
+ .await;
+
+ let output = Command::new(witryna_bin())
+ .args([
+ "--config",
+ config_path.to_str().unwrap(),
+ "run",
+ "fail-site",
+ ])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ assert!(
+ !output.status.success(),
+ "should exit non-zero on build failure"
+ );
+
+ // Verify a log file was created
+ let logs_dir = log_dir.join("fail-site");
+ if logs_dir.is_dir() {
+ let mut entries = tokio::fs::read_dir(&logs_dir).await.unwrap();
+ let mut found_log = false;
+ while let Some(entry) = entries.next_entry().await.unwrap() {
+ if entry.file_name().to_string_lossy().ends_with(".log") {
+ found_log = true;
+ break;
+ }
+ }
+ assert!(found_log, "should have a .log file after failed build");
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Tier 2: requires git + container runtime
+// ---------------------------------------------------------------------------
+
+#[tokio::test]
+async fn cli_run_builds_site_successfully() {
+ skip_without_git!();
+ skip_without_runtime!();
+
+ let tempdir = TempDir::new().unwrap();
+ let base_dir = tempdir.path().join("data");
+ let log_dir = tempdir.path().join("logs");
+ tokio::fs::create_dir_all(&base_dir).await.unwrap();
+ tokio::fs::create_dir_all(&log_dir).await.unwrap();
+
+ let repo_dir = tempdir.path().join("repos");
+ tokio::fs::create_dir_all(&repo_dir).await.unwrap();
+ let repo_url = create_bare_repo(&repo_dir, "main").await;
+
+ let config_path = write_config(
+ tempdir.path(),
+ "test-site",
+ &repo_url,
+ &base_dir,
+ &log_dir,
+ "mkdir -p out && echo hello > out/index.html",
+ "out",
+ )
+ .await;
+
+ let output = Command::new(witryna_bin())
+ .args([
+ "--config",
+ config_path.to_str().unwrap(),
+ "run",
+ "test-site",
+ ])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ assert!(
+ output.status.success(),
+ "should exit 0 on success, stderr: {stderr}"
+ );
+
+ // Verify symlink exists
+ let current = base_dir.join("builds/test-site/current");
+ assert!(current.is_symlink(), "current symlink should exist");
+
+ // Verify published content
+ let target = tokio::fs::read_link(&current).await.unwrap();
+ let content = tokio::fs::read_to_string(target.join("index.html"))
+ .await
+ .unwrap();
+ assert!(content.contains("hello"), "published content should match");
+
+ // Verify log file exists
+ let logs_dir = log_dir.join("test-site");
+ assert!(logs_dir.is_dir(), "logs directory should exist");
+}
+
+#[tokio::test]
+async fn cli_run_verbose_shows_build_output() {
+ skip_without_git!();
+ skip_without_runtime!();
+
+ let tempdir = TempDir::new().unwrap();
+ let base_dir = tempdir.path().join("data");
+ let log_dir = tempdir.path().join("logs");
+ tokio::fs::create_dir_all(&base_dir).await.unwrap();
+ tokio::fs::create_dir_all(&log_dir).await.unwrap();
+
+ let repo_dir = tempdir.path().join("repos");
+ tokio::fs::create_dir_all(&repo_dir).await.unwrap();
+ let repo_url = create_bare_repo(&repo_dir, "main").await;
+
+ let config_path = write_config(
+ tempdir.path(),
+ "verbose-site",
+ &repo_url,
+ &base_dir,
+ &log_dir,
+ "echo VERBOSE_MARKER && mkdir -p out && echo ok > out/index.html",
+ "out",
+ )
+ .await;
+
+ let output = Command::new(witryna_bin())
+ .args([
+ "--config",
+ config_path.to_str().unwrap(),
+ "run",
+ "verbose-site",
+ "--verbose",
+ ])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .output()
+ .await
+ .unwrap();
+
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ assert!(output.status.success(), "should exit 0, stderr: {stderr}");
+
+ // In verbose mode, build output should appear in stderr
+ assert!(
+ stderr.contains("VERBOSE_MARKER"),
+ "stderr should contain build output in verbose mode, got: {stderr}"
+ );
+}