#[cfg(test)]
mod tests {
use nucleus::filesystem::{ContextMode, ContextPopulator, FilesystemState, TmpfsMount};
use nucleus::StateTransition;
use std::path::Path;
use tempfile::TempDir;
#[test]
fn test_populate_copies_files() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
std::fs::write(src.path().join("hello.txt"), "world").unwrap();
std::fs::write(src.path().join("main.rs"), "fn main() {}").unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
assert_eq!(
std::fs::read_to_string(dst.path().join("hello.txt")).unwrap(),
"world"
);
assert_eq!(
std::fs::read_to_string(dst.path().join("main.rs")).unwrap(),
"fn main() {}"
);
}
#[test]
fn test_populate_copies_nested_directories() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
std::fs::create_dir_all(src.path().join("a/b/c")).unwrap();
std::fs::write(src.path().join("a/b/c/deep.txt"), "deep").unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
assert_eq!(
std::fs::read_to_string(dst.path().join("a/b/c/deep.txt")).unwrap(),
"deep"
);
}
#[test]
fn test_populate_excludes_git() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
std::fs::create_dir(src.path().join(".git")).unwrap();
std::fs::write(src.path().join(".git/config"), "secret").unwrap();
std::fs::write(src.path().join("code.rs"), "pub fn foo() {}").unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
assert!(!dst.path().join(".git").exists());
assert!(dst.path().join("code.rs").exists());
}
#[test]
fn test_populate_excludes_sensitive_directories() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
for dir in &[".env", ".ssh", ".gnupg", ".aws", ".docker"] {
std::fs::create_dir(src.path().join(dir)).unwrap();
std::fs::write(src.path().join(format!("{}/secret", dir)), "sensitive").unwrap();
}
std::fs::write(src.path().join("safe.txt"), "ok").unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
for dir in &[".env", ".ssh", ".gnupg", ".aws", ".docker"] {
assert!(!dst.path().join(dir).exists(), "{} should be excluded", dir);
}
assert!(dst.path().join("safe.txt").exists());
}
#[test]
fn test_populate_excludes_env_variants() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
std::fs::write(src.path().join(".env.local"), "SECRET=x").unwrap();
std::fs::write(src.path().join(".env.production"), "DB=x").unwrap();
std::fs::write(src.path().join("config.toml"), "ok").unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
assert!(!dst.path().join(".env.local").exists());
assert!(!dst.path().join(".env.production").exists());
assert!(dst.path().join("config.toml").exists());
}
#[test]
fn test_populate_excludes_crypto_material() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
for ext in &[".pem", ".key", ".p12", ".crt", ".pfx", ".jks"] {
std::fs::write(src.path().join(format!("cert{}", ext)), "---").unwrap();
}
std::fs::write(src.path().join("app.rs"), "ok").unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
for ext in &[".pem", ".key", ".p12", ".crt", ".pfx", ".jks"] {
assert!(
!dst.path().join(format!("cert{}", ext)).exists(),
"*{} should be excluded",
ext
);
}
assert!(dst.path().join("app.rs").exists());
}
#[test]
fn test_populate_excludes_build_artifacts() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
for dir in &["target", "node_modules", "__pycache__"] {
std::fs::create_dir(src.path().join(dir)).unwrap();
std::fs::write(src.path().join(format!("{}/artifact", dir)), "big").unwrap();
}
std::fs::write(src.path().join("src.rs"), "ok").unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
for dir in &["target", "node_modules", "__pycache__"] {
assert!(!dst.path().join(dir).exists(), "{} should be excluded", dir);
}
}
#[test]
fn test_populate_excludes_editor_swap_files() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
std::fs::write(src.path().join("file.swp"), "swap").unwrap();
std::fs::write(src.path().join("file.swo"), "swap").unwrap();
std::fs::write(src.path().join("file.rs"), "ok").unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
assert!(!dst.path().join("file.swp").exists());
assert!(!dst.path().join("file.swo").exists());
assert!(dst.path().join("file.rs").exists());
}
#[test]
fn test_populate_excludes_secrets_by_name_pattern() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
std::fs::write(src.path().join("credentials.json"), "{}").unwrap();
std::fs::write(src.path().join("my_secret.txt"), "shh").unwrap();
std::fs::write(src.path().join("private_key.pem"), "---").unwrap();
std::fs::write(src.path().join("README.md"), "ok").unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
assert!(!dst.path().join("credentials.json").exists());
assert!(!dst.path().join("my_secret.txt").exists());
assert!(!dst.path().join("private_key.pem").exists());
assert!(dst.path().join("README.md").exists());
}
#[test]
fn test_populate_skips_symlinks() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
std::fs::write(src.path().join("real.txt"), "content").unwrap();
std::os::unix::fs::symlink(src.path().join("real.txt"), src.path().join("link.txt"))
.unwrap();
let populator = ContextPopulator::new(src.path(), dst.path());
populator.populate().unwrap();
assert!(dst.path().join("real.txt").exists());
assert!(
!dst.path().join("link.txt").exists(),
"Symlinks should be skipped"
);
}
#[test]
fn test_populate_nonexistent_source_errors() {
let dst = TempDir::new().unwrap();
let populator = ContextPopulator::new("/nonexistent/path", dst.path());
assert!(populator.populate().is_err());
}
#[test]
fn test_populate_source_is_file_errors() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
let file_path = src.path().join("file.txt");
std::fs::write(&file_path, "content").unwrap();
let populator = ContextPopulator::new(&file_path, dst.path());
assert!(populator.populate().is_err());
}
#[test]
fn test_populate_creates_destination_if_missing() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
let nested_dst = dst.path().join("a/b/c");
std::fs::write(src.path().join("file.txt"), "ok").unwrap();
let populator = ContextPopulator::new(src.path(), &nested_dst);
populator.populate().unwrap();
assert!(nested_dst.join("file.txt").exists());
}
#[test]
fn test_context_mode_copy_populates() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
std::fs::write(src.path().join("data.txt"), "hello").unwrap();
nucleus::filesystem::LazyContextPopulator::populate(
&ContextMode::Copy,
src.path(),
dst.path(),
)
.unwrap();
assert_eq!(
std::fs::read_to_string(dst.path().join("data.txt")).unwrap(),
"hello"
);
}
#[test]
fn test_context_mode_bind_mount_nonexistent_source() {
let dst = TempDir::new().unwrap();
let result = nucleus::filesystem::LazyContextPopulator::populate(
&ContextMode::BindMount,
Path::new("/nonexistent"),
dst.path(),
);
assert!(result.is_err());
}
#[test]
fn test_tmpfs_mount_initial_state() {
let mount = TmpfsMount::new("/tmp/test-nucleus-tmpfs", Some(64 * 1024 * 1024));
assert!(!mount.is_mounted());
assert_eq!(mount.path(), Path::new("/tmp/test-nucleus-tmpfs"));
}
#[test]
fn test_tmpfs_mount_no_size() {
let mount = TmpfsMount::new("/tmp/test-nucleus-tmpfs-nosiz", None);
assert!(!mount.is_mounted());
}
#[test]
fn test_filesystem_state_happy_path() {
let state = FilesystemState::Unmounted;
let state = state.transition(FilesystemState::Mounted).unwrap();
let state = state.transition(FilesystemState::Populated).unwrap();
let state = state.transition(FilesystemState::Pivoted).unwrap();
let state = state.transition(FilesystemState::UnmountedFinal).unwrap();
assert!(state.is_terminal());
}
#[test]
fn test_filesystem_state_stuttering_steps() {
assert!(FilesystemState::Unmounted
.transition(FilesystemState::Unmounted)
.is_ok());
assert!(FilesystemState::Mounted
.transition(FilesystemState::Mounted)
.is_ok());
assert!(FilesystemState::Populated
.transition(FilesystemState::Populated)
.is_ok());
assert!(FilesystemState::Pivoted
.transition(FilesystemState::Pivoted)
.is_ok());
assert!(FilesystemState::UnmountedFinal
.transition(FilesystemState::UnmountedFinal)
.is_ok());
}
#[test]
fn test_filesystem_state_cannot_skip() {
assert!(FilesystemState::Unmounted
.transition(FilesystemState::Populated)
.is_err());
assert!(FilesystemState::Mounted
.transition(FilesystemState::Pivoted)
.is_err());
assert!(FilesystemState::Unmounted
.transition(FilesystemState::UnmountedFinal)
.is_err());
}
#[test]
fn test_filesystem_state_cannot_go_backwards() {
assert!(FilesystemState::Pivoted
.transition(FilesystemState::Populated)
.is_err());
assert!(FilesystemState::UnmountedFinal
.transition(FilesystemState::Pivoted)
.is_err());
}
#[test]
fn test_filesystem_state_allows_cleanup_to_unmounted() {
assert!(FilesystemState::Mounted
.transition(FilesystemState::Unmounted)
.is_ok());
assert!(FilesystemState::Populated
.transition(FilesystemState::Unmounted)
.is_ok());
}
#[test]
fn test_filesystem_only_final_is_terminal() {
assert!(!FilesystemState::Unmounted.is_terminal());
assert!(!FilesystemState::Mounted.is_terminal());
assert!(!FilesystemState::Populated.is_terminal());
assert!(!FilesystemState::Pivoted.is_terminal());
assert!(FilesystemState::UnmountedFinal.is_terminal());
}
}