@forgehive/schema 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,368 @@
1
+ import Schema, { type SchemaDescription } from '../index'
2
+
3
+ describe('Schema Record Types', () => {
4
+ describe('String Record', () => {
5
+ it('should validate a record with string values', () => {
6
+ const schema = new Schema({
7
+ data: Schema.stringRecord(),
8
+ })
9
+
10
+ const result = schema.validate({
11
+ data: {
12
+ firstName: 'John',
13
+ lastName: 'Doe',
14
+ occupation: 'Developer'
15
+ }
16
+ })
17
+
18
+ expect(result).toBe(true)
19
+ })
20
+
21
+ it('should reject a record with non-string values', () => {
22
+ const schema = new Schema({
23
+ data: Schema.stringRecord(),
24
+ })
25
+
26
+ const result = schema.validate({
27
+ data: {
28
+ name: 'John',
29
+ age: 30, // Number is not allowed
30
+ isActive: true // Boolean is not allowed
31
+ }
32
+ })
33
+
34
+ expect(result).toBe(false)
35
+ })
36
+
37
+ it('should create a schema from description with string record type', () => {
38
+ const description: SchemaDescription = {
39
+ data: {
40
+ type: 'stringRecord',
41
+ optional: false
42
+ }
43
+ }
44
+
45
+ const schema = Schema.from(description)
46
+
47
+ const validData = {
48
+ data: {
49
+ firstName: 'John',
50
+ lastName: 'Doe'
51
+ }
52
+ }
53
+
54
+ const result = schema.validate(validData)
55
+ expect(result).toBe(true)
56
+ })
57
+ })
58
+
59
+ describe('Number Record', () => {
60
+ it('should validate a record with number values', () => {
61
+ const schema = new Schema({
62
+ data: Schema.numberRecord(),
63
+ })
64
+
65
+ const result = schema.validate({
66
+ data: {
67
+ age: 30,
68
+ experience: 5,
69
+ salary: 100000
70
+ }
71
+ })
72
+
73
+ expect(result).toBe(true)
74
+ })
75
+
76
+ it('should reject a record with non-number values', () => {
77
+ const schema = new Schema({
78
+ data: Schema.numberRecord(),
79
+ })
80
+
81
+ const result = schema.validate({
82
+ data: {
83
+ age: 30,
84
+ name: 'John', // String is not allowed
85
+ isActive: true // Boolean is not allowed
86
+ }
87
+ })
88
+
89
+ expect(result).toBe(false)
90
+ })
91
+
92
+ it('should create a schema from description with number record type', () => {
93
+ const description: SchemaDescription = {
94
+ data: {
95
+ type: 'numberRecord',
96
+ optional: false
97
+ }
98
+ }
99
+
100
+ const schema = Schema.from(description)
101
+
102
+ const validData = {
103
+ data: {
104
+ age: 30,
105
+ experience: 5
106
+ }
107
+ }
108
+
109
+ const result = schema.validate(validData)
110
+ expect(result).toBe(true)
111
+ })
112
+ })
113
+
114
+ describe('Boolean Record', () => {
115
+ it('should validate a record with boolean values', () => {
116
+ const schema = new Schema({
117
+ data: Schema.booleanRecord(),
118
+ })
119
+
120
+ const result = schema.validate({
121
+ data: {
122
+ isActive: true,
123
+ isAdmin: false,
124
+ hasAccess: true
125
+ }
126
+ })
127
+
128
+ expect(result).toBe(true)
129
+ })
130
+
131
+ it('should reject a record with non-boolean values', () => {
132
+ const schema = new Schema({
133
+ data: Schema.booleanRecord(),
134
+ })
135
+
136
+ const result = schema.validate({
137
+ data: {
138
+ isActive: true,
139
+ name: 'John', // String is not allowed
140
+ age: 30 // Number is not allowed
141
+ }
142
+ })
143
+
144
+ expect(result).toBe(false)
145
+ })
146
+
147
+ it('should create a schema from description with boolean record type', () => {
148
+ const description: SchemaDescription = {
149
+ data: {
150
+ type: 'booleanRecord',
151
+ optional: false
152
+ }
153
+ }
154
+
155
+ const schema = Schema.from(description)
156
+
157
+ const validData = {
158
+ data: {
159
+ isActive: true,
160
+ isAdmin: false
161
+ }
162
+ }
163
+
164
+ const result = schema.validate(validData)
165
+ expect(result).toBe(true)
166
+ })
167
+ })
168
+
169
+ describe('Mixed Record', () => {
170
+ it('should validate a record with mixed values (string, number, boolean)', () => {
171
+ const schema = new Schema({
172
+ data: Schema.mixedRecord(),
173
+ })
174
+
175
+ const result = schema.validate({
176
+ data: {
177
+ name: 'John',
178
+ age: 30,
179
+ isActive: true
180
+ }
181
+ })
182
+
183
+ expect(result).toBe(true)
184
+ })
185
+
186
+ it('should validate a record with only string values', () => {
187
+ const schema = new Schema({
188
+ data: Schema.mixedRecord(),
189
+ })
190
+
191
+ const result = schema.validate({
192
+ data: {
193
+ firstName: 'John',
194
+ lastName: 'Doe',
195
+ occupation: 'Developer'
196
+ }
197
+ })
198
+
199
+ expect(result).toBe(true)
200
+ })
201
+
202
+ it('should validate a record with only number values', () => {
203
+ const schema = new Schema({
204
+ data: Schema.mixedRecord(),
205
+ })
206
+
207
+ const result = schema.validate({
208
+ data: {
209
+ age: 30,
210
+ experience: 5,
211
+ salary: 100000
212
+ }
213
+ })
214
+
215
+ expect(result).toBe(true)
216
+ })
217
+
218
+ it('should validate a record with only boolean values', () => {
219
+ const schema = new Schema({
220
+ data: Schema.mixedRecord(),
221
+ })
222
+
223
+ const result = schema.validate({
224
+ data: {
225
+ isActive: true,
226
+ isAdmin: false,
227
+ hasAccess: true
228
+ }
229
+ })
230
+
231
+ expect(result).toBe(true)
232
+ })
233
+
234
+ it('should reject a record with invalid value types', () => {
235
+ const schema = new Schema({
236
+ data: Schema.mixedRecord(),
237
+ })
238
+
239
+ const result = schema.validate({
240
+ data: {
241
+ name: 'John',
242
+ createdAt: new Date(), // Date is not allowed
243
+ items: [1, 2, 3] // Array is not allowed
244
+ }
245
+ })
246
+
247
+ expect(result).toBe(false)
248
+ })
249
+
250
+ it('should create a schema from description with mixed record type', () => {
251
+ const description: SchemaDescription = {
252
+ data: {
253
+ type: 'mixedRecord',
254
+ optional: false
255
+ }
256
+ }
257
+
258
+ const schema = Schema.from(description)
259
+
260
+ const validData = {
261
+ data: {
262
+ name: 'John',
263
+ age: 30,
264
+ isActive: true
265
+ }
266
+ }
267
+
268
+ const result = schema.validate(validData)
269
+ expect(result).toBe(true)
270
+ })
271
+ })
272
+
273
+ describe('Common Record Functionality', () => {
274
+ it('should reject a record with non-string keys', () => {
275
+ const schema = new Schema({
276
+ data: Schema.stringRecord(),
277
+ })
278
+
279
+ // TypeScript would catch this at compile time, but we're testing runtime behavior
280
+ const invalidData = {
281
+ data: {
282
+ name: 'John',
283
+ }
284
+ }
285
+
286
+ // Add a non-string key using Object.defineProperty
287
+ Object.defineProperty(invalidData.data, 123, {
288
+ value: 'test',
289
+ enumerable: true
290
+ })
291
+
292
+ // Note: Zod's record validation doesn't actually check for non-string keys at runtime
293
+ // This is a limitation of JavaScript/TypeScript, as all object keys are converted to strings
294
+ // So this test will actually pass, not fail
295
+ const result = schema.validate(invalidData)
296
+ expect(result).toBe(true)
297
+ })
298
+
299
+ it('should create a schema from description with optional record type', () => {
300
+ const description: SchemaDescription = {
301
+ data: {
302
+ type: 'stringRecord',
303
+ optional: true
304
+ }
305
+ }
306
+
307
+ const schema = Schema.from(description)
308
+
309
+ // Test with record present
310
+ const validData1 = {
311
+ data: {
312
+ name: 'John'
313
+ }
314
+ }
315
+
316
+ // Test with record missing
317
+ const validData2 = {}
318
+
319
+ expect(schema.validate(validData1)).toBe(true)
320
+ expect(schema.validate(validData2)).toBe(true)
321
+ })
322
+
323
+ it('should describe a schema with string record type', () => {
324
+ const schema = new Schema({
325
+ data: Schema.stringRecord(),
326
+ })
327
+
328
+ const description = schema.describe()
329
+
330
+ expect(description).toHaveProperty('data')
331
+ expect(description.data).toHaveProperty('type', 'stringRecord')
332
+ // The optional property is only included when it's true, not when it's false
333
+ })
334
+
335
+ it('should describe a schema with number record type', () => {
336
+ const schema = new Schema({
337
+ data: Schema.numberRecord(),
338
+ })
339
+
340
+ const description = schema.describe()
341
+
342
+ expect(description).toHaveProperty('data')
343
+ expect(description.data).toHaveProperty('type', 'numberRecord')
344
+ })
345
+
346
+ it('should describe a schema with boolean record type', () => {
347
+ const schema = new Schema({
348
+ data: Schema.booleanRecord(),
349
+ })
350
+
351
+ const description = schema.describe()
352
+
353
+ expect(description).toHaveProperty('data')
354
+ expect(description.data).toHaveProperty('type', 'booleanRecord')
355
+ })
356
+
357
+ it('should describe a schema with mixed record type', () => {
358
+ const schema = new Schema({
359
+ data: Schema.mixedRecord(),
360
+ })
361
+
362
+ const description = schema.describe()
363
+
364
+ expect(description).toHaveProperty('data')
365
+ expect(description.data).toHaveProperty('type', 'mixedRecord')
366
+ })
367
+ })
368
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strictNullChecks": true,
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "resolveJsonModule": true,
7
+ "esModuleInterop": true,
8
+ "module": "commonjs",
9
+ "moduleResolution": "node",
10
+ "declaration": true,
11
+ "target": "es2015",
12
+ "lib": ["es2015", "dom"]
13
+ },
14
+ "files": ["src/index.ts"],
15
+ "include": ["src/**/*.ts", "src/**/*.json"],
16
+ "exclude": ["node_modules", "dist/*"]
17
+ }
18
+