use pushkin_compiler::{compile, CompileError, CompileRequest, Target};
const USER_SCHEMA: &str = r#"{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1, "maxLength": 200 },
"email": { "type": "string", "format": "email" },
"role": { "default": "member", "type": "string", "enum": ["member", "admin"] }
},
"required": ["name", "email"],
"additionalProperties": false
}"#;
fn request(target: Target) -> CompileRequest {
CompileRequest {
contract_name: "user".to_owned(),
schema_json: USER_SCHEMA.to_owned(),
target,
epoch: 1,
}
}
#[test]
fn emits_strict_zod_binding() {
let out = compile(&request(Target::Zod)).unwrap();
assert!(
out.content.contains(".strict()"),
"Zod objects must be strict: {}",
out.content
);
assert!(out.content.contains("name"));
assert!(out.content.contains("email"));
assert!(out.content.contains("z.infer"));
}
#[test]
fn emits_strict_pydantic_binding() {
let out = compile(&request(Target::Pydantic)).unwrap();
assert!(
out.content.contains("extra='forbid'") || out.content.contains("extra=\"forbid\""),
"Pydantic must forbid unknown fields: {}",
out.content
);
assert!(out.content.contains("strict=True"));
assert!(out.content.contains("class UserCreate"));
}
#[test]
fn emits_strict_rust_binding() {
let out = compile(&request(Target::Rust)).unwrap();
assert!(
out.content.contains("deny_unknown_fields"),
"Rust serde must deny unknown fields: {}",
out.content
);
assert!(out.content.contains("pub struct UserCreate"));
}
#[test]
fn emits_sql_ddl() {
let out = compile(&request(Target::Sql)).unwrap();
assert!(out.content.contains("CREATE TABLE"));
assert!(out.content.to_lowercase().contains("not null"));
assert!(
out.content.contains("CHECK"),
"enum must become a CHECK constraint: {}",
out.content
);
}
#[test]
fn bindings_carry_epoch_header() {
for target in [Target::Zod, Target::Pydantic, Target::Rust, Target::Sql] {
let out = compile(&request(target)).unwrap();
assert!(
out.content.contains("pushkin-epoch: 1"),
"{target:?} missing epoch header:\n{}",
out.content
);
assert!(
out.content.to_lowercase().contains("generated"),
"{target:?} must be marked generated"
);
}
}
#[test]
fn regeneration_is_byte_identical() {
for target in [Target::Zod, Target::Pydantic, Target::Rust, Target::Sql] {
let first = compile(&request(target)).unwrap();
let second = compile(&request(target)).unwrap();
assert_eq!(
first.content, second.content,
"{target:?} regeneration must be deterministic"
);
}
}
#[test]
fn rejects_unrepresentable_construct_with_alternatives() {
let unrepresentable = r#"{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"patternProperties": { "^x-": { "type": "string" } },
"additionalProperties": false
}"#;
let err = compile(&CompileRequest {
contract_name: "user".to_owned(),
schema_json: unrepresentable.to_owned(),
target: Target::Rust,
epoch: 1,
})
.unwrap_err();
let message = err.to_string();
assert!(
message.contains("patternProperties"),
"must name the construct: {message}"
);
assert!(
message.contains("properties") || message.contains("alternative"),
"must suggest a named alternative: {message}"
);
assert!(matches!(err, CompileError::Unrepresentable { .. }));
}
#[test]
fn rejects_invalid_schema_json() {
let err = compile(&CompileRequest {
contract_name: "user".to_owned(),
schema_json: "{ not json".to_owned(),
target: Target::Zod,
epoch: 1,
})
.unwrap_err();
assert!(matches!(err, CompileError::InvalidSchema { .. }));
}