openapi-nexus 0.2.2

OpenAPI 3.x multi-language code generator
Documentation
//! Smoke test: `sigil_emit_api::generate_api_files` must produce one API class
//! file per tag for the petstore fixture and each file must contain structural
//! fragments (imports, ApiInterface, ApiClass, at least one Raw + convenience
//! method pair).

use std::fs;
use std::path::Path;

use openapi_nexus::generators::request_inputs::RequestInputPlan;
use openapi_nexus::generators::typescript::fetch::sigil_emit_api::generate_api_files;
use sigil_stitch::lang::typescript::TypeScript;

fn read_fixture(rel: &str) -> String {
    for base in [
        "tests/fixtures",
        "../tests/fixtures",
        "../../tests/fixtures",
        "../../../tests/fixtures",
    ] {
        let p = Path::new(base).join(rel);
        if p.exists() {
            return fs::read_to_string(p).unwrap();
        }
    }
    panic!("fixture not found: {rel}");
}

#[test]
fn petstore_api_scaffold_emits_one_file_per_tag() {
    let yaml = read_fixture("valid/petstore.yaml");
    let parsed = openapi_nexus::parser::parse_content_yaml(&yaml).unwrap();
    let ir = openapi_nexus::ir::lower::lower(parsed).unwrap();

    let request_inputs = RequestInputPlan::empty();
    let files = generate_api_files(&ir, false, &request_inputs, &TypeScript::new())
        .expect("scaffold renders");
    assert!(
        !files.is_empty(),
        "expected at least one API file for petstore"
    );

    let pet_api = files
        .iter()
        .find(|f| f.filename.ends_with("PetApi.ts"))
        .expect("PetApi.ts must exist for petstore");

    // Every API file has a header + class + interface.
    let required_fragments = [
        "@generated by openapi-nexus",
        "export interface PetApiInterface",
        "export class PetApi",
        // Constructor delegates to BaseAPI with DefaultConfig fallback.
        "super(configuration ?? DefaultConfig)",
        // At least one async Raw method + convenience wrapper for petstore.
        "Raw(",
        "await this.",
        "return await response.value();",
    ];
    for frag in required_fragments {
        assert!(
            pet_api.content.contains(frag),
            "expected `{frag}` in PetApi.ts:\n{}",
            pet_api.content
        );
    }

    // Handy for eyeballing the shape with `cargo test … -- --nocapture`.
    eprintln!(
        "--- PetApi.ts (scaffold) ---\n{}\n--- end ---",
        pet_api.content
    );
}

#[test]
fn minimal_api_scaffold_has_no_request_interface_when_op_has_no_params() {
    let yaml = read_fixture("valid/minimal.yaml");
    let parsed = openapi_nexus::parser::parse_content_yaml(&yaml).unwrap();
    let ir = openapi_nexus::ir::lower::lower(parsed).unwrap();

    let request_inputs = RequestInputPlan::empty();
    let files = generate_api_files(&ir, false, &request_inputs, &TypeScript::new())
        .expect("scaffold renders");
    let default_api = files
        .iter()
        .find(|f| f.filename.ends_with("DefaultApi.ts"))
        .expect("DefaultApi.ts must exist for minimal");

    // Minimal's `getTest` op has no parameters → no `ApiGetTestRequest` shim.
    assert!(
        !default_api.content.contains("ApiGetTestRequest"),
        "op without params should skip request interface:\n{}",
        default_api.content
    );
    // initOverrides is still present as the sole parameter.
    assert!(
        default_api.content.contains("initOverrides?"),
        "initOverrides? must be on every method:\n{}",
        default_api.content
    );
}