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

659 lines
18 KiB
Rust

#![cfg(feature = "daemon")]
mod utils;
use nix_js::value::Value;
use utils::{eval, eval_result};
#[test]
fn derivation_minimal() {
let result =
eval(r#"derivation { name = "hello"; builder = "/bin/sh"; system = "x86_64-linux"; }"#);
match result {
Value::AttrSet(attrs) => {
assert_eq!(attrs.get("type"), Some(&Value::String("derivation".into())));
assert_eq!(attrs.get("name"), Some(&Value::String("hello".into())));
assert_eq!(attrs.get("builder"), Some(&Value::String("/bin/sh".into())));
assert_eq!(
attrs.get("system"),
Some(&Value::String("x86_64-linux".into()))
);
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("drvPath"));
if let Some(Value::String(path)) = attrs.get("outPath") {
assert_eq!(path, "/nix/store/pnwh4xsfs4j508bs9iw6bpkyc4zw6ryx-hello");
} else {
panic!("outPath should be a string");
}
if let Some(Value::String(path)) = attrs.get("drvPath") {
assert_eq!(
path,
"/nix/store/x0sj6ynccvc1a8kxr8fifnlf7qlxw6hd-hello.drv"
);
} else {
panic!("drvPath should be a string");
}
}
_ => panic!("Expected AttrSet, got {:?}", result),
}
}
#[test]
fn derivation_with_args() {
let result = eval(
r#"derivation {
name = "test";
builder = "/bin/sh";
system = "x86_64-linux";
args = ["-c" "echo hello"];
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("args"));
if let Some(Value::List(args)) = attrs.get("args") {
assert_eq!(args.len(), 2);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn derivation_to_string() {
let result = eval(
r#"toString (derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; })"#,
);
match result {
Value::String(s) => assert_eq!(s, "/nix/store/xpcvxsx5sw4rbq666blz6sxqlmsqphmr-foo"),
_ => panic!("Expected String, got {:?}", result),
}
}
#[test]
fn derivation_missing_name() {
let result = eval_result(r#"derivation { builder = "/bin/sh"; system = "x86_64-linux"; }"#);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("missing required attribute 'name'"));
}
#[test]
fn derivation_invalid_name_with_drv_suffix() {
let result = eval_result(
r#"derivation { name = "foo.drv"; builder = "/bin/sh"; system = "x86_64-linux"; }"#,
);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("cannot end with .drv"));
}
#[test]
fn derivation_missing_builder() {
let result = eval_result(r#"derivation { name = "test"; system = "x86_64-linux"; }"#);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("missing required attribute 'builder'"));
}
#[test]
fn derivation_missing_system() {
let result = eval_result(r#"derivation { name = "test"; builder = "/bin/sh"; }"#);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("missing required attribute 'system'"));
}
#[test]
fn derivation_with_env_vars() {
let result = eval(
r#"derivation {
name = "test";
builder = "/bin/sh";
system = "x86_64-linux";
MY_VAR = "hello";
ANOTHER = "world";
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert_eq!(attrs.get("MY_VAR"), Some(&Value::String("hello".into())));
assert_eq!(attrs.get("ANOTHER"), Some(&Value::String("world".into())));
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn derivation_strict() {
let result = eval(
r#"builtins.derivationStrict { name = "test"; builder = "/bin/sh"; system = "x86_64-linux"; }"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("out"));
assert!(!attrs.contains_key("type"));
assert!(!attrs.contains_key("outPath"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn derivation_deterministic_paths() {
let expr = r#"derivation { name = "hello"; builder = "/bin/sh"; system = "x86_64-linux"; }"#;
let result1 = eval(expr);
let result2 = eval(expr);
match (result1, result2) {
(Value::AttrSet(attrs1), Value::AttrSet(attrs2)) => {
assert_eq!(attrs1.get("drvPath"), attrs2.get("drvPath"));
assert_eq!(attrs1.get("outPath"), attrs2.get("outPath"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn derivation_escaping_in_aterm() {
let result = eval(
r#"derivation {
name = "test";
builder = "/bin/sh";
system = "x86_64-linux";
args = ["-c" "echo \"hello\nworld\""];
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("outPath"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn multi_output_two_outputs() {
let drv = eval(
r#"derivation {
name = "multi";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev"];
}"#,
);
match drv {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("out"));
assert!(attrs.contains_key("dev"));
assert!(attrs.contains_key("outPath"));
if let Some(Value::String(drv_path)) = attrs.get("drvPath") {
assert_eq!(
drv_path,
"/nix/store/vmyjryfipkn9ss3ya23hk8p3m58l6dsl-multi.drv"
);
} else {
panic!(
"drvPath should be a string, got: {:?}",
attrs.get("drvPath")
);
}
if let Some(Value::String(out_path)) = attrs.get("outPath") {
assert_eq!(
out_path,
"/nix/store/a3d95yg9d215c54n0ybr4npmpnj29229-multi"
);
} else {
panic!("outPath should be a string");
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn multi_output_three_outputs() {
let result = eval(
r#"derivation {
name = "three";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev" "doc"];
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("out"));
assert!(attrs.contains_key("dev"));
assert!(attrs.contains_key("doc"));
// Verify exact paths match CppNix
if let Some(Value::String(drv_path)) = attrs.get("drvPath") {
assert_eq!(
drv_path,
"/nix/store/w08rpwvs5j9yxvdx5f5yg0p5i3ncazdx-three.drv"
);
}
if let Some(Value::String(out_path)) = attrs.get("out") {
assert_eq!(
out_path,
"/nix/store/i479clih5pb6bn2d2b758sbaylvbs2cl-three"
);
}
if let Some(Value::String(dev_path)) = attrs.get("dev") {
assert_eq!(
dev_path,
"/nix/store/gg8v395vci5xg1i9grc8ifh5xagw5f2j-three-dev"
);
}
if let Some(Value::String(doc_path)) = attrs.get("doc") {
assert_eq!(
doc_path,
"/nix/store/p2avgz16qx5k2jgnq3ch04k154xj1ac0-three-doc"
);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn multi_output_backward_compat() {
let result = eval(
r#"derivation {
name = "compat";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out"];
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("out"));
if let (Some(Value::String(out_path)), Some(Value::String(out))) =
(attrs.get("outPath"), attrs.get("out"))
{
assert_eq!(out_path, out);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn multi_output_deterministic() {
let result1 = eval(
r#"derivation {
name = "determ";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev"];
}"#,
);
let result2 = eval(
r#"derivation {
name = "determ";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev"];
}"#,
);
assert_eq!(result1, result2);
}
#[test]
fn fixed_output_sha256_flat() {
let result = eval(
r#"derivation {
name = "fixed";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "0000000000000000000000000000000000000000000000000000000000000000";
outputHashAlgo = "sha256";
outputHashMode = "flat";
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("drvPath"));
// Verify exact paths match CppNix
if let Some(Value::String(out_path)) = attrs.get("outPath") {
assert_eq!(
out_path,
"/nix/store/ap9h69qwrm5060ldi96axyklh3pr3yjn-fixed"
);
}
if let Some(Value::String(drv_path)) = attrs.get("drvPath") {
assert_eq!(
drv_path,
"/nix/store/kj9gsfz5cngc38n1xlf6ljlgvnsfg0cj-fixed.drv"
);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn fixed_output_default_algo() {
let result = eval(
r#"derivation {
name = "default";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "0000000000000000000000000000000000000000000000000000000000000000";
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("outPath"));
// Verify it defaults to sha256 (same as explicitly specifying it)
if let Some(Value::String(out_path)) = attrs.get("outPath") {
assert!(out_path.contains("/nix/store/"));
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn fixed_output_recursive_mode() {
let result = eval(
r#"derivation {
name = "recursive";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "1111111111111111111111111111111111111111111111111111111111111111";
outputHashAlgo = "sha256";
outputHashMode = "recursive";
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("drvPath"));
// Verify exact path matches CppNix
if let Some(Value::String(out_path)) = attrs.get("outPath") {
assert_eq!(
out_path,
"/nix/store/qyal5s16hfwxhz5zwpf8h8yv2bs84z56-recursive"
);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn fixed_output_rejects_multi_output() {
let result = eval_result(
r#"derivation {
name = "invalid";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "0000000000000000000000000000000000000000000000000000000000000000";
outputs = ["out" "dev"];
}"#,
);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("fixed-output") && err_msg.contains("one"));
}
#[test]
fn fixed_output_invalid_hash_mode() {
let result = eval_result(
r#"derivation {
name = "invalid";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "0000000000000000000000000000000000000000000000000000000000000000";
outputHashMode = "invalid";
}"#,
);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("outputHashMode") && err_msg.contains("invalid"));
}
#[test]
fn structured_attrs_basic() {
let result = eval(
r#"derivation {
name = "struct";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = true;
foo = "bar";
count = 42;
enabled = true;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("foo"));
assert!(attrs.contains_key("count"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn structured_attrs_nested() {
let result = eval(
r#"derivation {
name = "nested";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = true;
data = { x = 1; y = [2 3]; };
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("data"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn structured_attrs_rejects_functions() {
let result = eval_result(
r#"derivation {
name = "invalid";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = true;
func = x: x + 1;
}"#,
);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("function") && err_msg.contains("serialize"));
}
#[test]
fn structured_attrs_false() {
let result = eval(
r#"derivation {
name = "normal";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = false;
foo = "bar";
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("foo"));
if let Some(Value::String(val)) = attrs.get("foo") {
assert_eq!(val, "bar");
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn ignore_nulls_true() {
let result = eval(
r#"derivation {
name = "ignore";
builder = "/bin/sh";
system = "x86_64-linux";
__ignoreNulls = true;
foo = "bar";
nullValue = null;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("foo"));
assert!(attrs.contains_key("nullValue"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn ignore_nulls_false() {
let result = eval(
r#"derivation {
name = "keep";
builder = "/bin/sh";
system = "x86_64-linux";
__ignoreNulls = false;
nullValue = null;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("nullValue"));
if let Some(Value::String(val)) = attrs.get("nullValue") {
assert_eq!(val, "");
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn ignore_nulls_with_structured_attrs() {
let result = eval(
r#"derivation {
name = "combined";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = true;
__ignoreNulls = true;
foo = "bar";
nullValue = null;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("foo"));
assert!(attrs.contains_key("nullValue"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn all_features_combined() {
let result = eval(
r#"derivation {
name = "all";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev"];
__structuredAttrs = true;
__ignoreNulls = true;
data = { x = 1; };
nullValue = null;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("out"));
assert!(attrs.contains_key("dev"));
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("data"));
assert!(attrs.contains_key("nullValue"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test]
fn fixed_output_with_structured_attrs() {
let result = eval(
r#"derivation {
name = "fixstruct";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "abc123";
__structuredAttrs = true;
data = { key = "value"; };
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("data"));
}
_ => panic!("Expected AttrSet"),
}
}