use oag_core::config::{GeneratorConfig, OutputLayout};
use oag_core::engine::generate;
use oag_core::engine::pack::TemplatePack;
use oag_core::{parse, transform};
const LITERAL_DEFAULT: &str = include_str!("fixtures/literal-default.yaml");
fn config_for(output: &str) -> GeneratorConfig {
GeneratorConfig {
output: output.to_string(),
layout: OutputLayout::Modular,
split_by: None,
base_url: None,
source_dir: ".".to_string(),
scaffold: None,
no_jsdoc: None,
}
}
fn render(pack_rel: &str) -> Vec<oag_core::GeneratedFile> {
let spec = parse::from_yaml(LITERAL_DEFAULT).expect("fixture should parse");
let ir = transform::transform(&spec).expect("fixture should transform");
let pack_dir = format!("{}/../../packs/{pack_rel}", env!("CARGO_MANIFEST_DIR"));
let pack = TemplatePack::from_dir(std::path::Path::new(&pack_dir))
.unwrap_or_else(|e| panic!("loading pack {pack_rel}: {e}"));
let out = generate(&ir, &config_for("out"), &pack).expect("generation should succeed");
out.source_files
}
fn file_ending_with(files: &[oag_core::GeneratedFile], suffix: &str) -> String {
files
.iter()
.find(|f| f.path.ends_with(suffix))
.unwrap_or_else(|| panic!("no rendered file ending in {suffix}"))
.content
.clone()
}
#[test]
fn node_client_renders_literal_default_as_required_literal() {
let files = render("node-client");
let types = file_ending_with(&files, "types.ts");
assert!(
types.contains("type: \"message\";"),
"expected required `type: \"message\";`, got:\n{types}"
);
assert!(
types.contains("objectType: \"list\";"),
"expected required `objectType: \"list\";`, got:\n{types}"
);
assert!(
types.contains("version: 1;"),
"expected required `version: 1;`, got:\n{types}"
);
assert!(
!types.contains("type?:"),
"literal `type` must not be optional, got:\n{types}"
);
assert!(
types.contains("type: \"text\";"),
"expected `type: \"text\";` for TextBlock, got:\n{types}"
);
assert!(
types.contains("temperature?:"),
"non-literal `temperature` should stay optional, got:\n{types}"
);
assert!(
types.contains("role?:"),
"multi-value enum `role` should stay optional, got:\n{types}"
);
assert!(
types.contains("content: string;"),
"required `content` should be present, got:\n{types}"
);
}
#[test]
fn fastapi_server_renders_literal_default_as_defaulted_literal() {
let files = render("fastapi-server");
let models = file_ending_with(&files, "models.py");
assert!(
models.contains("type: Literal[\"message\"] = \"message\""),
"expected `type: Literal[\"message\"] = \"message\"`, got:\n{models}"
);
assert!(
models.contains(
"object_type: Literal[\"list\"] = Field(default=\"list\", alias=\"objectType\")"
),
"expected aliased literal default for object_type, got:\n{models}"
);
assert!(
models.contains("version: Literal[1] = 1"),
"expected `version: Literal[1] = 1`, got:\n{models}"
);
assert!(
models.contains("type: Literal[\"text\"] = \"text\""),
"expected `type: Literal[\"text\"] = \"text\"` for TextBlock, got:\n{models}"
);
assert!(
models.contains("from typing import Any, Literal"),
"expected `Literal` import, got:\n{models}"
);
assert!(
!models.contains("Literal[\"message\"] | None"),
"literal field must not be optional, got:\n{models}"
);
assert!(
!models.contains("= Literal["),
"must not assign a type as a value, got:\n{models}"
);
assert!(
models.contains("temperature: float | None = None"),
"non-literal `temperature` should stay optional, got:\n{models}"
);
assert!(
models.contains("content: str"),
"required `content` should be present, got:\n{models}"
);
}