use alef::core::config::NewAlefConfig;
use alef::e2e::codegen::E2eCodegen;
use alef::e2e::codegen::zig::ZigE2eCodegen;
use alef::e2e::fixture::{Assertion, Fixture, FixtureGroup, MockResponse};
use serde_json::json;
fn config_toml() -> &'static str {
r#"
[workspace]
languages = ["zig"]
[[crates]]
name = "kreuzcrawl"
sources = ["src/lib.rs"]
[crates.e2e]
fixtures = "fixtures"
output = "e2e"
[crates.e2e.call]
function = "scrape"
module = "kreuzcrawl"
result_var = "result"
returns_result = true
args = [{ name = "url", field = "url", type = "string" }]
"#
}
fn render_with_fixture(fixture: Fixture) -> String {
let cfg: NewAlefConfig = toml::from_str(config_toml()).expect("config parses");
let resolved = cfg.clone().resolve().expect("config resolves").remove(0);
let e2e = cfg.crates[0].e2e.clone().expect("e2e config present");
let groups = vec![FixtureGroup {
category: "error_handling".to_string(),
fixtures: vec![fixture],
}];
let files = ZigE2eCodegen
.generate(&groups, &e2e, &resolved, &[], &[])
.expect("generation succeeds");
files
.iter()
.find(|f| f.path.to_string_lossy().contains("error_handling_test.zig"))
.expect("error_handling_test.zig is emitted")
.content
.clone()
}
#[test]
fn greater_than_negative_one_is_skipped() {
let fixture = Fixture {
id: "links_greater_than_negative".to_string(),
category: Some("error_handling".to_string()),
description: "Links count > -1 (sentinel, always true)".to_string(),
tags: Vec::new(),
skip: None,
env: None,
setup: Vec::new(),
call: None,
input: json!({
"url": "http://example.com",
"config": { "html": "<a href='/'>Home</a>" }
}),
mock_response: Some(MockResponse {
status: 200,
body: Some(json!({
"object": {
"links": [
{ "text": "Home", "href": "/" }
]
}
})),
stream_chunks: None,
headers: Default::default(),
}),
visitor: None,
args: Vec::new(),
assertion_recipes: Vec::new(),
assertions: vec![Assertion {
assertion_type: "greater_than".to_string(),
field: Some("object.links.length".to_string()),
value: Some(json!(-1)),
values: None,
method: None,
check: None,
args: None,
return_type: None,
}],
source: "error_handling.json".to_string(),
http: None,
};
let rendered = render_with_fixture(fixture);
assert!(
!rendered.contains("@as(usize, -1)"),
"greater_than: -1 must not emit @as(usize, -1). Rendered:\n{rendered}"
);
assert!(
!rendered.contains("object.links.length > @as("),
"greater_than: -1 must not emit any comparator assertion. Rendered:\n{rendered}"
);
}
#[test]
fn greater_than_or_equal_negative_one_is_skipped() {
let fixture = Fixture {
id: "items_gte_negative".to_string(),
category: Some("error_handling".to_string()),
description: "Items count >= -1 (sentinel, always true)".to_string(),
tags: Vec::new(),
skip: None,
env: None,
setup: Vec::new(),
call: None,
input: json!({
"url": "http://example.com"
}),
mock_response: Some(MockResponse {
status: 200,
body: Some(json!({
"array": [1, 2, 3]
})),
stream_chunks: None,
headers: Default::default(),
}),
visitor: None,
args: Vec::new(),
assertion_recipes: Vec::new(),
assertions: vec![Assertion {
assertion_type: "greater_than_or_equal".to_string(),
field: Some("array.length".to_string()),
value: Some(json!(-1)),
values: None,
method: None,
check: None,
args: None,
return_type: None,
}],
source: "error_handling.json".to_string(),
http: None,
};
let rendered = render_with_fixture(fixture);
assert!(
!rendered.contains("@as(usize, -1)"),
"greater_than_or_equal: -1 must not emit @as(usize, -1). Rendered:\n{rendered}"
);
assert!(
!rendered.contains("array.length >= @as("),
"greater_than_or_equal: -1 must not emit any comparator assertion. Rendered:\n{rendered}"
);
}
#[test]
fn greater_than_positive_value_is_emitted() {
let fixture = Fixture {
id: "links_greater_than_zero".to_string(),
category: Some("error_handling".to_string()),
description: "Links count > 0 (valid, not a sentinel)".to_string(),
tags: Vec::new(),
skip: None,
env: None,
setup: Vec::new(),
call: None,
input: json!({
"url": "http://example.com"
}),
mock_response: Some(MockResponse {
status: 200,
body: Some(json!({
"object": {
"links": [
{ "text": "Home", "href": "/" }
]
}
})),
stream_chunks: None,
headers: Default::default(),
}),
visitor: None,
args: Vec::new(),
assertion_recipes: Vec::new(),
assertions: vec![Assertion {
assertion_type: "greater_than".to_string(),
field: Some("object.links.length".to_string()),
value: Some(json!(0)),
values: None,
method: None,
check: None,
args: None,
return_type: None,
}],
source: "error_handling.json".to_string(),
http: None,
};
let rendered = render_with_fixture(fixture);
assert!(
rendered.contains("object.links.len > 0"),
"greater_than: 0 must emit a valid assertion. Rendered:\n{rendered}"
);
}
#[test]
fn less_than_negative_value_is_emitted() {
let fixture = Fixture {
id: "count_less_than_negative".to_string(),
category: Some("error_handling".to_string()),
description: "Count < -1 (edge case, must emit)".to_string(),
tags: Vec::new(),
skip: None,
env: None,
setup: Vec::new(),
call: None,
input: json!({
"url": "http://example.com"
}),
mock_response: Some(MockResponse {
status: 200,
body: Some(json!({
"count": -5
})),
stream_chunks: None,
headers: Default::default(),
}),
visitor: None,
args: Vec::new(),
assertion_recipes: Vec::new(),
assertions: vec![Assertion {
assertion_type: "less_than".to_string(),
field: Some("count".to_string()),
value: Some(json!(-1)),
values: None,
method: None,
check: None,
args: None,
return_type: None,
}],
source: "error_handling.json".to_string(),
http: None,
};
let rendered = render_with_fixture(fixture);
assert!(
rendered.contains("result.len < -1"),
"less_than: -1 must emit assertion. Rendered:\n{rendered}"
);
}