Skip to content

Commit 1bea053

Browse files
committed
Add tests
1 parent 5c3114f commit 1bea053

13 files changed

+1602
-0
lines changed
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/** @jest-environment ./src/utils/enableVersionCheck.ts */
2+
3+
import { createTestNode } from "@test-utils";
4+
5+
import { KurrentDBClient } from "@kurrent/kurrentdb-client";
6+
7+
describe("checkSchemaCompatibility", () => {
8+
const node = createTestNode();
9+
let client!: KurrentDBClient;
10+
11+
const generateSchemaName = () =>
12+
`test-schema-${Date.now()}-${Math.random().toString(36).substring(7)}`;
13+
14+
beforeAll(async () => {
15+
await node.up();
16+
client = KurrentDBClient.connectionString(node.connectionString());
17+
});
18+
19+
afterAll(async () => {
20+
await node.down();
21+
});
22+
23+
describe("check by schema name", () => {
24+
test("compatible definition returns success", async () => {
25+
const schemaName = generateSchemaName();
26+
27+
// Create schema with backward compatibility
28+
await client.createSchema(
29+
schemaName,
30+
{
31+
dataFormat: "json",
32+
compatibility: "backward",
33+
},
34+
{
35+
schemaDefinition: JSON.stringify({
36+
type: "object",
37+
properties: {
38+
name: { type: "string" },
39+
},
40+
required: ["name"],
41+
}),
42+
}
43+
);
44+
45+
// Check backward compatible definition (adding optional field)
46+
const result = await client.checkSchemaCompatibility(
47+
JSON.stringify({
48+
type: "object",
49+
properties: {
50+
name: { type: "string" },
51+
age: { type: "number" },
52+
},
53+
required: ["name"],
54+
}),
55+
{ schemaName, dataFormat: "json" }
56+
);
57+
58+
expect(result.isCompatible).toBe(true);
59+
if (!result.isCompatible) {
60+
expect(result.errors.length).toBe(0);
61+
}
62+
});
63+
64+
test("incompatible definition returns errors", async () => {
65+
const schemaName = generateSchemaName();
66+
67+
// Create schema with full compatibility
68+
await client.createSchema(
69+
schemaName,
70+
{
71+
dataFormat: "json",
72+
compatibility: "full",
73+
},
74+
{
75+
schemaDefinition: JSON.stringify({
76+
type: "object",
77+
properties: {
78+
name: { type: "string" },
79+
},
80+
required: ["name"],
81+
}),
82+
}
83+
);
84+
85+
// Check incompatible definition (changing required field type)
86+
const result = await client.checkSchemaCompatibility(
87+
JSON.stringify({
88+
type: "object",
89+
properties: {
90+
name: { type: "number" },
91+
},
92+
required: ["name"],
93+
}),
94+
{ schemaName, dataFormat: "json" }
95+
);
96+
97+
expect(result.isCompatible).toBe(false);
98+
if (!result.isCompatible) {
99+
expect(result.errors.length).toBeGreaterThan(0);
100+
}
101+
});
102+
103+
test("compatibility mode 'none' accepts any definition", async () => {
104+
const schemaName = generateSchemaName();
105+
106+
// Create schema with no compatibility checks
107+
await client.createSchema(
108+
schemaName,
109+
{
110+
dataFormat: "json",
111+
compatibility: "none",
112+
},
113+
{
114+
schemaDefinition: JSON.stringify({
115+
type: "object",
116+
properties: { old: { type: "string" } },
117+
}),
118+
}
119+
);
120+
121+
// Completely different schema should be compatible with 'none'
122+
const result = await client.checkSchemaCompatibility(
123+
JSON.stringify({
124+
type: "array",
125+
items: { type: "number" },
126+
}),
127+
{ schemaName, dataFormat: "json" }
128+
);
129+
130+
expect(result.isCompatible).toBe(true);
131+
});
132+
});
133+
134+
describe("check by schema version ID", () => {
135+
test("check against specific version by ID", async () => {
136+
const schemaName = generateSchemaName();
137+
138+
// Create schema with multiple versions
139+
const createResult = await client.createSchema(
140+
schemaName,
141+
{
142+
dataFormat: "json",
143+
compatibility: "backward",
144+
},
145+
{
146+
schemaDefinition: JSON.stringify({
147+
type: "object",
148+
properties: { v1Field: { type: "string" } },
149+
}),
150+
}
151+
);
152+
153+
const version1Id = createResult.schemaVersionId!;
154+
155+
// Register second version
156+
await client.registerSchemaVersion(
157+
schemaName,
158+
JSON.stringify({
159+
type: "object",
160+
properties: {
161+
v1Field: { type: "string" },
162+
v2Field: { type: "number" },
163+
},
164+
})
165+
);
166+
167+
// Check compatibility against version 1 specifically
168+
const result = await client.checkSchemaCompatibility(
169+
JSON.stringify({
170+
type: "object",
171+
properties: {
172+
v1Field: { type: "string" },
173+
newField: { type: "boolean" },
174+
},
175+
}),
176+
{ schemaVersionId: version1Id, dataFormat: "json" }
177+
);
178+
179+
expect(result.isCompatible).toBeDefined();
180+
});
181+
});
182+
183+
describe("error details", () => {
184+
test("errors include descriptive messages", async () => {
185+
const schemaName = generateSchemaName();
186+
187+
await client.createSchema(
188+
schemaName,
189+
{
190+
dataFormat: "json",
191+
compatibility: "forward",
192+
},
193+
{
194+
schemaDefinition: JSON.stringify({
195+
type: "object",
196+
properties: {
197+
required_field: { type: "string" },
198+
},
199+
required: ["required_field"],
200+
}),
201+
}
202+
);
203+
204+
// Breaking forward compatibility by adding required field
205+
const result = await client.checkSchemaCompatibility(
206+
JSON.stringify({
207+
type: "object",
208+
properties: {
209+
required_field: { type: "string" },
210+
another_required: { type: "string" },
211+
},
212+
required: ["required_field", "another_required"],
213+
}),
214+
{ schemaName, dataFormat: "json" }
215+
);
216+
217+
// The exact behavior depends on the server's compatibility implementation
218+
expect(result).toBeDefined();
219+
expect(typeof result.isCompatible).toBe("boolean");
220+
if (!result.isCompatible) {
221+
expect(Array.isArray(result.errors)).toBe(true);
222+
}
223+
});
224+
});
225+
226+
describe("should handle errors", () => {
227+
test("error when checking non-existent schema", async () => {
228+
const nonExistentName = `non-existent-${Date.now()}`;
229+
230+
await expect(
231+
client.checkSchemaCompatibility(JSON.stringify({ type: "object" }), {
232+
schemaName: nonExistentName,
233+
dataFormat: "json",
234+
})
235+
).rejects.toThrow();
236+
});
237+
238+
test("error when checking non-existent version ID", async () => {
239+
const fakeVersionId = "00000000-0000-0000-0000-000000000000";
240+
241+
await expect(
242+
client.checkSchemaCompatibility(JSON.stringify({ type: "object" }), {
243+
schemaVersionId: fakeVersionId,
244+
dataFormat: "json",
245+
})
246+
).rejects.toThrow();
247+
});
248+
});
249+
});
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/** @jest-environment ./src/utils/enableVersionCheck.ts */
2+
3+
import { createTestNode } from "@test-utils";
4+
5+
import { KurrentDBClient } from "@kurrent/kurrentdb-client";
6+
7+
describe("createSchema", () => {
8+
const node = createTestNode();
9+
let client!: KurrentDBClient;
10+
11+
const generateSchemaName = () =>
12+
`test-schema-${Date.now()}-${Math.random().toString(36).substring(7)}`;
13+
14+
beforeAll(async () => {
15+
await node.up();
16+
client = KurrentDBClient.connectionString(node.connectionString());
17+
});
18+
19+
afterAll(async () => {
20+
await node.down();
21+
});
22+
23+
describe("should create a schema", () => {
24+
test("with metadata only", async () => {
25+
const schemaName = generateSchemaName();
26+
27+
const result = await client.createSchema(schemaName, {
28+
dataFormat: "json",
29+
compatibility: "backward",
30+
description: "Test schema without definition",
31+
});
32+
33+
expect(result).toBeDefined();
34+
expect(result.schemaVersionId).toBeUndefined();
35+
expect(result.versionNumber).toBeUndefined();
36+
});
37+
38+
test("with initial definition", async () => {
39+
const schemaName = generateSchemaName();
40+
const definition = JSON.stringify({
41+
type: "object",
42+
properties: {
43+
name: { type: "string" },
44+
age: { type: "number" },
45+
},
46+
});
47+
48+
const result = await client.createSchema(
49+
schemaName,
50+
{
51+
dataFormat: "json",
52+
compatibility: "backward",
53+
description: "Test schema with definition",
54+
},
55+
{ schemaDefinition: definition }
56+
);
57+
58+
expect(result).toBeDefined();
59+
expect(result.schemaVersionId).toBeDefined();
60+
expect(result.schemaVersionId).not.toBe("");
61+
expect(result.versionNumber).toBe(1);
62+
});
63+
64+
test("with tags", async () => {
65+
const schemaName = generateSchemaName();
66+
67+
const result = await client.createSchema(schemaName, {
68+
dataFormat: "json",
69+
compatibility: "none",
70+
tags: {
71+
environment: "test",
72+
owner: "test-suite",
73+
},
74+
});
75+
76+
expect(result).toBeDefined();
77+
});
78+
79+
test("with different data formats", async () => {
80+
const formats = ["json", "protobuf", "avro", "bytes"] as const;
81+
82+
for (const dataFormat of formats) {
83+
const schemaName = generateSchemaName();
84+
85+
const result = await client.createSchema(schemaName, {
86+
dataFormat,
87+
compatibility: "none",
88+
});
89+
90+
expect(result).toBeDefined();
91+
}
92+
});
93+
});
94+
95+
describe("should handle errors", () => {
96+
test("duplicate schema name", async () => {
97+
const schemaName = generateSchemaName();
98+
99+
// Create first schema
100+
await client.createSchema(schemaName, {
101+
dataFormat: "json",
102+
compatibility: "none",
103+
});
104+
105+
// Attempt to create duplicate
106+
await expect(
107+
client.createSchema(schemaName, {
108+
dataFormat: "json",
109+
compatibility: "none",
110+
})
111+
).rejects.toThrow();
112+
});
113+
});
114+
});

0 commit comments

Comments
 (0)