Files
nix-js/nix-js/tests/io_operations.rs

371 lines
11 KiB
Rust

mod utils;
use nix_js::context::Context;
use nix_js::error::Source;
use nix_js::value::Value;
use crate::utils::{eval, eval_result};
#[test]
fn import_absolute_path() {
let temp_dir = tempfile::tempdir().unwrap();
let lib_path = temp_dir.path().join("nix_test_lib.nix");
std::fs::write(&lib_path, "{ add = a: b: a + b; }").unwrap();
let expr = format!(r#"(import "{}").add 3 5"#, lib_path.display());
assert_eq!(eval(&expr), Value::Int(8));
}
#[test]
fn import_nested() {
let temp_dir = tempfile::tempdir().unwrap();
let lib_path = temp_dir.path().join("lib.nix");
std::fs::write(&lib_path, "{ add = a: b: a + b; }").unwrap();
let main_path = temp_dir.path().join("main.nix");
let main_content = format!(
r#"let lib = import {}; in {{ result = lib.add 10 20; }}"#,
lib_path.display()
);
std::fs::write(&main_path, main_content).unwrap();
let expr = format!(r#"(import "{}").result"#, main_path.display());
assert_eq!(eval(&expr), Value::Int(30));
}
#[test]
fn import_relative_path() {
let temp_dir = tempfile::tempdir().unwrap();
let subdir = temp_dir.path().join("subdir");
std::fs::create_dir_all(&subdir).unwrap();
let lib_path = temp_dir.path().join("lib.nix");
std::fs::write(&lib_path, "{ multiply = a: b: a * b; }").unwrap();
let helper_path = subdir.join("helper.nix");
std::fs::write(&helper_path, "{ subtract = a: b: a - b; }").unwrap();
let main_path = temp_dir.path().join("main.nix");
let main_content = r#"
let
lib = import ./lib.nix;
helper = import ./subdir/helper.nix;
in {
result1 = lib.multiply 3 4;
result2 = helper.subtract 10 3;
}
"#;
std::fs::write(&main_path, main_content).unwrap();
let expr = format!(r#"let x = import "{}"; in x.result1"#, main_path.display());
assert_eq!(eval(&expr), Value::Int(12));
let expr = format!(r#"let x = import "{}"; in x.result2"#, main_path.display());
assert_eq!(eval(&expr), Value::Int(7));
}
#[test]
fn import_returns_function() {
let temp_dir = tempfile::tempdir().unwrap();
let func_path = temp_dir.path().join("nix_test_func.nix");
std::fs::write(&func_path, "x: x * 2").unwrap();
let expr = format!(r#"(import "{}") 5"#, func_path.display());
assert_eq!(eval(&expr), Value::Int(10));
}
#[test]
fn import_with_complex_dependency_graph() {
let temp_dir = tempfile::tempdir().unwrap();
let utils_path = temp_dir.path().join("utils.nix");
std::fs::write(&utils_path, "{ double = x: x * 2; }").unwrap();
let math_path = temp_dir.path().join("math.nix");
let math_content = r#"let utils = import ./utils.nix; in { triple = x: x + utils.double x; }"#;
std::fs::write(&math_path, math_content).unwrap();
let main_path = temp_dir.path().join("main.nix");
let main_content = r#"let math = import ./math.nix; in math.triple 5"#;
std::fs::write(&main_path, main_content).unwrap();
let expr = format!(r#"import "{}""#, main_path.display());
assert_eq!(eval(&expr), Value::Int(15));
}
// Tests for builtins.path
#[test]
fn path_with_file() {
let mut ctx = Context::new().unwrap();
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "Hello, World!").unwrap();
let expr = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
// Should return a store path string
if let Value::String(store_path) = result {
assert!(store_path.starts_with(ctx.get_store_dir()));
assert!(store_path.contains("test.txt"));
} else {
panic!("Expected string, got {:?}", result);
}
}
#[test]
fn path_with_custom_name() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("original.txt");
std::fs::write(&test_file, "Content").unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; name = "custom-name"; }}"#,
test_file.display()
);
let result = eval(&expr);
if let Value::String(store_path) = result {
assert!(store_path.contains("custom-name"));
assert!(!store_path.contains("original.txt"));
} else {
panic!("Expected string, got {:?}", result);
}
}
#[test]
fn path_with_directory_recursive() {
let mut ctx = Context::new().unwrap();
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("mydir");
std::fs::create_dir_all(&test_dir).unwrap();
std::fs::write(test_dir.join("file1.txt"), "Content 1").unwrap();
std::fs::write(test_dir.join("file2.txt"), "Content 2").unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; recursive = true; }}"#,
test_dir.display()
);
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
if let Value::String(store_path) = result {
assert!(store_path.starts_with(ctx.get_store_dir()));
assert!(store_path.contains("mydir"));
} else {
panic!("Expected string, got {:?}", result);
}
}
#[test]
fn path_flat_with_file() {
let mut ctx = Context::new().unwrap();
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("flat.txt");
std::fs::write(&test_file, "Flat content").unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; recursive = false; }}"#,
test_file.display()
);
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
if let Value::String(store_path) = result {
assert!(store_path.starts_with(ctx.get_store_dir()));
} else {
panic!("Expected string, got {:?}", result);
}
}
#[test]
fn path_flat_with_directory_fails() {
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("mydir");
std::fs::create_dir_all(&test_dir).unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; recursive = false; }}"#,
test_dir.display()
);
let result = eval_result(&expr);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("recursive") || err_msg.contains("regular file"));
}
#[test]
fn path_nonexistent_fails() {
let expr = r#"builtins.path { path = "/nonexistent/path/that/should/not/exist"; }"#;
let result = eval_result(expr);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("does not exist"));
}
#[test]
fn path_missing_path_param() {
let expr = r#"builtins.path { name = "test"; }"#;
let result = eval_result(expr);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("path") && err_msg.contains("required"));
}
#[test]
fn path_with_sha256() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("hash_test.txt");
std::fs::write(&test_file, "Test content for hashing").unwrap();
// First, get the hash by calling without sha256
let expr1 = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
let result1 = eval(&expr1);
let store_path1 = match result1 {
Value::String(s) => s,
_ => panic!("Expected string"),
};
// Compute the actual hash (for testing, we'll just verify the same path is returned)
// In real usage, the user would know the hash beforehand
let expr2 = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
let result2 = eval(&expr2);
let store_path2 = match result2 {
Value::String(s) => s,
_ => panic!("Expected string"),
};
// Same input should produce same output
assert_eq!(store_path1, store_path2);
}
#[test]
fn path_deterministic() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("deterministic.txt");
std::fs::write(&test_file, "Same content").unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; name = "myfile"; }}"#,
test_file.display()
);
let result1 = eval(&expr);
let result2 = eval(&expr);
// Same inputs should produce same store path
assert_eq!(result1, result2);
}
#[test]
fn read_file_type_regular_file() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "Test content").unwrap();
let expr = format!(r#"builtins.readFileType {}"#, test_file.display());
assert_eq!(eval(&expr), Value::String("regular".to_string()));
}
#[test]
fn read_file_type_directory() {
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("testdir");
std::fs::create_dir(&test_dir).unwrap();
let expr = format!(r#"builtins.readFileType {}"#, test_dir.display());
assert_eq!(eval(&expr), Value::String("directory".to_string()));
}
#[test]
fn read_file_type_symlink() {
let temp_dir = tempfile::tempdir().unwrap();
let target = temp_dir.path().join("target.txt");
let symlink = temp_dir.path().join("link.txt");
std::fs::write(&target, "Target content").unwrap();
#[cfg(unix)]
std::os::unix::fs::symlink(&target, &symlink).unwrap();
#[cfg(unix)]
{
let expr = format!(r#"builtins.readFileType {}"#, symlink.display());
assert_eq!(eval(&expr), Value::String("symlink".to_string()));
}
}
#[test]
fn read_dir_basic() {
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("readdir_test");
std::fs::create_dir(&test_dir).unwrap();
std::fs::write(test_dir.join("file1.txt"), "Content 1").unwrap();
std::fs::write(test_dir.join("file2.txt"), "Content 2").unwrap();
std::fs::create_dir(test_dir.join("subdir")).unwrap();
let expr = format!(r#"builtins.readDir {}"#, test_dir.display());
let result = eval(&expr);
if let Value::AttrSet(attrs) = result {
assert_eq!(
attrs.get("file1.txt"),
Some(&Value::String("regular".to_string()))
);
assert_eq!(
attrs.get("file2.txt"),
Some(&Value::String("regular".to_string()))
);
assert_eq!(
attrs.get("subdir"),
Some(&Value::String("directory".to_string()))
);
assert_eq!(attrs.len(), 3);
} else {
panic!("Expected AttrSet, got {:?}", result);
}
}
#[test]
fn read_dir_empty() {
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("empty_dir");
std::fs::create_dir(&test_dir).unwrap();
let expr = format!(r#"builtins.readDir {}"#, test_dir.display());
let result = eval(&expr);
if let Value::AttrSet(attrs) = result {
assert_eq!(attrs.len(), 0);
} else {
panic!("Expected AttrSet, got {:?}", result);
}
}
#[test]
fn read_dir_nonexistent_fails() {
let expr = r#"builtins.readDir "/nonexistent/directory""#;
let result = eval_result(expr);
assert!(result.is_err());
}
#[test]
fn read_dir_on_file_fails() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "Test content").unwrap();
let expr = format!(r#"builtins.readDir {}"#, test_file.display());
let result = eval_result(&expr);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("not a directory"));
}