use super::render_snippet_body;
use crate::core::config::ResolvedCrateConfig;
use crate::core::config::extras::{AdapterConfig, AdapterPattern};
use crate::e2e::config::{CallConfig, CallOverride, E2eConfig};
use crate::e2e::fixture::Fixture;
fn line_containing<'a>(body: &'a str, needle: &str) -> &'a str {
body.lines()
.find(|line| line.contains(needle))
.unwrap_or_else(|| panic!("no line contains {needle} in:\n{body}"))
}
fn engine_handle_arg() -> crate::e2e::config::ArgMapping {
crate::e2e::config::ArgMapping {
name: "engine".into(),
field: "input.engine".into(),
arg_type: "handle".into(),
optional: false,
owned: false,
element_type: None,
go_type: None,
vec_inner_is_ref: false,
trait_name: None,
}
}
fn url_arg() -> crate::e2e::config::ArgMapping {
crate::e2e::config::ArgMapping {
name: "url".into(),
field: "input.url".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 urls_arg() -> crate::e2e::config::ArgMapping {
crate::e2e::config::ArgMapping {
name: "urls".into(),
field: "input.urls".into(),
arg_type: "mock_url_list".into(),
optional: false,
owned: false,
element_type: None,
go_type: None,
vec_inner_is_ref: false,
trait_name: None,
}
}
fn streaming_owner_adapter(name: &str, request_type: &str) -> AdapterConfig {
AdapterConfig {
name: name.to_string(),
pattern: AdapterPattern::Streaming,
core_path: format!("sample::{name}"),
params: Vec::new(),
returns: None,
error_type: None,
owner_type: Some("SampleEngine".to_string()),
item_type: Some("sample::SampleEvent".to_string()),
gil_release: false,
trait_name: None,
trait_method: None,
detect_async: false,
request_type: Some(request_type.to_string()),
skip_languages: Vec::new(),
}
}
#[test]
fn a_single_streaming_call_passes_the_typed_request_not_the_flat_url() {
let fixture = Fixture {
id: "stream_a_document".into(),
description: "Stream a document".into(),
input: serde_json::json!({"engine": {}, "url": "https://example.com/doc"}),
preserve_input_urls: true,
..Fixture::default()
};
let mut call = CallConfig {
function: "stream_events".into(),
result_var: "events".into(),
args: vec![engine_handle_arg(), url_arg()],
..CallConfig::default()
};
call.overrides.insert(
"java".into(),
CallOverride {
class: Some("Sample".into()),
..CallOverride::default()
},
);
let config = ResolvedCrateConfig {
name: "sample".into(),
adapters: vec![streaming_owner_adapter("stream_events", "sample::SampleStreamRequest")],
..ResolvedCrateConfig::default()
};
let body = render_snippet_body(
&fixture,
&E2eConfig {
call,
..E2eConfig::default()
},
&config,
&[],
);
assert_eq!(
line_containing(&body, "String url ="),
" String url = \"https://example.com/doc\";"
);
assert_eq!(
line_containing(&body, "urlReq ="),
" var urlReq = new SampleStreamRequest(url);"
);
assert_eq!(
line_containing(&body, "streamEvents("),
" var events = engine.streamEvents(urlReq);"
);
assert!(
!body.contains("Sample.streamEvents("),
"must not call a static overload the Java facade never declares:\n{body}"
);
assert!(
!body.contains("streamEvents(engine, url)"),
"must not pass the raw handle and URL positionally:\n{body}"
);
}
#[test]
fn a_batch_streaming_call_wraps_the_url_list_in_the_typed_batch_request() {
let fixture = Fixture {
id: "stream_several_documents".into(),
description: "Stream several documents".into(),
input: serde_json::json!({
"engine": {},
"urls": ["https://a.example.com/doc", "https://b.example.com/doc"]
}),
preserve_input_urls: true,
..Fixture::default()
};
let mut call = CallConfig {
function: "stream_events_batch".into(),
result_var: "events".into(),
args: vec![engine_handle_arg(), urls_arg()],
..CallConfig::default()
};
call.overrides.insert(
"java".into(),
CallOverride {
class: Some("Sample".into()),
..CallOverride::default()
},
);
let config = ResolvedCrateConfig {
name: "sample".into(),
adapters: vec![streaming_owner_adapter(
"stream_events_batch",
"sample::SampleBatchStreamRequest",
)],
..ResolvedCrateConfig::default()
};
let body = render_snippet_body(
&fixture,
&E2eConfig {
call,
..E2eConfig::default()
},
&config,
&[],
);
assert_eq!(
line_containing(&body, "java.util.List<String> urls ="),
" java.util.List<String> urls = java.util.List.of(\"https://a.example.com/doc\", \
\"https://b.example.com/doc\");"
);
assert_eq!(
line_containing(&body, "urlsReq ="),
" var urlsReq = new SampleBatchStreamRequest(urls);"
);
assert_eq!(
line_containing(&body, "streamEventsBatch("),
" var events = engine.streamEventsBatch(urlsReq);"
);
assert!(
!body.contains("java.util.List.of(\"https://a.example.com/doc\", \"https://b.example.com/doc\"))"),
"the raw URL list must not be passed positionally to the call itself:\n{body}"
);
}
#[test]
fn a_non_streaming_call_with_no_matching_adapter_keeps_the_flat_mock_url_shape() {
let fixture = Fixture {
id: "scrape_a_page".into(),
description: "Scrape a page".into(),
input: serde_json::json!({"url": "https://example.com/doc"}),
preserve_input_urls: true,
..Fixture::default()
};
let mut call = CallConfig {
function: "scrape".into(),
result_var: "result".into(),
args: vec![url_arg()],
..CallConfig::default()
};
call.overrides.insert(
"java".into(),
CallOverride {
class: Some("Sample".into()),
..CallOverride::default()
},
);
let config = ResolvedCrateConfig {
name: "sample".into(),
adapters: vec![streaming_owner_adapter("stream_events", "sample::SampleStreamRequest")],
..ResolvedCrateConfig::default()
};
let body = render_snippet_body(
&fixture,
&E2eConfig {
call,
..E2eConfig::default()
},
&config,
&[],
);
assert_eq!(
line_containing(&body, "scrape("),
" var result = Sample.scrape(url);"
);
assert!(
!body.contains("Req = new"),
"a call with no matching adapter must not wrap its mock_url arg in a request DTO:\n{body}"
);
}