@@ -329,6 +329,141 @@ TEST_F(nix_api_store_test_base, build_from_json)
329329 nix_store_free (store);
330330}
331331
332+ TEST_F (nix_api_store_test_base, nix_store_realise_invalid_system)
333+ {
334+ // Test that nix_store_realise properly reports errors when the system is invalid
335+ nix::experimentalFeatureSettings.set (" extra-experimental-features" , " ca-derivations" );
336+ nix::settings.substituters = {};
337+
338+ auto * store = open_local_store ();
339+
340+ std::filesystem::path unitTestData{getenv (" _NIX_TEST_UNIT_DATA" )};
341+ std::ifstream t{unitTestData / " derivation/ca/self-contained.json" };
342+ std::stringstream buffer;
343+ buffer << t.rdbuf ();
344+
345+ // Use an invalid system that cannot be built
346+ std::string jsonStr = nix::replaceStrings (buffer.str (), " x86_64-linux" , " bogus65-bogusos" );
347+
348+ auto * drv = nix_derivation_from_json (ctx, store, jsonStr.c_str ());
349+ assert_ctx_ok ();
350+ ASSERT_NE (drv, nullptr );
351+
352+ auto * drvPath = nix_add_derivation (ctx, store, drv);
353+ assert_ctx_ok ();
354+ ASSERT_NE (drvPath, nullptr );
355+
356+ int callbackCount = 0 ;
357+ auto cb = LambdaAdapter{.fun = [&](const char * outname, const StorePath * outPath) { callbackCount++; }};
358+
359+ auto ret = nix_store_realise (
360+ ctx, store, drvPath, static_cast <void *>(&cb), decltype (cb)::call_void<const char *, const StorePath *>);
361+
362+ // Should fail with an error
363+ ASSERT_NE (ret, NIX_OK);
364+ ASSERT_EQ (callbackCount, 0 ) << " Callback should not be invoked when build fails" ;
365+
366+ // Check that error message is set
367+ std::string errMsg = nix_err_msg (nullptr , ctx, nullptr );
368+ ASSERT_FALSE (errMsg.empty ()) << " Error message should be set" ;
369+ ASSERT_NE (errMsg.find (" system" ), std::string::npos) << " Error should mention system" ;
370+
371+ // Clean up
372+ nix_store_path_free (drvPath);
373+ nix_derivation_free (drv);
374+ nix_store_free (store);
375+ }
376+
377+ TEST_F (nix_api_store_test_base, nix_store_realise_builder_fails)
378+ {
379+ // Test that nix_store_realise properly reports errors when the builder fails
380+ nix::experimentalFeatureSettings.set (" extra-experimental-features" , " ca-derivations" );
381+ nix::settings.substituters = {};
382+
383+ auto * store = open_local_store ();
384+
385+ std::filesystem::path unitTestData{getenv (" _NIX_TEST_UNIT_DATA" )};
386+ std::ifstream t{unitTestData / " derivation/ca/self-contained.json" };
387+ std::stringstream buffer;
388+ buffer << t.rdbuf ();
389+
390+ // Replace with current system and make builder command fail
391+ std::string jsonStr = nix::replaceStrings (buffer.str (), " x86_64-linux" , nix::settings.thisSystem .get ());
392+ jsonStr = nix::replaceStrings (jsonStr, " echo $name foo > $out" , " exit 1" );
393+
394+ auto * drv = nix_derivation_from_json (ctx, store, jsonStr.c_str ());
395+ assert_ctx_ok ();
396+ ASSERT_NE (drv, nullptr );
397+
398+ auto * drvPath = nix_add_derivation (ctx, store, drv);
399+ assert_ctx_ok ();
400+ ASSERT_NE (drvPath, nullptr );
401+
402+ int callbackCount = 0 ;
403+ auto cb = LambdaAdapter{.fun = [&](const char * outname, const StorePath * outPath) { callbackCount++; }};
404+
405+ auto ret = nix_store_realise (
406+ ctx, store, drvPath, static_cast <void *>(&cb), decltype (cb)::call_void<const char *, const StorePath *>);
407+
408+ // Should fail with an error
409+ ASSERT_NE (ret, NIX_OK);
410+ ASSERT_EQ (callbackCount, 0 ) << " Callback should not be invoked when build fails" ;
411+
412+ // Check that error message is set
413+ std::string errMsg = nix_err_msg (nullptr , ctx, nullptr );
414+ ASSERT_FALSE (errMsg.empty ()) << " Error message should be set" ;
415+
416+ // Clean up
417+ nix_store_path_free (drvPath);
418+ nix_derivation_free (drv);
419+ nix_store_free (store);
420+ }
421+
422+ TEST_F (nix_api_store_test_base, nix_store_realise_builder_no_output)
423+ {
424+ // Test that nix_store_realise properly reports errors when builder succeeds but produces no output
425+ nix::experimentalFeatureSettings.set (" extra-experimental-features" , " ca-derivations" );
426+ nix::settings.substituters = {};
427+
428+ auto * store = open_local_store ();
429+
430+ std::filesystem::path unitTestData{getenv (" _NIX_TEST_UNIT_DATA" )};
431+ std::ifstream t{unitTestData / " derivation/ca/self-contained.json" };
432+ std::stringstream buffer;
433+ buffer << t.rdbuf ();
434+
435+ // Replace with current system and make builder succeed but not produce output
436+ std::string jsonStr = nix::replaceStrings (buffer.str (), " x86_64-linux" , nix::settings.thisSystem .get ());
437+ jsonStr = nix::replaceStrings (jsonStr, " echo $name foo > $out" , " true" );
438+
439+ auto * drv = nix_derivation_from_json (ctx, store, jsonStr.c_str ());
440+ assert_ctx_ok ();
441+ ASSERT_NE (drv, nullptr );
442+
443+ auto * drvPath = nix_add_derivation (ctx, store, drv);
444+ assert_ctx_ok ();
445+ ASSERT_NE (drvPath, nullptr );
446+
447+ int callbackCount = 0 ;
448+ auto cb = LambdaAdapter{.fun = [&](const char * outname, const StorePath * outPath) { callbackCount++; }};
449+
450+ auto ret = nix_store_realise (
451+ ctx, store, drvPath, static_cast <void *>(&cb), decltype (cb)::call_void<const char *, const StorePath *>);
452+
453+ // Should fail with an error
454+ ASSERT_NE (ret, NIX_OK);
455+ ASSERT_EQ (callbackCount, 0 ) << " Callback should not be invoked when build produces no output" ;
456+
457+ // Check that error message is set
458+ std::string errMsg = nix_err_msg (nullptr , ctx, nullptr );
459+ ASSERT_FALSE (errMsg.empty ()) << " Error message should be set" ;
460+
461+ // Clean up
462+ nix_store_path_free (drvPath);
463+ nix_derivation_free (drv);
464+ nix_store_free (store);
465+ }
466+
332467TEST_F (NixApiStoreTestWithRealisedPath, nix_store_get_fs_closure_with_outputs)
333468{
334469 // Test closure computation with include_outputs on a derivation path
0 commit comments