11import { describe , it , expect } from "vitest" ;
22import { convertJsonSchemaToZod , jsonSchemaObjectToZodRawShape } from "./index" ;
33import { z } from "zod/v4" ;
4- import { JSONSchema } from "zod/v4/core" ;
4+ import { JSONSchema } from "zod/v4/core" ;
55
66describe ( "convertJsonSchemaToZod" , ( ) => {
77 it ( "should correctly convert a schema with additionalProperties: {}" , ( ) => {
@@ -93,7 +93,11 @@ describe("convertJsonSchemaToZod", () => {
9393
9494 const resultSchema = z . toJSONSchema ( zodSchema ) ;
9595 // Zod v4 converts unions to anyOf instead of enum
96- expect ( resultSchema . anyOf ) . toEqual ( [ { type : "number" , const : 1 } , { type : "number" , const : 2 } , { type : "number" , const : 3 } ] ) ;
96+ expect ( resultSchema . anyOf ) . toEqual ( [
97+ { type : "number" , const : 1 } ,
98+ { type : "number" , const : 2 } ,
99+ { type : "number" , const : 3 } ,
100+ ] ) ;
97101 } ) ;
98102
99103 it ( "should correctly convert a schema with boolean enum (no type)" , ( ) => {
@@ -111,7 +115,10 @@ describe("convertJsonSchemaToZod", () => {
111115
112116 const resultSchema = z . toJSONSchema ( zodSchema ) ;
113117 // Zod v4 converts unions to anyOf instead of enum
114- expect ( resultSchema . anyOf ) . toEqual ( [ { type : "boolean" , const : true } , { type : "boolean" , const : false } ] ) ;
118+ expect ( resultSchema . anyOf ) . toEqual ( [
119+ { type : "boolean" , const : true } ,
120+ { type : "boolean" , const : false } ,
121+ ] ) ;
115122 } ) ;
116123
117124 it ( "should correctly convert a schema with mixed enum (no type)" , ( ) => {
@@ -132,7 +139,12 @@ describe("convertJsonSchemaToZod", () => {
132139
133140 const resultSchema = z . toJSONSchema ( zodSchema ) ;
134141 // Zod v4 converts unions to anyOf instead of enum
135- expect ( resultSchema . anyOf ) . toEqual ( [ { type : "string" , const : "red" } , { type : "number" , const : 1 } , { type : "boolean" , const : true } , { type : "null" } ] ) ;
142+ expect ( resultSchema . anyOf ) . toEqual ( [
143+ { type : "string" , const : "red" } ,
144+ { type : "number" , const : 1 } ,
145+ { type : "boolean" , const : true } ,
146+ { type : "null" } ,
147+ ] ) ;
136148 } ) ;
137149
138150 it ( "should correctly convert a schema with single item mixed enum (no type)" , ( ) => {
@@ -570,12 +582,12 @@ describe("convertJsonSchemaToZod", () => {
570582
571583 // Test that the validation works correctly
572584 expect ( zodSchema . safeParse ( "hello" ) . success ) . toBe ( true ) ; // 5 chars, within range
573- expect ( zodSchema . safeParse ( "hi" ) . success ) . toBe ( false ) ; // 2 chars, too short
585+ expect ( zodSchema . safeParse ( "hi" ) . success ) . toBe ( false ) ; // 2 chars, too short
574586 expect ( zodSchema . safeParse ( "this is too long" ) . success ) . toBe ( false ) ; // too long
575587
576588 // Test Unicode support
577589 expect ( zodSchema . safeParse ( "💩💩💩" ) . success ) . toBe ( true ) ; // 3 graphemes
578- expect ( zodSchema . safeParse ( "💩" ) . success ) . toBe ( false ) ; // 1 grapheme, too short
590+ expect ( zodSchema . safeParse ( "💩" ) . success ) . toBe ( false ) ; // 1 grapheme, too short
579591
580592 // Note: length constraints implemented with .refine() don't round-trip
581593 // back to JSON Schema, so we only test the validation behavior
@@ -1025,7 +1037,7 @@ describe("jsonSchemaObjectToZodRawShape", () => {
10251037 // Verify types are correct - required fields are direct types
10261038 expect ( rawShape . name instanceof z . ZodString ) . toBe ( true ) ;
10271039 expect ( rawShape . age instanceof z . ZodNumber ) . toBe ( true ) ;
1028-
1040+
10291041 // isActive is not in required array, so it should be optional
10301042 expect ( rawShape . isActive instanceof z . ZodOptional ) . toBe ( true ) ;
10311043 // Check the inner type of the optional
@@ -1097,16 +1109,14 @@ describe("jsonSchemaObjectToZodRawShape", () => {
10971109 user :
{ email :
"[email protected] " } , 10981110 } ) ,
10991111 ) . toThrow ( ) ;
1100-
1112+
11011113 // Since user is optional at the top level, empty object should pass
1102- expect ( ( ) =>
1103- schema . parse ( { } ) ,
1104- ) . not . toThrow ( ) ;
1114+ expect ( ( ) => schema . parse ( { } ) ) . not . toThrow ( ) ;
11051115 } ) ;
11061116
11071117 it ( "should be usable to build custom schemas" , ( ) => {
11081118 const jsonSchema : JSONSchema . BaseSchema = {
1109- type : "object" ,
1119+ type : "object" ,
11101120 properties : {
11111121 name : { type : "string" } ,
11121122 age : { type : "integer" } ,
@@ -1217,4 +1227,42 @@ describe("jsonSchemaObjectToZodRawShape", () => {
12171227 } ) ,
12181228 ) . not . toThrow ( ) ;
12191229 } ) ;
1230+
1231+ it ( "schema with defaults should parse empty objects" , ( ) => {
1232+ const jsonSchema = {
1233+ type : "object" ,
1234+ properties : {
1235+ field1 : { type : "string" , default : "test" } ,
1236+ field2 : { type : "number" , default : 42 } ,
1237+ field3 : { type : "boolean" , default : true } ,
1238+ field4 : { type : "string" , enum : [ "test1" , "test2" ] , default : "test2" } ,
1239+ field5 : {
1240+ type : "object" ,
1241+ default : { name : "test" , age : 10 , isActive : true } ,
1242+ properties : {
1243+ name : { type : "string" } ,
1244+ age : { type : "integer" , minimum : - 43 , maximum : 120 } ,
1245+ isActive : { type : "boolean" } ,
1246+ } ,
1247+ required : [ "name" , "age" , "isActive" ] ,
1248+ } ,
1249+ field6 : { type : "string" , default : 42 } ,
1250+ } ,
1251+ // No required field - all properties should be optional
1252+ } ;
1253+
1254+ const rawShape = jsonSchemaObjectToZodRawShape ( jsonSchema ) ;
1255+ const schema = z . object ( rawShape ) ;
1256+
1257+ // All fields should be optional
1258+ expect ( ( ) => schema . parse ( { } ) ) . not . toThrow ( ) ;
1259+ expect ( schema . parse ( { } ) ) . toEqual ( {
1260+ field1 : "test" ,
1261+ field2 : 42 ,
1262+ field3 : true ,
1263+ field4 : "test2" ,
1264+ field5 : { name : "test" , age : 10 , isActive : true } ,
1265+ // `field6` doesn't get the default, because it's not the correct type
1266+ } ) ;
1267+ } ) ;
12201268} ) ;
0 commit comments