feat: initial path implementation

This commit is contained in:
2026-01-16 23:09:41 +08:00
parent 97e5e7b995
commit f2fc12026f
22 changed files with 903 additions and 61 deletions

View File

@@ -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);
}