Skip to content

Commit c04a8e9

Browse files
committed
feat: add tests for dataTypeMap to validate recursive schema handling
1 parent 99702c7 commit c04a8e9

1 file changed

Lines changed: 83 additions & 0 deletions

File tree

ts/test/datatype.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import "reflect-metadata";
2+
import { describe, expect, it } from "vitest";
3+
import { z, type ZodTypeAny } from "zod";
4+
import { Identifier } from "../src/decorators/meta.dec";
5+
import { Schema } from "../src/decorators/datatype.dec";
6+
import { dataTypeMap } from "../src/map/datatype.map";
7+
8+
describe("dataTypeMap type generation", () => {
9+
it("inlines plain schemas", () => {
10+
@Identifier("PlainType")
11+
@Schema(z.object({ name: z.string() }))
12+
class PlainType {}
13+
14+
expect(dataTypeMap(PlainType).type).toBe("{ name: string; }");
15+
});
16+
17+
it("resolves mutually recursive schemas by identifier regardless of registration order", () => {
18+
const OrderSchema: ZodTypeAny = z.lazy(() =>
19+
z.object({ addresses: z.array(AddressSchema) })
20+
);
21+
const AddressSchema: ZodTypeAny = z.lazy(() =>
22+
z.object({ order: OrderSchema })
23+
);
24+
25+
@Identifier("MutualOrder")
26+
@Schema(OrderSchema)
27+
class MutualOrder {}
28+
29+
@Identifier("MutualAddress")
30+
@Schema(AddressSchema)
31+
class MutualAddress {}
32+
33+
const orderDef = dataTypeMap(MutualOrder);
34+
const addressDef = dataTypeMap(MutualAddress);
35+
36+
expect(orderDef.type).toBe("{ addresses: MutualAddress[]; }");
37+
expect(addressDef.type).toBe("{ order: MutualOrder; }");
38+
});
39+
40+
it("references itself by identifier for self-recursive schemas", () => {
41+
const NodeSchema: ZodTypeAny = z.lazy(() =>
42+
z.object({ children: z.array(NodeSchema) })
43+
);
44+
45+
@Identifier("TreeNode")
46+
@Schema(NodeSchema)
47+
class TreeNode {}
48+
49+
expect(dataTypeMap(TreeNode).type).toBe("{ children: TreeNode[]; }");
50+
});
51+
52+
it("prints registered schemas embedded in other types as identifier references", () => {
53+
const ItemSchema: ZodTypeAny = z.lazy(() =>
54+
z.object({ related: z.array(ItemSchema) })
55+
);
56+
57+
@Identifier("EmbeddedItem")
58+
@Schema(ItemSchema)
59+
class EmbeddedItem {}
60+
61+
@Identifier("ItemPayload")
62+
@Schema(z.object({ item: ItemSchema, count: z.number() }))
63+
class ItemPayload {}
64+
65+
dataTypeMap(EmbeddedItem);
66+
const payloadDef = dataTypeMap(ItemPayload);
67+
68+
expect(payloadDef.type).toBe("{ item: EmbeddedItem; count: number; }");
69+
});
70+
71+
it("throws a helpful error when a recursive schema is not registered as a data type", () => {
72+
const LoopSchema: ZodTypeAny = z.lazy(() =>
73+
z.object({ next: LoopSchema })
74+
);
75+
76+
@Identifier("BrokenPayload")
77+
@Schema(z.object({ loop: LoopSchema }))
78+
class BrokenPayload {}
79+
80+
const def = dataTypeMap(BrokenPayload);
81+
expect(() => def.type).toThrow(/BrokenPayload.*recursive/i);
82+
});
83+
});

0 commit comments

Comments
 (0)