use crate::e2e::config::{CallConfig, E2eConfig};
use crate::e2e::fixture::{Assertion, Fixture};
use super::test_function::{GoTestFunctionContext, render_test_function};
fn base_fixture(id: &str, assertions: Vec<Assertion>) -> Fixture {
Fixture {
docs: None,
requirements: Vec::new(),
id: id.to_string(),
category: None,
description: "test fixture".to_string(),
tags: vec![],
skip: None,
env: None,
setup: Vec::new(),
call: None,
input: serde_json::Value::Null,
mock_response: Some(crate::e2e::fixture::MockResponse {
status: 200,
body: Some(serde_json::Value::Null),
stream_chunks: None,
headers: std::collections::BTreeMap::new(),
}),
source: String::new(),
http: None,
asyncapi: None,
websocket: None,
preserve_input_urls: false,
assertions,
visitor: None,
args: vec![],
assertion_recipes: vec![],
}
}
fn render(fixture: &Fixture, optional_fields: &[&str]) -> String {
let e2e_config = E2eConfig {
call: CallConfig {
function: "extract".to_string(),
module: "github.com/example/mylib".to_string(),
result_var: "result".to_string(),
returns_result: true,
..CallConfig::default()
},
fields_optional: optional_fields.iter().map(|f| f.to_string()).collect(),
fields_array: std::iter::once("results".to_string()).collect(),
..E2eConfig::default()
};
let mut out = String::new();
let config = crate::core::config::ResolvedCrateConfig::default();
let type_defs: Vec<crate::core::ir::TypeDef> = Vec::new();
let enums: Vec<crate::core::ir::EnumDef> = Vec::new();
render_test_function(
&mut out,
fixture,
GoTestFunctionContext {
import_alias: "pkg",
e2e_config: &e2e_config,
adapters: &[],
data_enum_names: &std::collections::HashSet::new(),
config: &config,
type_defs: &type_defs,
enums: &enums,
errors: &[],
functions: &[],
},
);
out
}
#[test]
fn ancestor_guard_without_sibling_presence_fails_on_nil() {
let fixture = base_fixture(
"edge_metadata_title",
vec![
Assertion {
assertion_type: "not_error".to_string(),
..Default::default()
},
Assertion {
assertion_type: "equals".to_string(),
field: Some("results[0].metadata.title".to_string()),
value: Some(serde_json::Value::String("Simple Table Test".to_string())),
..Default::default()
},
],
);
let out = render(&fixture, &["results[0].metadata"]);
assert!(
out.contains("if result.Results[0].Metadata != nil {"),
"expected an ancestor guard on Results[0].Metadata; got:\n{out}"
);
assert!(
out.contains(
"} else {\n\t\tt.Errorf(\"expected %s to be present, got nil\", `result.Results[0].Metadata`)\n\t}"
),
"expected a failing else branch naming the nil ancestor; got:\n{out}"
);
}
#[test]
fn ancestor_guard_with_sibling_presence_check_stays_guard_only() {
let fixture = base_fixture(
"edge_metadata_title_with_not_empty",
vec![
Assertion {
assertion_type: "not_error".to_string(),
..Default::default()
},
Assertion {
assertion_type: "not_empty".to_string(),
field: Some("results[0].metadata".to_string()),
..Default::default()
},
Assertion {
assertion_type: "equals".to_string(),
field: Some("results[0].metadata.title".to_string()),
value: Some(serde_json::Value::String("Simple Table Test".to_string())),
..Default::default()
},
],
);
let out = render(&fixture, &["results[0].metadata"]);
assert!(
!out.contains("expected %s to be present, got nil"),
"sibling not_empty on the exact ancestor should suppress the failing else; got:\n{out}"
);
let expected_lines = [
"\tif result.Results[0].Metadata != nil {",
"\t\tif len(result.Results) == 0 {",
"\t\t\tt.Fatalf(\"expected non-empty %s\", `result.Results`)",
"\t\t}",
"\t\tif string(result.Results[0].Metadata.Title) != `Simple Table Test` {",
"\t\t\tt.Errorf(\"equals mismatch: got %v\", result.Results[0].Metadata.Title)",
"\t\t}",
"\t}",
];
let expected = expected_lines.join("\n");
assert!(
out.contains(expected.as_str()),
"expected the equals assertion to stay guard-only inside the ancestor wrap; got:\n{out}"
);
}
#[test]
fn nested_ancestor_and_field_guard_do_not_collide() {
let fixture = base_fixture(
"edge_metadata_score",
vec![
Assertion {
assertion_type: "not_error".to_string(),
..Default::default()
},
Assertion {
assertion_type: "greater_than_or_equal".to_string(),
field: Some("results[0].metadata.score".to_string()),
value: Some(serde_json::Value::from(0.5)),
..Default::default()
},
],
);
let out = render(&fixture, &["results[0].metadata", "results[0].metadata.score"]);
let expected_lines = [
"\tif result.Results[0].Metadata != nil {",
"\t\tif len(result.Results) == 0 {",
"\t\t\tt.Fatalf(\"expected non-empty %s\", `result.Results`)",
"\t\t}",
"\t\tif result.Results[0].Metadata.Score != nil {",
"\t\t\tif result.Results[0].Metadata.Score < 0.5 {",
"\t\t\t\tt.Errorf(\"expected >= 0.5, got %v\", result.Results[0].Metadata.Score)",
"\t\t\t}",
"\t\t}",
"\t} else {",
"\t\tt.Errorf(\"expected %s to be present, got nil\", `result.Results[0].Metadata`)",
"\t}",
];
let expected = expected_lines.join("\n");
assert!(
out.contains(expected.as_str()),
"expected the outer ancestor guard's failing else to nest around the leaf's own \
guard-only nullable check without a second else; got:\n{out}"
);
}