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_code(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_code(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_code(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")); }