feat: initial path implementation
This commit is contained in:
@@ -100,3 +100,186 @@ fn import_with_complex_dependency_graph() {
|
||||
let expr = format!(r#"import "{}""#, main_path.display());
|
||||
assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(15));
|
||||
}
|
||||
|
||||
// Tests for builtins.path
|
||||
|
||||
#[test]
|
||||
fn test_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_code(&expr).unwrap();
|
||||
|
||||
// Should return a store path string
|
||||
if let Value::String(store_path) = result {
|
||||
assert!(store_path.starts_with("/nix/store/"));
|
||||
assert!(store_path.contains("test.txt"));
|
||||
} else {
|
||||
panic!("Expected string, got {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_with_custom_name() {
|
||||
let mut ctx = Context::new().unwrap();
|
||||
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 = ctx.eval_code(&expr).unwrap();
|
||||
|
||||
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 test_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_code(&expr).unwrap();
|
||||
|
||||
if let Value::String(store_path) = result {
|
||||
assert!(store_path.starts_with("/nix/store/"));
|
||||
assert!(store_path.contains("mydir"));
|
||||
} else {
|
||||
panic!("Expected string, got {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_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_code(&expr).unwrap();
|
||||
|
||||
if let Value::String(store_path) = result {
|
||||
assert!(store_path.starts_with("/nix/store/"));
|
||||
} else {
|
||||
panic!("Expected string, got {:?}", result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_flat_with_directory_fails() {
|
||||
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();
|
||||
|
||||
let expr = format!(
|
||||
r#"builtins.path {{ path = {}; recursive = false; }}"#,
|
||||
test_dir.display()
|
||||
);
|
||||
let result = ctx.eval_code(&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 test_path_nonexistent_fails() {
|
||||
let mut ctx = Context::new().unwrap();
|
||||
|
||||
let expr = r#"builtins.path { path = "/nonexistent/path/that/should/not/exist"; }"#;
|
||||
let result = ctx.eval_code(expr);
|
||||
|
||||
assert!(result.is_err());
|
||||
let err_msg = result.unwrap_err().to_string();
|
||||
assert!(err_msg.contains("does not exist"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_missing_path_param() {
|
||||
let mut ctx = Context::new().unwrap();
|
||||
|
||||
let expr = r#"builtins.path { name = "test"; }"#;
|
||||
let result = ctx.eval_code(expr);
|
||||
|
||||
assert!(result.is_err());
|
||||
let err_msg = result.unwrap_err().to_string();
|
||||
assert!(err_msg.contains("path") && err_msg.contains("required"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_with_sha256() {
|
||||
let mut ctx = Context::new().unwrap();
|
||||
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 = ctx.eval_code(&expr1).unwrap();
|
||||
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 = ctx.eval_code(&expr2).unwrap();
|
||||
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 test_path_deterministic() {
|
||||
let mut ctx = Context::new().unwrap();
|
||||
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 = ctx.eval_code(&expr).unwrap();
|
||||
let result2 = ctx.eval_code(&expr).unwrap();
|
||||
|
||||
// Same inputs should produce same store path
|
||||
assert_eq!(result1, result2);
|
||||
}
|
||||
|
||||
118
nix-js/tests/path_operations.rs
Normal file
118
nix-js/tests/path_operations.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
mod utils;
|
||||
|
||||
use nix_js::value::Value;
|
||||
use utils::{eval, eval_result};
|
||||
|
||||
#[test]
|
||||
fn test_path_type_of() {
|
||||
let result = eval("builtins.typeOf ./foo");
|
||||
assert_eq!(result, Value::String("path".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_path_true() {
|
||||
let result = eval("builtins.isPath ./foo");
|
||||
assert_eq!(result, Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_path_false_string() {
|
||||
let result = eval(r#"builtins.isPath "./foo""#);
|
||||
assert_eq!(result, Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_path_false_number() {
|
||||
let result = eval("builtins.isPath 42");
|
||||
assert_eq!(result, Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_concat_type() {
|
||||
// path + string = path
|
||||
let result = eval(r#"builtins.typeOf (./foo + "/bar")"#);
|
||||
assert_eq!(result, Value::String("path".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_path_concat_type() {
|
||||
// string + path = string
|
||||
let result = eval(r#"builtins.typeOf ("prefix-" + ./foo)"#);
|
||||
assert_eq!(result, Value::String("string".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basename_of_path() {
|
||||
let result = eval("builtins.baseNameOf ./path/to/file.nix");
|
||||
assert!(matches!(result, Value::String(s) if s == "file.nix"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basename_of_string() {
|
||||
let result = eval(r#"builtins.baseNameOf "/path/to/file.nix""#);
|
||||
assert_eq!(result, Value::String("file.nix".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dir_of_path_type() {
|
||||
// dirOf preserves path type
|
||||
let result = eval("builtins.typeOf (builtins.dirOf ./path/to/file.nix)");
|
||||
assert_eq!(result, Value::String("path".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dir_of_string_type() {
|
||||
// dirOf preserves string type
|
||||
let result = eval(r#"builtins.typeOf (builtins.dirOf "/path/to/file.nix")"#);
|
||||
assert_eq!(result, Value::String("string".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_equality() {
|
||||
// Same path should be equal
|
||||
let result = eval("./foo == ./foo");
|
||||
assert_eq!(result, Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_not_equal_string() {
|
||||
// Paths and strings are different types - should not be equal
|
||||
let result = eval(r#"./foo == "./foo""#);
|
||||
assert_eq!(result, Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_path_absolute() {
|
||||
// toPath with absolute path returns string
|
||||
let result = eval(r#"builtins.toPath "/foo/bar""#);
|
||||
assert_eq!(result, Value::String("/foo/bar".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_path_type_is_string() {
|
||||
// toPath returns a string, not a path
|
||||
let result = eval(r#"builtins.typeOf (builtins.toPath "/foo")"#);
|
||||
assert_eq!(result, Value::String("string".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_path_relative_fails() {
|
||||
// toPath with relative path should fail
|
||||
let result = eval_result(r#"builtins.toPath "foo/bar""#);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_path_empty_fails() {
|
||||
// toPath with empty string should fail
|
||||
let result = eval_result(r#"builtins.toPath """#);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_path_from_path_value() {
|
||||
// toPath can accept a path value too (coerces to string first)
|
||||
let result = eval("builtins.toPath ./foo");
|
||||
// Should succeed and return the absolute path as a string
|
||||
assert!(matches!(result, Value::String(s) if s.starts_with("/")));
|
||||
}
|
||||
Reference in New Issue
Block a user