use super::*;
fn hermetic_config(toml: &str) -> ResolvedCrateConfig {
let alef_cfg: crate::core::config::NewAlefConfig = toml::from_str(toml).unwrap();
alef_cfg.resolve().unwrap().remove(0)
}
#[test]
fn explicit_environment_reaches_the_ffi_build_process() {
let config = hermetic_config(
r#"
[workspace]
languages = ["ffi"]
[workspace.build_commands.ffi]
precondition = "true"
build = 'test "$ALEF_EXPORT_GENERATED_HEADERS" = "1"'
[[crates]]
name = "environment-test-lib"
sources = ["src/lib.rs"]
"#,
);
build_with_environment(
&config,
&[Language::Ffi],
false,
&[("ALEF_EXPORT_GENERATED_HEADERS", "1")],
false,
)
.expect("the explicit header-export environment must reach Cargo's build process");
}
#[test]
fn one_backend_failure_does_not_block_the_others() {
let marker_dir = tempfile::tempdir().expect("failed to create temp dir for build markers");
let marker_node = marker_dir.path().join("node.built");
let marker_go = marker_dir.path().join("go.built");
let config = hermetic_config(&format!(
r#"
[workspace]
languages = ["php", "node", "ffi", "go"]
[workspace.build_commands.php]
precondition = "true"
build = "false"
[workspace.build_commands.node]
precondition = "true"
build = "touch {node_marker}"
[workspace.build_commands.ffi]
precondition = "true"
build = "true"
[workspace.build_commands.go]
precondition = "true"
build = "touch {go_marker}"
[[crates]]
name = "orchestration-test-lib"
sources = ["src/lib.rs"]
"#,
node_marker = marker_node.display(),
go_marker = marker_go.display(),
));
let result = build(
&config,
&[Language::Php, Language::Node, Language::Ffi, Language::Go],
false,
false,
);
assert!(result.is_err(), "php's failure must surface in the aggregate result");
let message = format!("{:#}", result.unwrap_err());
assert!(
message.contains("php"),
"aggregate error must name the failed language: {message}"
);
assert!(
marker_node.exists(),
"node (independent, ordered after php in the list) must still be attempted and succeed"
);
assert!(
marker_go.exists(),
"go (ffi_dependent) must still be attempted and succeed even though the independent \
stage had a failure — that's the class of language that used to be silently dropped"
);
}
#[test]
fn strict_fails_the_run_when_a_language_is_skipped_for_a_missing_toolchain() {
let marker_dir = tempfile::tempdir().expect("tempdir");
let node_marker = marker_dir.path().join("node.built");
let config = hermetic_config(&format!(
r#"
[workspace]
languages = ["node", "go"]
[workspace.build_commands.node]
precondition = "true"
build = "touch {node_marker}"
[workspace.build_commands.go]
precondition = "false"
build = "true"
[[crates]]
name = "strict-toolchain-test-lib"
sources = ["src/lib.rs"]
"#,
node_marker = node_marker.display(),
));
let lenient = build(&config, &[Language::Node, Language::Go], false, false);
assert!(
lenient.is_ok(),
"without --strict, a toolchain-missing skip must not fail the run: {lenient:?}"
);
assert!(
node_marker.exists(),
"node has no toolchain problem and must still be built"
);
let node_marker_strict = marker_dir.path().join("node.built.strict");
let config = hermetic_config(&format!(
r#"
[workspace]
languages = ["node", "go"]
[workspace.build_commands.node]
precondition = "true"
build = "touch {node_marker}"
[workspace.build_commands.go]
precondition = "false"
build = "true"
[[crates]]
name = "strict-toolchain-test-lib"
sources = ["src/lib.rs"]
"#,
node_marker = node_marker_strict.display(),
));
let strict = build(&config, &[Language::Node, Language::Go], false, true);
let message = strict
.map(|()| String::new())
.unwrap_or_else(|error| format!("{error:#}"));
assert!(
!message.is_empty(),
"--strict must fail the run when a language was skipped for a missing toolchain"
);
assert!(message.contains("--strict is set"), "{message}");
assert!(message.contains("go"), "{message}");
assert!(
node_marker_strict.exists(),
"go's toolchain skip must not stop an unrelated, ready language from being attempted"
);
}