use std::{fs, os::unix::fs::PermissionsExt};
use tempfile::tempdir;
use monocore::{
oci::{
distribution::{DockerRegistry, OciRegistryPull},
rootfs,
},
utils::MERGED_SUBDIR,
};
#[test_log::test(tokio::test)]
#[ignore = "requires pulling Docker images"]
async fn test_oci_rootfs_merge_basic_merge() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let registry = DockerRegistry::with_oci_dir(temp_dir.path().to_path_buf());
registry
.pull_image("library/alpine", Some("latest"))
.await?;
let dest_dir = temp_dir.path().join("merged_test");
fs::create_dir_all(&dest_dir)?;
rootfs::merge(temp_dir.path(), &dest_dir, "library_alpine__latest").await?;
let expected_dirs = vec!["bin", "etc", "home", "root", "usr", "var"];
for dir in &expected_dirs {
assert!(
dest_dir.join(MERGED_SUBDIR).join(dir).exists(),
"Directory {} should exist",
dir
);
}
rootfs::remove(&dest_dir).await?;
Ok(())
}
#[test_log::test(tokio::test)]
#[ignore = "requires pulling Docker images"]
async fn test_oci_rootfs_merge_layer_permissions() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let registry = DockerRegistry::with_oci_dir(temp_dir.path().to_path_buf());
registry.pull_image("library/nginx", Some("alpine")).await?;
let dest_dir = temp_dir.path().join("merged_perms_test");
fs::create_dir_all(&dest_dir)?;
rootfs::merge(temp_dir.path(), &dest_dir, "library_nginx__alpine").await?;
let nginx_binary = dest_dir.join("merged/usr/sbin/nginx");
let metadata = fs::metadata(&nginx_binary)?;
let mode = metadata.permissions().mode();
assert!(
mode & 0o111 != 0,
"nginx binary should have executable permissions"
);
rootfs::remove(&dest_dir).await?;
Ok(())
}
#[test_log::test(tokio::test)]
#[ignore = "requires pulling Docker images"]
async fn test_oci_rootfs_merge_merge_cleanup() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let registry = DockerRegistry::with_oci_dir(temp_dir.path().to_path_buf());
registry
.pull_image("library/alpine", Some("latest"))
.await?;
let dest_dir = temp_dir.path().join("merged_cleanup_test");
fs::create_dir_all(&dest_dir)?;
rootfs::merge(temp_dir.path(), &dest_dir, "library_alpine__latest").await?;
assert!(dest_dir.join(MERGED_SUBDIR).exists());
rootfs::remove(&dest_dir).await?;
assert!(!dest_dir.join(MERGED_SUBDIR).exists());
Ok(())
}
#[test_log::test(tokio::test)]
#[ignore = "requires pulling Docker images"]
async fn test_oci_rootfs_merge_concurrent_merges() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let registry = DockerRegistry::with_oci_dir(temp_dir.path().to_path_buf());
let pull_tasks = tokio::join!(
registry.pull_image("library/alpine", Some("latest")),
registry.pull_image("library/busybox", Some("latest"))
);
pull_tasks.0?;
pull_tasks.1?;
let dest_dir1 = temp_dir.path().join("merged_concurrent_1");
let dest_dir2 = temp_dir.path().join("merged_concurrent_2");
fs::create_dir_all(&dest_dir1)?;
fs::create_dir_all(&dest_dir2)?;
let merge_results = tokio::join!(
rootfs::merge(temp_dir.path(), &dest_dir1, "library_alpine__latest"),
rootfs::merge(temp_dir.path(), &dest_dir2, "library_busybox__latest")
);
merge_results.0?;
merge_results.1?;
assert!(dest_dir1.join("merged/bin").exists());
assert!(dest_dir2.join("merged/bin").exists());
let cleanup_results = tokio::join!(rootfs::remove(&dest_dir1), rootfs::unmount(&dest_dir2));
cleanup_results.0?;
cleanup_results.1?;
Ok(())
}
#[test_log::test(tokio::test)]
#[ignore = "requires pulling Docker images"]
async fn test_oci_rootfs_merge_error_handling() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let dest_dir = temp_dir.path().join("merged_error_test");
fs::create_dir_all(&dest_dir)?;
let result = rootfs::merge(temp_dir.path(), &dest_dir, "nonexistent_image").await;
assert!(result.is_err());
assert!(!dest_dir.join("work").exists());
assert!(!dest_dir.join("upper").exists());
assert!(!dest_dir.join(MERGED_SUBDIR).exists());
Ok(())
}