#![allow(
clippy::doc_markdown,
clippy::redundant_clone,
reason = "Test file with code generation"
)]
use serde_json::json;
use spikard_cli::codegen::{
CodegenEngine, CodegenOutcome, CodegenRequest, CodegenTargetKind, SchemaKind, TargetLanguage,
};
use std::path::PathBuf;
fn root_schema(path: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../testing_data/schemas")
.join(path)
}
#[test]
fn codegen_engine_openapi_in_memory() {
let request = CodegenRequest {
schema_path: root_schema("auth-service.openapi.yaml"),
schema_kind: SchemaKind::OpenApi,
target: CodegenTargetKind::Server {
language: TargetLanguage::TypeScript,
output: None,
},
dto: None,
};
let outcome = CodegenEngine::execute(request).expect("openapi codegen should succeed");
match outcome {
CodegenOutcome::InMemory(code) => assert!(!code.trim().is_empty()),
CodegenOutcome::Files(_) => panic!("expected in-memory outcome"),
}
}
#[test]
fn codegen_engine_openrpc_writes_output_file() {
let temp_dir = tempfile::tempdir().expect("temp dir");
let out_path = temp_dir.path().join("handlers.ts");
let request = CodegenRequest {
schema_path: root_schema("user-api.openrpc.json"),
schema_kind: SchemaKind::OpenRpc,
target: CodegenTargetKind::JsonRpcHandlers {
language: TargetLanguage::TypeScript,
output: out_path.clone(),
},
dto: None,
};
let outcome = CodegenEngine::execute(request).expect("openrpc handler generation should succeed");
match outcome {
CodegenOutcome::Files(files) => {
assert_eq!(files.len(), 1);
assert_eq!(files[0].path, out_path);
assert!(files[0].description.contains("JSON-RPC"));
}
CodegenOutcome::InMemory(_) => panic!("expected file outcome"),
}
let generated = std::fs::read_to_string(&out_path).expect("handler output should be written");
assert!(generated.contains("handleJsonRpcCall"));
}
#[test]
fn codegen_engine_asyncapi_bundle_creates_assets() {
let temp_dir = tempfile::tempdir().expect("temp dir");
let output_dir = temp_dir.path().join("out");
let request = CodegenRequest {
schema_path: root_schema("chat-service.asyncapi.yaml"),
schema_kind: SchemaKind::AsyncApi,
target: CodegenTargetKind::AsyncAll {
output: output_dir.clone(),
},
dto: None,
};
let outcome = CodegenEngine::execute(request).expect("asyncapi bundle generation should succeed");
let CodegenOutcome::Files(assets) = outcome else {
panic!("expected file outcome");
};
assert!(!assets.is_empty(), "expected generated assets");
for asset in &assets {
assert!(asset.path.exists(), "missing generated file: {}", asset.path.display());
assert!(!asset.description.trim().is_empty());
}
}
#[test]
fn unsupported_schema_target_pair_is_rejected() {
let temp_dir = tempfile::tempdir().expect("temp dir");
let request = CodegenRequest {
schema_path: root_schema("auth-service.openapi.yaml"),
schema_kind: SchemaKind::OpenApi,
target: CodegenTargetKind::JsonRpcHandlers {
language: TargetLanguage::TypeScript,
output: temp_dir.path().join("out.ts"),
},
dto: None,
};
let err = CodegenEngine::execute(request).expect_err("unsupported combo should be rejected");
assert!(err.to_string().contains("Unsupported schema/target combination"));
}
#[test]
fn codegen_engine_graphql_accepts_introspection_json() {
let temp_dir = tempfile::tempdir().expect("temp dir");
let schema_path = temp_dir.path().join("schema.json");
let output_path = temp_dir.path().join("generated.py");
let introspection = json!({
"data": {
"__schema": {
"description": "Introspection schema",
"queryType": { "name": "Query" },
"mutationType": null,
"subscriptionType": null,
"directives": [],
"types": [
{
"kind": "OBJECT",
"name": "Query",
"description": null,
"fields": [
{
"name": "hello",
"description": "Greeting",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "OBJECT", "name": "User", "ofType": null }
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "User",
"description": "User record",
"fields": [
{
"name": "name",
"description": "Display name",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "String",
"description": null,
"fields": null,
"inputFields": null,
"enumValues": null,
"possibleTypes": null
}
]
}
}
});
std::fs::write(&schema_path, introspection.to_string()).expect("write schema");
let request = CodegenRequest {
schema_path,
schema_kind: SchemaKind::GraphQL,
target: CodegenTargetKind::GraphQL {
language: TargetLanguage::Python,
output: output_path.clone(),
target: "types".to_string(),
},
dto: None,
};
let outcome = CodegenEngine::execute(request).expect("graphql introspection codegen should succeed");
match outcome {
CodegenOutcome::Files(files) => {
assert_eq!(files.len(), 1);
assert_eq!(files[0].path, output_path);
}
CodegenOutcome::InMemory(_) => panic!("expected file outcome"),
}
let generated = std::fs::read_to_string(&output_path).expect("generated output");
assert!(generated.contains("GraphQL types generated from schema."));
assert!(generated.contains("class User(Struct, frozen=True, kw_only=True):"));
assert!(generated.contains("name: str"));
}