use alef::core::config::NewAlefConfig;
use alef::e2e::codegen::E2eCodegen;
use alef::e2e::codegen::elixir::ElixirCodegen;
use alef::e2e::fixture::{
Assertion, Fixture, FixtureDocs, FixtureDocsFileInput, FixtureDocsPresentation, FixtureGroup,
};
fn build_config_with_args(args_toml: &str) -> (alef::e2e::config::E2eConfig, alef::core::config::ResolvedCrateConfig) {
let toml_src = format!(
r#"
[workspace]
languages = ["elixir"]
[[crates]]
name = "mylib"
sources = ["src/lib.rs"]
[crates.e2e]
fixtures = "fixtures"
output = "e2e"
[crates.e2e.call]
function = "extract"
module = "MyLib"
result_var = "result"
returns_result = true
args = [
{args_toml}
]
"#
);
let cfg: NewAlefConfig = toml::from_str(&toml_src).expect("config parses");
let e2e = cfg.crates[0].e2e.clone().unwrap();
let resolved = cfg.resolve().expect("resolves").remove(0);
(e2e, resolved)
}
fn fixture_with_input(input: serde_json::Value) -> FixtureGroup {
FixtureGroup {
category: "test".to_string(),
fixtures: vec![Fixture {
docs: None,
requirements: Vec::new(),
id: "test_fixture".to_string(),
category: Some("test".to_string()),
description: "test fixture".to_string(),
tags: Vec::new(),
skip: None,
env: None,
setup: Vec::new(),
call: None,
input,
mock_response: None,
visitor: None,
args: Vec::new(),
assertion_recipes: Vec::new(),
assertions: vec![Assertion {
skip: None,
assertion_type: "not_empty".to_string(),
field: Some("output".to_string()),
value: None,
values: None,
method: None,
check: None,
args: None,
return_type: None,
}],
source: "test/test_fixture.json".to_string(),
http: None,
asyncapi: None,
websocket: None,
preserve_input_urls: false,
}],
}
}
fn fixture_with_docs_file(input: serde_json::Value, doc_field: &str, doc_path: &str) -> FixtureGroup {
FixtureGroup {
category: "test".to_string(),
fixtures: vec![Fixture {
docs: Some(FixtureDocs {
sample_url: None,
topic: "test".to_string(),
stem: None,
paths: Default::default(),
title: None,
description: None,
input: None,
shows: Vec::new(),
error: None,
presentation: Some(FixtureDocsPresentation {
call: None,
input: None,
args: None,
files: vec![FixtureDocsFileInput {
field: doc_field.to_string(),
path: doc_path.to_string(),
}],
operations: Vec::new(),
}),
client: None,
side_effects: Default::default(),
coverage_exceptions: Default::default(),
sample_url_vars: Default::default(),
body_file: None,
}),
requirements: Vec::new(),
id: "test_fixture".to_string(),
category: Some("test".to_string()),
description: "test fixture".to_string(),
tags: Vec::new(),
skip: None,
env: None,
setup: Vec::new(),
call: None,
input,
mock_response: None,
visitor: None,
args: Vec::new(),
assertion_recipes: Vec::new(),
assertions: vec![Assertion {
skip: None,
assertion_type: "not_empty".to_string(),
field: Some("output".to_string()),
value: None,
values: None,
method: None,
check: None,
args: None,
return_type: None,
}],
source: "test/test_fixture.json".to_string(),
http: None,
asyncapi: None,
websocket: None,
preserve_input_urls: false,
}],
}
}
fn generate_test_body(args_toml: &str, input: serde_json::Value) -> String {
generate_test_body_with_docs(args_toml, vec![fixture_with_input(input)])
}
fn generate_test_body_with_docs(args_toml: &str, groups: Vec<FixtureGroup>) -> String {
let (e2e, resolved) = build_config_with_args(args_toml);
let files = ElixirCodegen
.generate(&groups, &e2e, &resolved, &[], &[], &[], &[])
.expect("generation succeeds");
let test_file = files
.iter()
.find(|f| f.path.to_string_lossy().ends_with("test_test.exs"))
.expect("Elixir test file is emitted");
test_file.content.clone()
}
#[test]
fn docs_file_read_template_emits_integer_list_not_bare_binary() {
let args_toml = r#"
{ name = "config", field = "input.config", type = "json_object", element_type = "MyConfigType" }
"#;
let input = serde_json::json!({
"config": { "content": "placeholder" }
});
let groups = vec![fixture_with_docs_file(input, "/config/content", "text/plain.txt")];
let body = generate_test_body_with_docs(args_toml, groups);
assert!(
body.contains(r#"content: :binary.bin_to_list(File.read!("text/plain.txt"))"#),
"expected exact integer-list docs-file read, got:\n{body}"
);
assert!(
!body.contains(r#"content: File.read!("text/plain.txt")"#),
"found a bare File.read! from the docs_file_read.jinja path, unwrapped before the Jason.encode! hop:\n{body}"
);
}
#[test]
fn bytes_arg_file_path_emits_integer_list_not_bare_binary() {
let args_toml = r#"
{ name = "data", field = "input.data", type = "bytes" }
"#;
let input = serde_json::json!({
"data": "docs/sample.bin"
});
let body = generate_test_body(args_toml, input);
assert!(
body.contains(r#"data = :binary.bin_to_list(File.read!("../../test_documents/docs/sample.bin"))"#),
"expected exact integer-list file read, got:\n{body}"
);
assert!(
!body.contains("data = File.read!("),
"found a bare File.read! assignment that skips the byte-integer conversion:\n{body}"
);
}
#[test]
fn bytes_arg_base64_emits_integer_list_not_bare_binary() {
let args_toml = r#"
{ name = "data", field = "input.data", type = "bytes" }
"#;
let input = serde_json::json!({
"data": "xyzAMQ"
});
let body = generate_test_body(args_toml, input);
assert!(
body.contains(r#"data = :binary.bin_to_list(Base.decode64!("xyzAMQ", padding: false))"#),
"expected exact integer-list base64 decode, got:\n{body}"
);
assert!(
!body.contains("data = Base.decode64!("),
"found a bare Base.decode64! assignment that skips the byte-integer conversion:\n{body}"
);
}