#![allow(deprecated)]
#![allow(clippy::unwrap_used)] #![allow(clippy::expect_used)]
use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
use tempfile::TempDir;
#[allow(deprecated)]
fn bashrs_cmd() -> Command {
assert_cmd::cargo_bin_cmd!("bashrs")
}
const DOCKERFILE_NO_USER: &str = r#"FROM debian:12-slim
WORKDIR /app
COPY app.py /app/
CMD ["python3", "app.py"]
"#;
const DOCKERFILE_UNPINNED: &str = r#"FROM ubuntu
RUN apt-get update && apt-get install -y curl
CMD ["bash"]
"#;
const DOCKERFILE_NO_CLEANUP: &str = r#"FROM debian:12-slim
RUN apt-get update && apt-get install -y curl
CMD ["bash"]
"#;
const DOCKERFILE_NO_FLAG: &str = r#"FROM debian:12-slim
RUN apt-get install -y python3
CMD ["python3"]
"#;
const DOCKERFILE_ADD_LOCAL: &str = r#"FROM debian:12-slim
ADD app.py /app/
CMD ["python3", "/app/app.py"]
"#;
#[test]
fn test_dockerfile_001_purify_command_exists() {
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
fs::write(&input_file, DOCKERFILE_NO_USER).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.assert()
.success(); }
#[test]
fn test_dockerfile_002_purify_to_stdout() {
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
fs::write(&input_file, DOCKERFILE_NO_USER).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.assert()
.success()
.stdout(predicate::str::contains("USER")); }
#[test]
fn test_dockerfile_003_purify_to_file() {
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
let output_file = temp_dir.path().join("Dockerfile.purified");
fs::write(&input_file, DOCKERFILE_NO_USER).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.arg("-o")
.arg(&output_file)
.assert()
.success();
assert!(output_file.exists());
}
#[test]
fn test_dockerfile_docker001_adds_missing_user_directive() {
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
let output_file = temp_dir.path().join("Dockerfile.purified");
fs::write(&input_file, DOCKERFILE_NO_USER).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.arg("-o")
.arg(&output_file)
.assert()
.success();
let output_content = fs::read_to_string(&output_file).unwrap();
assert!(
output_content.contains("USER appuser"),
"Expected USER directive to be added"
);
assert!(
output_content.contains("RUN groupadd -r appuser"),
"Expected user creation RUN command"
);
let user_pos = output_content.find("USER").unwrap();
let cmd_pos = output_content.find("CMD").unwrap();
assert!(user_pos < cmd_pos, "USER directive should come before CMD");
}
#[test]
fn test_dockerfile_docker001_preserves_existing_user() {
let dockerfile_with_user = r#"FROM debian:12-slim
WORKDIR /app
USER www-data
CMD ["bash"]
"#;
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
let output_file = temp_dir.path().join("Dockerfile.purified");
fs::write(&input_file, dockerfile_with_user).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.arg("-o")
.arg(&output_file)
.assert()
.success();
let output_content = fs::read_to_string(&output_file).unwrap();
assert!(
output_content.contains("USER www-data"),
"Expected existing USER directive to be preserved"
);
assert!(
!output_content.contains("groupadd -r appuser"),
"Should not add new user when one already exists"
);
}
#[test]
fn test_dockerfile_docker001_skip_for_scratch_image() {
let dockerfile_scratch = r#"FROM scratch
COPY app /app
CMD ["/app"]
"#;
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
let output_file = temp_dir.path().join("Dockerfile.purified");
fs::write(&input_file, dockerfile_scratch).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.arg("-o")
.arg(&output_file)
.assert()
.success();
let output_content = fs::read_to_string(&output_file).unwrap();
assert!(
!output_content.contains("USER"),
"Should not add USER directive for scratch images"
);
}
#[test]
fn test_dockerfile_docker002_pins_untagged_image() {
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
let output_file = temp_dir.path().join("Dockerfile.purified");
fs::write(&input_file, DOCKERFILE_UNPINNED).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.arg("-o")
.arg(&output_file)
.assert()
.success();
let output_content = fs::read_to_string(&output_file).unwrap();
assert!(
output_content.contains("FROM ubuntu:22.04")
|| output_content.contains("FROM ubuntu:24.04"),
"Expected ubuntu to be pinned to LTS version"
);
assert!(
!output_content.contains("FROM ubuntu\n") && !output_content.contains("FROM ubuntu "),
"Should not have unpinned ubuntu"
);
}
#[test]
fn test_dockerfile_docker002_pins_latest_tag() {
let dockerfile_latest = r#"FROM debian:latest
CMD ["bash"]
"#;
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
let output_file = temp_dir.path().join("Dockerfile.purified");
fs::write(&input_file, dockerfile_latest).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.arg("-o")
.arg(&output_file)
.assert()
.success();
let output_content = fs::read_to_string(&output_file).unwrap();
assert!(
output_content.contains("FROM debian:12") || output_content.contains("FROM debian:11"),
"Expected :latest to be replaced with stable version"
);
}
#[test]
fn test_dockerfile_docker003_adds_apt_cleanup() {
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
let output_file = temp_dir.path().join("Dockerfile.purified");
fs::write(&input_file, DOCKERFILE_NO_CLEANUP).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.arg("-o")
.arg(&output_file)
.assert()
.success();
let output_content = fs::read_to_string(&output_file).unwrap();
assert!(
output_content.contains("rm -rf /var/lib/apt/lists/*"),
"Expected apt cleanup command to be added"
);
assert!(
output_content.contains("apt-get install") && output_content.contains("rm -rf"),
"Cleanup should be in same RUN command"
);
}
#[test]
fn test_dockerfile_docker003_adds_apk_cleanup() {
let dockerfile_apk = r#"FROM alpine:3.19
RUN apk add curl
CMD ["bash"]
"#;
let temp_dir = TempDir::new().unwrap();
let input_file = temp_dir.path().join("Dockerfile");
let output_file = temp_dir.path().join("Dockerfile.purified");
fs::write(&input_file, dockerfile_apk).unwrap();
bashrs_cmd()
.arg("dockerfile")
.arg("purify")
.arg(&input_file)
.arg("-o")
.arg(&output_file)
.assert()
.success();
let output_content = fs::read_to_string(&output_file).unwrap();
assert!(
output_content.contains("rm -rf /var/cache/apk/*"),
"Expected apk cleanup command to be added"
);
}
#[test]
include!("cli_dockerfile_purify_tests_dockerfile_2.rs");