use crate::core::config::e2e::sample_url::has_url_scheme;
use crate::core::config::e2e::{CallConfig, DocsSampleBaseUrl};
use crate::e2e::fixture::Fixture;
enum DeclaredValue {
Undeclared,
HarnessRelative(serde_json::Value),
AlreadyMeaningful,
}
fn scalar_declared_value(
input: &serde_json::Value,
field: &str,
sample_base_url: DocsSampleBaseUrl<'_>,
) -> DeclaredValue {
let Some(value) = crate::e2e::codegen::resolve_field(input, field).as_str() else {
return DeclaredValue::Undeclared;
};
if has_url_scheme(value) {
DeclaredValue::AlreadyMeaningful
} else {
DeclaredValue::HarnessRelative(serde_json::Value::String(sample_base_url.join(value)))
}
}
fn list_declared_value(
input: &serde_json::Value,
field: &str,
sample_base_url: DocsSampleBaseUrl<'_>,
) -> DeclaredValue {
let Some(elements) = crate::e2e::codegen::resolve_urls_field(input, field).as_array() else {
return DeclaredValue::Undeclared;
};
let Some(urls) = elements
.iter()
.map(serde_json::Value::as_str)
.collect::<Option<Vec<_>>>()
else {
return DeclaredValue::AlreadyMeaningful;
};
if !urls.is_empty() && urls.iter().all(|url| has_url_scheme(url)) {
return DeclaredValue::AlreadyMeaningful;
}
let rewritten = urls
.into_iter()
.map(|url| {
if has_url_scheme(url) {
serde_json::Value::String(url.to_string())
} else {
serde_json::Value::String(sample_base_url.join(url))
}
})
.collect();
DeclaredValue::HarnessRelative(serde_json::Value::Array(rewritten))
}
pub(super) fn with_default_mock_url_literals(
mut fixture: Fixture,
call: &CallConfig,
sample_base_url: DocsSampleBaseUrl<'_>,
) -> Fixture {
let candidates: Vec<(String, bool)> = fixture
.resolved_args(call)
.iter()
.filter_map(|arg| match arg.arg_type.as_str() {
"mock_url" => Some((arg.field.clone(), false)),
"mock_url_list" => Some((arg.field.clone(), true)),
_ => None,
})
.collect();
let mut injected_any = false;
for (field, is_list) in candidates {
let declared = if is_list {
list_declared_value(&fixture.input, &field, sample_base_url)
} else {
scalar_declared_value(&fixture.input, &field, sample_base_url)
};
let value = match declared {
DeclaredValue::Undeclared => {
if is_list {
serde_json::json!([sample_base_url.base()])
} else {
serde_json::Value::String(sample_base_url.base().to_string())
}
}
DeclaredValue::HarnessRelative(rewritten) => rewritten,
DeclaredValue::AlreadyMeaningful => {
if !fixture.preserve_input_urls {
tracing::warn!(
target: "alef::e2e::snippets::mock_url_defaults",
fixture = %fixture.id,
field = %field,
"fixture declares an absolute URL for a mock_url/mock_url_list argument \
without setting preserve_input_urls; the literal is discarded and the \
mock-server address is bound instead, in both the executable e2e suite \
and this fixture's documentation snippet -- set preserve_input_urls = \
true if the URL is meaningful to the test, or remove the declared value \
to let the zero-edit default stand in"
);
}
continue;
}
};
if set_input_field(&mut fixture.input, &field, value) {
injected_any = true;
}
}
if injected_any {
fixture.preserve_input_urls = true;
}
fixture
}
fn set_input_field(input: &mut serde_json::Value, field_path: &str, value: serde_json::Value) -> bool {
let path = field_path.strip_prefix("input.").unwrap_or(field_path);
if path.is_empty() || path == "input" {
return false;
}
let mut segments = path.split('.').peekable();
let mut current = input;
while let Some(segment) = segments.next() {
if !current.is_object() {
*current = serde_json::Value::Object(serde_json::Map::new());
}
let Some(map) = current.as_object_mut() else {
return false;
};
if segments.peek().is_none() {
map.insert(segment.to_string(), value);
return true;
}
current = map
.entry(segment.to_string())
.or_insert_with(|| serde_json::Value::Object(serde_json::Map::new()));
}
false
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::config::e2e::ArgMapping;
fn mock_url_arg(field: &str) -> ArgMapping {
ArgMapping {
name: "url".into(),
field: field.into(),
arg_type: "mock_url".into(),
optional: false,
owned: false,
element_type: None,
go_type: None,
vec_inner_is_ref: false,
trait_name: None,
}
}
fn mock_url_list_arg(field: &str) -> ArgMapping {
ArgMapping {
arg_type: "mock_url_list".into(),
..mock_url_arg(field)
}
}
fn placeholder_base() -> DocsSampleBaseUrl<'static> {
DocsSampleBaseUrl::resolve(None).expect("an unconfigured base always resolves")
}
fn apply_defaults(fixture: Fixture, call: &CallConfig) -> Fixture {
with_default_mock_url_literals(fixture, call, placeholder_base())
}
fn call_with_args(args: Vec<ArgMapping>) -> CallConfig {
CallConfig {
args,
..CallConfig::default()
}
}
#[test]
fn injects_the_default_url_when_the_fixture_declares_none() {
let fixture = Fixture {
input: serde_json::json!({}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_arg("url")]);
let result = apply_defaults(fixture, &call);
assert_eq!(
result.input.get("url").and_then(|v| v.as_str()),
Some(placeholder_base().base())
);
assert!(
result.preserve_input_urls,
"injecting a literal must opt the fixture in"
);
}
#[test]
fn leaves_an_already_declared_absolute_url_untouched() {
let fixture = Fixture {
input: serde_json::json!({"url": "http://127.0.0.1:9/"}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_arg("url")]);
let result = apply_defaults(fixture, &call);
assert_eq!(
result.input.get("url").and_then(|v| v.as_str()),
Some("http://127.0.0.1:9/")
);
assert!(
!result.preserve_input_urls,
"a scheme-carrying literal is left for the fixture author to opt in explicitly"
);
}
#[test]
fn rewrites_a_declared_relative_scalar_url_to_the_default_base() {
let fixture = Fixture {
input: serde_json::json!({"url": "/seed1"}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_arg("url")]);
let result = apply_defaults(fixture, &call);
assert_eq!(
result.input.get("url").and_then(|v| v.as_str()),
Some("https://example.com/seed1")
);
assert!(result.preserve_input_urls);
}
#[test]
fn rewrites_every_relative_element_of_a_declared_url_list() {
let fixture = Fixture {
input: serde_json::json!({"urls": ["/seed1", "/seed2"]}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_list_arg("urls")]);
let result = apply_defaults(fixture, &call);
assert_eq!(
result.input.get("urls").and_then(|v| v.as_array()),
Some(&vec![
serde_json::Value::String("https://example.com/seed1".to_string()),
serde_json::Value::String("https://example.com/seed2".to_string()),
])
);
assert!(result.preserve_input_urls);
}
#[test]
fn a_mixed_url_list_only_rewrites_its_relative_elements() {
let fixture = Fixture {
input: serde_json::json!({"urls": ["/seed1", "http://127.0.0.1:9/seed2"]}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_list_arg("urls")]);
let result = apply_defaults(fixture, &call);
assert_eq!(
result.input.get("urls").and_then(|v| v.as_array()),
Some(&vec![
serde_json::Value::String("https://example.com/seed1".to_string()),
serde_json::Value::String("http://127.0.0.1:9/seed2".to_string()),
])
);
assert!(result.preserve_input_urls);
}
#[test]
fn leaves_an_already_declared_absolute_url_list_untouched() {
let fixture = Fixture {
input: serde_json::json!({"urls": ["http://127.0.0.1:9/a", "gopher://127.0.0.1:9/b"]}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_list_arg("urls")]);
let result = apply_defaults(fixture, &call);
assert_eq!(
result.input.get("urls").and_then(|v| v.as_array()),
Some(&vec![
serde_json::Value::String("http://127.0.0.1:9/a".to_string()),
serde_json::Value::String("gopher://127.0.0.1:9/b".to_string()),
])
);
assert!(!result.preserve_input_urls);
}
#[test]
fn a_declared_empty_url_list_is_marked_preserved() {
let fixture = Fixture {
input: serde_json::json!({"urls": []}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_list_arg("urls")]);
let result = apply_defaults(fixture, &call);
assert_eq!(result.input.get("urls").and_then(|v| v.as_array()), Some(&vec![]));
assert!(result.preserve_input_urls);
}
#[test]
#[tracing_test::traced_test]
fn warns_when_a_meaningful_url_is_declared_without_preserve_input_urls() {
let fixture = Fixture {
id: "validation_probe".into(),
input: serde_json::json!({"url": "http://127.0.0.1:9/"}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_arg("url")]);
apply_defaults(fixture, &call);
assert!(
logs_contain("without setting preserve_input_urls"),
"expected a warning naming the missing preserve_input_urls opt-in"
);
}
#[test]
#[tracing_test::traced_test]
fn does_not_warn_once_preserve_input_urls_is_set() {
let fixture = Fixture {
id: "validation_probe".into(),
preserve_input_urls: true,
input: serde_json::json!({"url": "http://127.0.0.1:9/"}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_arg("url")]);
apply_defaults(fixture, &call);
assert!(
!logs_contain("without setting preserve_input_urls"),
"a fixture that already opted in must not be flagged"
);
}
#[test]
fn injects_a_single_element_default_list_for_mock_url_list() {
let fixture = Fixture {
input: serde_json::json!({}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_list_arg("urls")]);
let result = apply_defaults(fixture, &call);
assert_eq!(
result.input.get("urls").and_then(|v| v.as_array()),
Some(&vec![serde_json::Value::String(placeholder_base().base().to_string())])
);
assert!(result.preserve_input_urls);
}
#[test]
fn a_configured_sample_base_url_replaces_the_placeholder_for_an_undeclared_argument() {
let fixture = Fixture {
input: serde_json::json!({}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_arg("url")]);
let base = DocsSampleBaseUrl::resolve(Some("https://samples.example.org")).expect("valid base");
let result = with_default_mock_url_literals(fixture, &call, base);
assert_eq!(
result.input.get("url").and_then(|v| v.as_str()),
Some("https://samples.example.org")
);
assert!(result.preserve_input_urls);
}
#[test]
fn a_configured_sample_base_url_is_the_base_declared_relative_paths_resolve_against() {
let fixture = Fixture {
input: serde_json::json!({"urls": ["/pdf/report.pdf", "/pdf/memo.pdf"]}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_list_arg("urls")]);
let base = DocsSampleBaseUrl::resolve(Some("https://samples.example.org/")).expect("valid base");
let result = with_default_mock_url_literals(fixture, &call, base);
assert_eq!(
result.input.get("urls").and_then(|v| v.as_array()),
Some(&vec![
serde_json::Value::String("https://samples.example.org/pdf/report.pdf".to_string()),
serde_json::Value::String("https://samples.example.org/pdf/memo.pdf".to_string()),
])
);
assert!(result.preserve_input_urls);
}
#[test]
fn a_declared_absolute_url_outranks_the_configured_sample_base_url() {
let fixture = Fixture {
preserve_input_urls: true,
input: serde_json::json!({"url": "https://docs.example.net/report.pdf"}),
..Fixture::default()
};
let call = call_with_args(vec![mock_url_arg("url")]);
let base = DocsSampleBaseUrl::resolve(Some("https://samples.example.org")).expect("valid base");
let result = with_default_mock_url_literals(fixture, &call, base);
assert_eq!(
result.input.get("url").and_then(|v| v.as_str()),
Some("https://docs.example.net/report.pdf"),
"an absolute declared URL is the fixture author's choice and stays verbatim"
);
}
#[test]
fn a_fixture_with_no_mock_url_arg_is_left_unmarked() {
let fixture = Fixture {
input: serde_json::json!({"text": "sample"}),
..Fixture::default()
};
let call = call_with_args(vec![ArgMapping {
name: "text".into(),
field: "text".into(),
arg_type: "string".into(),
optional: false,
owned: false,
element_type: None,
go_type: None,
vec_inner_is_ref: false,
trait_name: None,
}]);
let result = apply_defaults(fixture, &call);
assert!(!result.preserve_input_urls);
assert!(result.input.get("url").is_none());
}
#[test]
fn a_declared_relative_batch_url_list_survives_the_full_snippet_pipeline() {
use crate::core::config::NewAlefConfig;
use crate::e2e::codegen::E2eCodegen;
use crate::e2e::codegen::python::PythonE2eCodegen;
let cfg_str = r#"
[workspace]
languages = ["python"]
[[crates]]
name = "example-core"
sources = ["src/lib.rs"]
[crates.e2e]
fixtures = "fixtures"
[crates.e2e.call]
function = "batch_scrape"
module = "example_api"
args = [{ name = "urls", field = "batch_urls", type = "mock_url_list" }]
"#;
let cfg: NewAlefConfig = toml::from_str(cfg_str).expect("config parses");
let e2e = cfg.crates[0].e2e.clone().expect("e2e config");
let resolved = cfg.resolve().expect("config resolves").remove(0);
let fixture = Fixture {
id: "batch_crawl_basic".into(),
input: serde_json::json!({"batch_urls": ["/seed1", "/seed2"]}),
..Fixture::default()
};
let docs_fixture = fixture.docs_call_fixture();
let call = e2e.resolve_call_for_fixture(
docs_fixture.call.as_deref(),
&docs_fixture.id,
&docs_fixture.resolved_category(),
&docs_fixture.tags,
&docs_fixture.input,
);
let docs_fixture = with_default_mock_url_literals(docs_fixture, call, placeholder_base());
assert_eq!(
docs_fixture.input.get("batch_urls").and_then(|v| v.as_array()),
Some(&vec![
serde_json::Value::String("https://example.com/seed1".to_string()),
serde_json::Value::String("https://example.com/seed2".to_string()),
]),
"the module must rewrite the declared relative paths before codegen ever sees them"
);
assert!(docs_fixture.preserve_input_urls);
let body = PythonE2eCodegen
.render_snippet_body(&docs_fixture, &e2e, &resolved, &[], &[])
.expect("python snippet body renders");
assert!(
!body.contains("MOCK_SERVER"),
"rendered snippet body must carry no mock-harness wiring:\n{body}"
);
crate::e2e::snippets::mock_harness_guard::reject_mock_harness_scaffolding(&body, &docs_fixture, "python")
.expect("the harness-leak guard must accept a fully preserved, rewritten snippet body");
}
}