alef 0.67.4

Opinionated polyglot binding generator for Rust libraries
Documentation
//! Docs-snippet rendering for streaming adapters declared with an `owner_type`.

use super::test_support::line_containing;
use super::*;
use crate::e2e::config::CallConfig;

/// Synthetic streaming `owner_type` adapter whose facade signature takes exactly
/// the declared `request` param, matching the shape declared by
/// `[[crates.adapters]]` in `alef.toml`. Mirrors
/// `test_method::tests::streaming_owner_adapter`.
fn streaming_owner_adapter(function_name: &str, owner_type: &str) -> crate::core::config::extras::AdapterConfig {
    crate::core::config::extras::AdapterConfig {
        name: function_name.to_string(),
        pattern: crate::core::config::extras::AdapterPattern::Streaming,
        core_path: format!("test_core::{function_name}"),
        params: vec![crate::core::config::extras::AdapterParam {
            name: "request".to_string(),
            ty: "sample::StreamRequest".to_string(),
            optional: false,
        }],
        returns: None,
        error_type: None,
        owner_type: Some(owner_type.to_string()),
        item_type: Some("Item".to_string()),
        gil_release: false,
        trait_name: None,
        trait_method: None,
        detect_async: false,
        request_type: None,
        skip_languages: Vec::new(),
    }
}

fn streaming_owner_call() -> CallConfig {
    CallConfig {
        function: "stream_items".into(),
        result_var: "result".into(),
        args: vec![
            crate::e2e::config::ArgMapping {
                name: "handle".into(),
                field: "input.handle".into(),
                arg_type: "handle".into(),
                optional: false,
                owned: false,
                element_type: None,
                go_type: None,
                vec_inner_is_ref: false,
                trait_name: None,
            },
            crate::e2e::config::ArgMapping {
                name: "url".into(),
                field: "input.url".into(),
                arg_type: "string".into(),
                optional: false,
                owned: false,
                element_type: None,
                go_type: None,
                vec_inner_is_ref: false,
                trait_name: None,
            },
        ],
        ..CallConfig::default()
    }
}

fn streaming_owner_fixture() -> Fixture {
    Fixture {
        id: "stream_basic".into(),
        description: "Stream items".into(),
        input: serde_json::json!({ "handle": {}, "url": "https://example.com" }),
        ..Fixture::default()
    }
}

/// Regression for #149: an owner_type streaming adapter's declared request
/// param (`[[crates.adapters.params]]`) must be built from the fixture input
/// and bound to a local `val request = ...` before the call references it —
/// mirroring `test_method.rs`'s identical construction for the generated
/// JUnit test. Before this fix `render_snippet_body` never resolved the
/// adapter's `streaming_request` at all: the handle-typed arg was still
/// treated as the call receiver (so it never reached `args`), but nothing
/// rebuilt or bound the request in its place, leaving the call either
/// missing its required argument or passing an un-rebuilt raw value instead
/// of the declared `request` identifier this test pins.
#[test]
fn kotlin_snippet_binds_the_declared_request_before_the_call() {
    let config = ResolvedCrateConfig {
        name: "sample".into(),
        adapters: vec![streaming_owner_adapter("stream_items", "Engine")],
        ..ResolvedCrateConfig::default()
    };
    let body = render_snippet_body(
        &streaming_owner_fixture(),
        &E2eConfig {
            call: streaming_owner_call(),
            ..E2eConfig::default()
        },
        &config,
        &[],
        &[],
        false,
    )
    .expect("snippet renders");

    assert_eq!(
        line_containing(&body, "val request ="),
        r#"val request = mapper.readValue("{\"url\":\"https://example.com\"}", StreamRequest::class.java)"#
    );
    assert_eq!(
        line_containing(&body, "val result ="),
        "val result = handle.streamItems(request)"
    );
    assert!(
        !body.contains("\\\"handle\\\""),
        "owner handle config must not leak into the request JSON:\n{body}"
    );
    assert!(
        !body.contains("streamItems(\"https://example.com\")"),
        "the raw url must not be passed positionally in place of the declared request:\n{body}"
    );
}

#[test]
fn kotlin_android_snippet_binds_the_declared_request_before_the_call() {
    let config = ResolvedCrateConfig {
        name: "sample".into(),
        adapters: vec![streaming_owner_adapter("stream_items", "Engine")],
        ..ResolvedCrateConfig::default()
    };
    let body = render_snippet_body(
        &streaming_owner_fixture(),
        &E2eConfig {
            call: streaming_owner_call(),
            ..E2eConfig::default()
        },
        &config,
        &[],
        &[],
        true,
    )
    .expect("snippet renders");

    assert_eq!(
        line_containing(&body, "val request ="),
        r#"val request = mapper.readValue("{\"url\":\"https://example.com\"}", StreamRequest::class.java)"#
    );
    assert_eq!(
        line_containing(&body, "val result ="),
        "val result = handle.streamItems(request)"
    );
    assert!(
        !body.contains("\\\"handle\\\""),
        "owner handle config must not leak into the request JSON:\n{body}"
    );
}