2025-06-07 20:46:08 -04:00
|
|
|
use anyhow::Result;
|
|
|
|
|
use assert_cmd::Command;
|
|
|
|
|
use predicates::prelude::*;
|
2025-06-07 21:03:33 -04:00
|
|
|
use std::env;
|
|
|
|
|
use std::process::Command as StdCommand;
|
2025-06-07 20:46:08 -04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_version_command() -> Result<()> {
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
cmd.arg("--version");
|
|
|
|
|
cmd.assert()
|
|
|
|
|
.success()
|
|
|
|
|
.stdout(predicate::str::contains("krust 0.1.0"));
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_version_subcommand() -> Result<()> {
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
cmd.arg("version");
|
|
|
|
|
cmd.assert()
|
|
|
|
|
.success()
|
|
|
|
|
.stdout(predicate::str::contains("krust 0.1.0"));
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_help_command() -> Result<()> {
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
cmd.arg("--help");
|
|
|
|
|
cmd.assert().success().stdout(predicate::str::contains(
|
|
|
|
|
"A container image build tool for Rust applications",
|
|
|
|
|
));
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_build_help() -> Result<()> {
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
cmd.arg("build").arg("--help");
|
|
|
|
|
cmd.assert().success().stdout(predicate::str::contains(
|
|
|
|
|
"Build a container image from a Rust application",
|
|
|
|
|
));
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2025-06-07 21:03:33 -04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_build_requires_repo_or_image() -> Result<()> {
|
|
|
|
|
// Use the example directory
|
|
|
|
|
let example_dir = env::current_dir()?.join("example").join("hello-krust");
|
|
|
|
|
|
|
|
|
|
// Run krust build without KRUST_REPO or --image
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
cmd.arg("build")
|
|
|
|
|
.arg("--no-push")
|
2025-06-07 22:14:21 -04:00
|
|
|
.arg(".") // Explicitly pass current directory
|
2025-06-07 21:03:33 -04:00
|
|
|
.current_dir(&example_dir)
|
|
|
|
|
.env_remove("KRUST_REPO");
|
|
|
|
|
|
2025-06-07 21:17:30 -04:00
|
|
|
cmd.assert().failure().stderr(predicate::str::contains(
|
|
|
|
|
"Either --image or KRUST_REPO must be set",
|
|
|
|
|
));
|
2025-06-07 21:03:33 -04:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_build_with_krust_repo_env() -> Result<()> {
|
|
|
|
|
// Use the example directory
|
|
|
|
|
let example_dir = env::current_dir()?.join("example").join("hello-krust");
|
|
|
|
|
|
|
|
|
|
// Run krust build with KRUST_REPO env var
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
cmd.arg("build")
|
|
|
|
|
.arg("--no-push")
|
2025-06-07 22:28:15 -04:00
|
|
|
.arg("--platform")
|
|
|
|
|
.arg("linux/amd64")
|
2025-06-07 22:14:21 -04:00
|
|
|
.arg(".") // Explicitly pass current directory
|
2025-06-07 21:03:33 -04:00
|
|
|
.env("KRUST_REPO", "test.local")
|
|
|
|
|
.current_dir(&example_dir);
|
|
|
|
|
|
2025-06-07 21:17:30 -04:00
|
|
|
cmd.assert()
|
|
|
|
|
.success()
|
|
|
|
|
.stderr(predicate::str::contains("Building Rust project"))
|
|
|
|
|
.stderr(predicate::str::contains(
|
2025-06-07 22:28:15 -04:00
|
|
|
"Successfully built image for 1 platform(s)",
|
2025-06-07 21:17:30 -04:00
|
|
|
));
|
2025-06-07 21:03:33 -04:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_command_substitution_syntax() -> Result<()> {
|
|
|
|
|
// Get the example project directory
|
|
|
|
|
let example_dir = env::current_dir()?.join("example").join("hello-krust");
|
|
|
|
|
|
|
|
|
|
// Test that output is clean for command substitution
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
let output = cmd
|
|
|
|
|
.arg("build")
|
|
|
|
|
.arg("--no-push")
|
2025-06-07 22:28:15 -04:00
|
|
|
.arg("--platform")
|
|
|
|
|
.arg("linux/amd64")
|
2025-06-07 21:03:33 -04:00
|
|
|
.arg("--image")
|
|
|
|
|
.arg("test.local/hello:latest")
|
2025-06-07 22:14:21 -04:00
|
|
|
.arg(".") // Explicitly pass current directory
|
2025-06-07 21:03:33 -04:00
|
|
|
.current_dir(&example_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
|
|
|
|
|
// Stdout should be empty when --no-push is used
|
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
2025-06-07 21:17:30 -04:00
|
|
|
assert!(
|
|
|
|
|
stdout.trim().is_empty(),
|
|
|
|
|
"Stdout should be empty with --no-push"
|
|
|
|
|
);
|
2025-06-07 21:03:33 -04:00
|
|
|
|
|
|
|
|
// Stderr should contain log messages
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
assert!(stderr.contains("Building Rust project"));
|
|
|
|
|
assert!(stderr.contains("Successfully built image"));
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_verbose_logging() -> Result<()> {
|
|
|
|
|
let example_dir = env::current_dir()?.join("example").join("hello-krust");
|
|
|
|
|
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
cmd.arg("--verbose")
|
|
|
|
|
.arg("build")
|
|
|
|
|
.arg("--no-push")
|
2025-06-07 22:28:15 -04:00
|
|
|
.arg("--platform")
|
|
|
|
|
.arg("linux/amd64")
|
2025-06-07 21:03:33 -04:00
|
|
|
.arg("--image")
|
|
|
|
|
.arg("test.local/hello:latest")
|
2025-06-07 22:14:21 -04:00
|
|
|
.arg(".") // Explicitly pass current directory
|
2025-06-07 21:03:33 -04:00
|
|
|
.current_dir(&example_dir);
|
|
|
|
|
|
|
|
|
|
cmd.assert()
|
|
|
|
|
.success()
|
|
|
|
|
.stderr(predicate::str::contains("DEBUG"));
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_full_build_and_run_workflow() -> Result<()> {
|
2025-06-07 21:17:30 -04:00
|
|
|
// This test requires Docker
|
2025-06-07 21:47:46 -04:00
|
|
|
let docker_check = StdCommand::new("docker").arg("version").output();
|
|
|
|
|
match docker_check {
|
|
|
|
|
Ok(output) if output.status.success() => {
|
|
|
|
|
// Docker is available, proceed with test
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
// Docker not available or not working
|
|
|
|
|
panic!("Docker is required for this test but is not available");
|
|
|
|
|
}
|
2025-06-07 21:03:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the example project directory
|
|
|
|
|
let example_dir = env::current_dir()?.join("example").join("hello-krust");
|
|
|
|
|
|
|
|
|
|
// Build and push to ttl.sh
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
let output = cmd
|
|
|
|
|
.arg("build")
|
2025-06-07 22:28:15 -04:00
|
|
|
.arg("--platform")
|
|
|
|
|
.arg("linux/amd64")
|
2025-06-07 22:14:21 -04:00
|
|
|
.arg(".") // Explicitly pass current directory
|
2025-06-07 21:03:33 -04:00
|
|
|
.env("KRUST_REPO", "ttl.sh/krust-test")
|
|
|
|
|
.current_dir(&example_dir)
|
|
|
|
|
.output()?;
|
|
|
|
|
|
|
|
|
|
assert!(output.status.success(), "Build failed");
|
|
|
|
|
|
|
|
|
|
// Get the image reference from stdout
|
|
|
|
|
let image_ref = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
|
|
|
|
assert!(image_ref.starts_with("ttl.sh/krust-test/hello-krust@sha256:"));
|
|
|
|
|
|
|
|
|
|
// Try to run the image
|
|
|
|
|
let docker_output = StdCommand::new("docker")
|
|
|
|
|
.args(&["run", "--rm", &image_ref])
|
|
|
|
|
.output()?;
|
|
|
|
|
|
|
|
|
|
assert!(docker_output.status.success(), "Docker run failed");
|
|
|
|
|
let docker_stdout = String::from_utf8_lossy(&docker_output.stdout);
|
|
|
|
|
assert!(docker_stdout.contains("Hello from krust example!"));
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2025-06-07 22:28:15 -04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_single_platform_build() -> Result<()> {
|
|
|
|
|
let example_dir = env::current_dir()?.join("example").join("hello-krust");
|
|
|
|
|
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
cmd.arg("build")
|
|
|
|
|
.arg("--no-push")
|
|
|
|
|
.arg("--platform")
|
|
|
|
|
.arg("linux/amd64")
|
|
|
|
|
.arg("--image")
|
|
|
|
|
.arg("test.local/single-platform:latest")
|
|
|
|
|
.arg(".")
|
|
|
|
|
.current_dir(&example_dir);
|
|
|
|
|
|
|
|
|
|
cmd.assert().success().stderr(predicate::str::contains(
|
|
|
|
|
"Building for platform: linux/amd64",
|
|
|
|
|
));
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_multi_platform_build() -> Result<()> {
|
|
|
|
|
// Check if we have the ARM64 target installed
|
|
|
|
|
let has_arm64_target = StdCommand::new("rustup")
|
|
|
|
|
.args(&["target", "list", "--installed"])
|
|
|
|
|
.output()
|
|
|
|
|
.map(|output| {
|
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
stdout.contains("aarch64-unknown-linux-musl")
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
|
|
let example_dir = env::current_dir()?.join("example").join("hello-krust");
|
|
|
|
|
|
|
|
|
|
let mut cmd = Command::cargo_bin("krust")?;
|
|
|
|
|
|
|
|
|
|
if has_arm64_target {
|
|
|
|
|
// Test with both platforms if ARM64 is available
|
|
|
|
|
cmd.arg("build")
|
|
|
|
|
.arg("--no-push")
|
|
|
|
|
.arg("--platform")
|
|
|
|
|
.arg("linux/amd64,linux/arm64")
|
|
|
|
|
.arg("--image")
|
|
|
|
|
.arg("test.local/multi-platform:latest")
|
|
|
|
|
.arg(".")
|
|
|
|
|
.current_dir(&example_dir);
|
|
|
|
|
|
|
|
|
|
cmd.assert()
|
|
|
|
|
.success()
|
|
|
|
|
.stderr(predicate::str::contains(
|
|
|
|
|
"Building for platform: linux/amd64",
|
|
|
|
|
))
|
|
|
|
|
.stderr(predicate::str::contains(
|
|
|
|
|
"Building for platform: linux/arm64",
|
|
|
|
|
));
|
|
|
|
|
} else {
|
|
|
|
|
// Test with multiple x64 platforms (same platform twice to test the multi-platform flow)
|
|
|
|
|
cmd.arg("build")
|
|
|
|
|
.arg("--no-push")
|
|
|
|
|
.arg("--platform")
|
|
|
|
|
.arg("linux/amd64")
|
|
|
|
|
.arg("--platform")
|
|
|
|
|
.arg("linux/amd64")
|
|
|
|
|
.arg("--image")
|
|
|
|
|
.arg("test.local/multi-platform:latest")
|
|
|
|
|
.arg(".")
|
|
|
|
|
.current_dir(&example_dir);
|
|
|
|
|
|
|
|
|
|
cmd.assert().success().stderr(predicate::str::contains(
|
|
|
|
|
"Building for platform: linux/amd64",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|