use crate::core::template_versions as tv;
use crate::e2e::config::E2eConfig;
use std::fmt::Write as FmtWrite;
fn render_env_setup_block(e2e_config: &E2eConfig) -> String {
if e2e_config.env.is_empty() {
return String::new();
}
let mut keys: Vec<&String> = e2e_config.env.keys().collect();
keys.sort();
let mut out = String::new();
for k in keys {
let v = &e2e_config.env[k];
out.push_str(&format!(
"unless System.get_env(\"{}\") do\n System.put_env(\"{}\", \"{}\")\nend\n",
k, k, v
));
}
out.push('\n');
out
}
fn elixir_nif_module(e2e_config: &E2eConfig) -> String {
let raw_module = &e2e_config.call.module;
let module_path = if raw_module.contains('.') || raw_module.chars().next().is_some_and(|c| c.is_uppercase()) {
raw_module.clone()
} else {
super::values::elixir_module_name(raw_module)
};
format!("{module_path}.Native")
}
fn render_native_env_and_chdir(e2e_config: &E2eConfig) -> String {
if e2e_config.env.is_empty() {
return String::new();
}
let mut out = String::new();
let nif_module = elixir_nif_module(e2e_config);
out.push_str("# Erlang os:putenv does not propagate to the native C runtime that an FFI\n");
out.push_str("# library reads via getenv. Push each value through the binding's set_env NIF\n");
out.push_str("# (libc setenv) so the native side observes the same environment.\n");
let mut keys: Vec<&String> = e2e_config.env.keys().collect();
keys.sort();
for k in keys {
let v = &e2e_config.env[k];
out.push_str("try do\n");
let _ = writeln!(out, " {nif_module}.set_env(\"{k}\", \"{v}\")");
out.push_str("rescue\n");
out.push_str(" _ -> :ok\n");
out.push_str("end\n");
}
out.push('\n');
let rel = e2e_config.test_documents_relative_from(1);
out.push_str("# Run from the test-documents dir so relative file URIs (e.g. \"text/report.txt\")\n");
out.push_str("# resolve, mirroring the other language suites which chdir before running.\n");
out.push_str("test_documents_dir =\n");
let _ = writeln!(
out,
" System.get_env(\"ALEF_TEST_DOCUMENTS_DIR\") || Path.expand(\"{rel}\", __DIR__)"
);
out.push('\n');
out.push_str("if File.dir?(test_documents_dir), do: File.cd!(test_documents_dir)\n\n");
out
}
pub(super) fn render_test_helper(has_http_tests: bool, e2e_config: &E2eConfig) -> String {
let env_setup = render_env_setup_block(e2e_config);
let native_and_chdir = render_native_env_and_chdir(e2e_config);
if has_http_tests {
let finch_setup = r#"# Start a named Finch pool before ExUnit configured to use HTTP/1 only.
# Tests pass `finch: AlefE2EFinch` on every Req call; the pool's protocol
# selection (via `pools.default.protocols: [:http1]`) is the canonical place
# to pin the wire protocol since Req rejects per-call `:connect_options` when
# `:finch` is set.
{:ok, _} = Finch.start_link(name: AlefE2EFinch, pools: %{:default => [protocols: [:http1]]})
ExUnit.start()
"#;
let mock_server =
crate::e2e::template_env::render("elixir/test_helper_mock_server.exs.jinja", minijinja::context!());
format!("{}{}{}{}", env_setup, native_and_chdir, finch_setup, mock_server)
} else {
format!("{}{}ExUnit.start()\n", env_setup, native_and_chdir)
}
}
pub(super) fn render_mix_exs(
pkg_name: &str,
pkg_path: &str,
dep_mode: crate::e2e::config::DependencyMode,
has_http_tests: bool,
has_mock_server_tests: bool,
has_nif_tests: bool,
uses_harness: bool,
) -> String {
let mut out = String::new();
let _ = writeln!(out, "defmodule E2eElixir.MixProject do");
let _ = writeln!(out, " use Mix.Project");
let _ = writeln!(out);
let _ = writeln!(out, " def project do");
let _ = writeln!(out, " [");
let _ = writeln!(out, " app: :e2e_elixir,");
let _ = writeln!(out, " version: \"0.1.0\",");
let _ = writeln!(out, " elixir: \"~> 1.14\",");
let _ = writeln!(out, " deps: deps()");
let _ = writeln!(out, " ]");
let _ = writeln!(out, " end");
let _ = writeln!(out);
let _ = writeln!(out, " defp deps do");
let _ = writeln!(out, " [");
let mut deps: Vec<String> = Vec::new();
if has_nif_tests && !pkg_path.is_empty() {
let pkg_atom = pkg_name;
let nif_dep = match dep_mode {
crate::e2e::config::DependencyMode::Local => {
format!(" {{:{pkg_atom}, path: \"{pkg_path}\"}}")
}
crate::e2e::config::DependencyMode::Registry => {
format!(" {{:{pkg_atom}, \"{pkg_path}\"}}")
}
};
deps.push(nif_dep);
deps.push(format!(
" {{:rustler_precompiled, \"{rp}\"}}",
rp = tv::hex::RUSTLER_PRECOMPILED
));
deps.push(format!(
" {{:rustler, \"{rustler}\", runtime: false}}",
rustler = tv::hex::RUSTLER
));
}
if has_http_tests || has_mock_server_tests || uses_harness {
deps.push(format!(" {{:finch, \"{finch}\"}}", finch = tv::hex::FINCH));
deps.push(format!(" {{:req, \"{req}\"}}", req = tv::hex::REQ));
deps.push(format!(" {{:jason, \"{jason}\"}}", jason = tv::hex::JASON));
}
let _ = writeln!(out, "{}", deps.join(",\n"));
let _ = writeln!(out, " ]");
let _ = writeln!(out, " end");
let _ = writeln!(out, "end");
out
}