@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.
- package/LICENSE +21 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.js +285 -0
- package/dist/test/custom-validations.test.d.ts +1 -0
- package/dist/test/custom-validations.test.js +259 -0
- package/dist/test/dates.test.d.ts +1 -0
- package/dist/test/dates.test.js +74 -0
- package/dist/test/index.test.d.ts +1 -0
- package/dist/test/index.test.js +170 -0
- package/dist/test/optionals.test.d.ts +1 -0
- package/dist/test/optionals.test.js +60 -0
- package/dist/test/record.test.d.ts +1 -0
- package/dist/test/record.test.js +303 -0
- package/jest.config.js +11 -0
- package/package.json +20 -0
- package/src/index.ts +361 -0
- package/src/test/custom-validations.test.ts +293 -0
- package/src/test/dates.test.ts +85 -0
- package/src/test/index.test.ts +205 -0
- package/src/test/optionals.test.ts +73 -0
- package/src/test/record.test.ts +368 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../index");
|
|
4
|
+
describe('Schema Custom Number Validations', () => {
|
|
5
|
+
it('should validate number min/max constraints', () => {
|
|
6
|
+
const schema = index_1.Schema.from({
|
|
7
|
+
age: {
|
|
8
|
+
type: 'number',
|
|
9
|
+
validations: {
|
|
10
|
+
min: 18,
|
|
11
|
+
max: 100
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
score: {
|
|
15
|
+
type: 'number',
|
|
16
|
+
validations: {
|
|
17
|
+
min: 0,
|
|
18
|
+
max: 100
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
// Valid cases
|
|
23
|
+
expect(schema.validate({ age: 25, score: 85 })).toBe(true);
|
|
24
|
+
expect(schema.validate({ age: 18, score: 0 })).toBe(true);
|
|
25
|
+
expect(schema.validate({ age: 100, score: 100 })).toBe(true);
|
|
26
|
+
// Invalid cases
|
|
27
|
+
expect(schema.validate({ age: 17, score: 85 })).toBe(false);
|
|
28
|
+
expect(schema.validate({ age: 25, score: -1 })).toBe(false);
|
|
29
|
+
expect(schema.validate({ age: 101, score: 85 })).toBe(false);
|
|
30
|
+
expect(schema.validate({ age: 25, score: 101 })).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
it('should correctly describe number validations', () => {
|
|
33
|
+
const schema = index_1.Schema.from({
|
|
34
|
+
age: {
|
|
35
|
+
type: 'number',
|
|
36
|
+
validations: {
|
|
37
|
+
min: 18,
|
|
38
|
+
max: 100
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
const description = schema.describe();
|
|
43
|
+
expect(description.age).toEqual({
|
|
44
|
+
type: 'number',
|
|
45
|
+
validations: {
|
|
46
|
+
min: 18,
|
|
47
|
+
max: 100
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('Schema Custom String Validations', () => {
|
|
53
|
+
it('should validate string email format', () => {
|
|
54
|
+
const schema = index_1.Schema.from({
|
|
55
|
+
email: {
|
|
56
|
+
type: 'string',
|
|
57
|
+
validations: {
|
|
58
|
+
email: true
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
// Valid cases
|
|
63
|
+
expect(schema.validate({ email: 'user@example.com' })).toBe(true);
|
|
64
|
+
expect(schema.validate({ email: 'test.name@domain.co.uk' })).toBe(true);
|
|
65
|
+
expect(schema.validate({ email: 'user+tag@example.com' })).toBe(true);
|
|
66
|
+
// Invalid cases
|
|
67
|
+
expect(schema.validate({ email: 'not-an-email' })).toBe(false);
|
|
68
|
+
expect(schema.validate({ email: 'missing@domain' })).toBe(false);
|
|
69
|
+
expect(schema.validate({ email: '@domain.com' })).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
it('should validate string length constraints', () => {
|
|
72
|
+
const schema = index_1.Schema.from({
|
|
73
|
+
username: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
validations: {
|
|
76
|
+
minLength: 3,
|
|
77
|
+
maxLength: 20
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// Valid cases
|
|
82
|
+
expect(schema.validate({ username: 'abc' })).toBe(true);
|
|
83
|
+
expect(schema.validate({ username: 'valid_username' })).toBe(true);
|
|
84
|
+
expect(schema.validate({ username: 'a'.repeat(20) })).toBe(true);
|
|
85
|
+
// Invalid cases
|
|
86
|
+
expect(schema.validate({ username: 'ab' })).toBe(false);
|
|
87
|
+
expect(schema.validate({ username: 'a'.repeat(21) })).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
it('should validate string regex pattern', () => {
|
|
90
|
+
const schema = index_1.Schema.from({
|
|
91
|
+
username: {
|
|
92
|
+
type: 'string',
|
|
93
|
+
validations: {
|
|
94
|
+
regex: '^[a-zA-Z0-9_]+$'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
// Valid cases
|
|
99
|
+
expect(schema.validate({ username: 'john_doe123' })).toBe(true);
|
|
100
|
+
expect(schema.validate({ username: 'User123' })).toBe(true);
|
|
101
|
+
expect(schema.validate({ username: '123456' })).toBe(true);
|
|
102
|
+
// Invalid cases
|
|
103
|
+
expect(schema.validate({ username: 'john-doe' })).toBe(false);
|
|
104
|
+
expect(schema.validate({ username: 'user@123' })).toBe(false);
|
|
105
|
+
expect(schema.validate({ username: 'user name' })).toBe(false);
|
|
106
|
+
});
|
|
107
|
+
it('should correctly describe string validations', () => {
|
|
108
|
+
const schema = index_1.Schema.from({
|
|
109
|
+
email: {
|
|
110
|
+
type: 'string',
|
|
111
|
+
validations: {
|
|
112
|
+
email: true,
|
|
113
|
+
minLength: 5,
|
|
114
|
+
maxLength: 100
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const description = schema.describe();
|
|
119
|
+
expect(description.email).toEqual({
|
|
120
|
+
type: 'string',
|
|
121
|
+
validations: {
|
|
122
|
+
email: true,
|
|
123
|
+
minLength: 5,
|
|
124
|
+
maxLength: 100
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
it('should correctly describe string regex validation', () => {
|
|
129
|
+
const schema = index_1.Schema.from({
|
|
130
|
+
username: {
|
|
131
|
+
type: 'string',
|
|
132
|
+
validations: {
|
|
133
|
+
regex: '^[a-zA-Z0-9_]+$'
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
const description = schema.describe();
|
|
138
|
+
expect(description.username).toEqual({
|
|
139
|
+
type: 'string',
|
|
140
|
+
validations: {
|
|
141
|
+
regex: '^[a-zA-Z0-9_]+$'
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
it('should handle regex with other string validations', () => {
|
|
146
|
+
const schema = index_1.Schema.from({
|
|
147
|
+
username: {
|
|
148
|
+
type: 'string',
|
|
149
|
+
validations: {
|
|
150
|
+
regex: '^[a-zA-Z0-9_]+$',
|
|
151
|
+
minLength: 3,
|
|
152
|
+
maxLength: 20
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
// Valid cases
|
|
157
|
+
expect(schema.validate({ username: 'john_doe123' })).toBe(true);
|
|
158
|
+
expect(schema.validate({ username: 'User123' })).toBe(true);
|
|
159
|
+
// Invalid cases - regex
|
|
160
|
+
expect(schema.validate({ username: 'john-doe' })).toBe(false);
|
|
161
|
+
expect(schema.validate({ username: 'user@123' })).toBe(false);
|
|
162
|
+
// Invalid cases - length
|
|
163
|
+
expect(schema.validate({ username: 'ab' })).toBe(false);
|
|
164
|
+
expect(schema.validate({ username: 'a'.repeat(21) })).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
it('should handle regex patterns containing forward slashes', () => {
|
|
167
|
+
const schema = index_1.Schema.from({
|
|
168
|
+
path: {
|
|
169
|
+
type: 'string',
|
|
170
|
+
validations: {
|
|
171
|
+
regex: '^/api/v[0-9]+/users/[0-9]+$'
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
// Valid cases
|
|
176
|
+
expect(schema.validate({ path: '/api/v1/users/123' })).toBe(true);
|
|
177
|
+
expect(schema.validate({ path: '/api/v2/users/456' })).toBe(true);
|
|
178
|
+
expect(schema.validate({ path: '/api/v10/users/789' })).toBe(true);
|
|
179
|
+
// Invalid cases
|
|
180
|
+
expect(schema.validate({ path: 'api/v1/users/123' })).toBe(false);
|
|
181
|
+
expect(schema.validate({ path: '/api/v1/users' })).toBe(false);
|
|
182
|
+
expect(schema.validate({ path: '/api/v1/users/abc' })).toBe(false);
|
|
183
|
+
// Verify the description preserves the slashes within the pattern
|
|
184
|
+
const description = schema.describe();
|
|
185
|
+
expect(description.path).toEqual({
|
|
186
|
+
type: 'string',
|
|
187
|
+
validations: {
|
|
188
|
+
regex: '^/api/v[0-9]+/users/[0-9]+$'
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
it('should handle round trip of regex patterns containing forward slashes', () => {
|
|
193
|
+
const schema = index_1.Schema.from({
|
|
194
|
+
path: {
|
|
195
|
+
type: 'string',
|
|
196
|
+
validations: {
|
|
197
|
+
regex: '^/api/v[0-9]+/users/[0-9]+$'
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
const description = schema.describe();
|
|
202
|
+
const cloneSchema = index_1.Schema.from(description);
|
|
203
|
+
// Valid cases
|
|
204
|
+
expect(cloneSchema.validate({ path: '/api/v1/users/123' })).toBe(true);
|
|
205
|
+
expect(cloneSchema.validate({ path: '/api/v2/users/456' })).toBe(true);
|
|
206
|
+
expect(cloneSchema.validate({ path: '/api/v10/users/789' })).toBe(true);
|
|
207
|
+
// Invalid cases
|
|
208
|
+
expect(cloneSchema.validate({ path: 'api/v1/users/123' })).toBe(false);
|
|
209
|
+
expect(cloneSchema.validate({ path: '/api/v1/users' })).toBe(false);
|
|
210
|
+
expect(cloneSchema.validate({ path: '/api/v1/users/abc' })).toBe(false);
|
|
211
|
+
// Verify the description preserves the slashes within the pattern
|
|
212
|
+
const cloneDescription = cloneSchema.describe();
|
|
213
|
+
expect(cloneDescription).toMatchObject(description);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
describe('Schema Roundtrip', () => {
|
|
217
|
+
it('should maintain custom validations through describe/from cycle', () => {
|
|
218
|
+
const originalSchema = index_1.Schema.from({
|
|
219
|
+
age: {
|
|
220
|
+
type: 'number',
|
|
221
|
+
validations: {
|
|
222
|
+
min: 18,
|
|
223
|
+
max: 100
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
email: {
|
|
227
|
+
type: 'string',
|
|
228
|
+
validations: {
|
|
229
|
+
email: true
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
username: {
|
|
233
|
+
type: 'string',
|
|
234
|
+
validations: {
|
|
235
|
+
regex: '^[a-zA-Z0-9_]+$',
|
|
236
|
+
minLength: 3,
|
|
237
|
+
maxLength: 20
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
const description = originalSchema.describe();
|
|
242
|
+
const reconstructedSchema = index_1.Schema.from(description);
|
|
243
|
+
// Both schemas should validate the same data
|
|
244
|
+
const validData = {
|
|
245
|
+
age: 25,
|
|
246
|
+
email: 'user@example.com',
|
|
247
|
+
username: 'john_doe123'
|
|
248
|
+
};
|
|
249
|
+
const invalidData = {
|
|
250
|
+
age: 17,
|
|
251
|
+
email: 'not-an-email',
|
|
252
|
+
username: 'john-doe'
|
|
253
|
+
};
|
|
254
|
+
expect(originalSchema.validate(validData)).toBe(true);
|
|
255
|
+
expect(reconstructedSchema.validate(validData)).toBe(true);
|
|
256
|
+
expect(originalSchema.validate(invalidData)).toBe(false);
|
|
257
|
+
expect(reconstructedSchema.validate(invalidData)).toBe(false);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../index");
|
|
4
|
+
describe('Schema single date field', () => {
|
|
5
|
+
const schema = index_1.Schema.from({
|
|
6
|
+
createdAt: { type: 'date' }
|
|
7
|
+
});
|
|
8
|
+
it('should validate a valid date', () => {
|
|
9
|
+
const data = {
|
|
10
|
+
createdAt: new Date()
|
|
11
|
+
};
|
|
12
|
+
expect(schema.validate(data)).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
it('should validate a valid date string', () => {
|
|
15
|
+
const data = {
|
|
16
|
+
createdAt: new Date('2024-03-20T12:00:00Z')
|
|
17
|
+
};
|
|
18
|
+
expect(schema.validate(data)).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it('should reject invalid date', () => {
|
|
21
|
+
const data = {
|
|
22
|
+
createdAt: 'invalid-date'
|
|
23
|
+
};
|
|
24
|
+
expect(schema.validate(data)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
it('should reject non-date value', () => {
|
|
27
|
+
const data = {
|
|
28
|
+
createdAt: 123
|
|
29
|
+
};
|
|
30
|
+
expect(schema.validate(data)).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('Schema array of dates', () => {
|
|
34
|
+
const schema = index_1.Schema.from({
|
|
35
|
+
timestamps: { type: 'array', items: { type: 'date' } }
|
|
36
|
+
});
|
|
37
|
+
it('should validate an array of valid dates', () => {
|
|
38
|
+
const data = {
|
|
39
|
+
timestamps: [new Date(), new Date()]
|
|
40
|
+
};
|
|
41
|
+
expect(schema.validate(data)).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
it('should validate an array of valid date strings', () => {
|
|
44
|
+
const data = {
|
|
45
|
+
timestamps: [new Date('2024-03-20T12:00:00Z'), new Date('2024-03-21T12:00:00Z')]
|
|
46
|
+
};
|
|
47
|
+
expect(schema.validate(data)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
it('should reject array with invalid date', () => {
|
|
50
|
+
const data = {
|
|
51
|
+
timestamps: [new Date(), 'invalid-date']
|
|
52
|
+
};
|
|
53
|
+
expect(schema.validate(data)).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it('should reject non-array value', () => {
|
|
56
|
+
const data = {
|
|
57
|
+
timestamps: 'not-an-array'
|
|
58
|
+
};
|
|
59
|
+
expect(schema.validate(data)).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('Dates Schema description', () => {
|
|
63
|
+
it('should correctly describe date fields', () => {
|
|
64
|
+
const schema = new index_1.Schema({
|
|
65
|
+
createdAt: index_1.Schema.date(),
|
|
66
|
+
timestamps: index_1.Schema.array(index_1.Schema.date())
|
|
67
|
+
});
|
|
68
|
+
const description = schema.describe();
|
|
69
|
+
expect(description).toEqual({
|
|
70
|
+
createdAt: { type: 'date' },
|
|
71
|
+
timestamps: { type: 'array', items: { type: 'date' } }
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const index_1 = __importDefault(require("../index"));
|
|
7
|
+
describe('Schema basic types', () => {
|
|
8
|
+
it('should validate a string', () => {
|
|
9
|
+
const schema = new index_1.default({
|
|
10
|
+
name: index_1.default.string(),
|
|
11
|
+
});
|
|
12
|
+
const result = schema.validate({
|
|
13
|
+
name: 'World',
|
|
14
|
+
});
|
|
15
|
+
expect(result).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it('should validate a string and number', () => {
|
|
18
|
+
const schema = new index_1.default({
|
|
19
|
+
name: index_1.default.string(),
|
|
20
|
+
age: index_1.default.number(),
|
|
21
|
+
});
|
|
22
|
+
const result = schema.validate({
|
|
23
|
+
name: 'World',
|
|
24
|
+
age: 20,
|
|
25
|
+
});
|
|
26
|
+
expect(result).toEqual(true);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('Schema description', () => {
|
|
30
|
+
it('should describe a string', () => {
|
|
31
|
+
const schema = new index_1.default({
|
|
32
|
+
name: index_1.default.string(),
|
|
33
|
+
});
|
|
34
|
+
const result = schema.describe();
|
|
35
|
+
expect(result).toEqual({
|
|
36
|
+
name: { type: 'string' },
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
it('should describe a string and number', () => {
|
|
40
|
+
const schema = new index_1.default({
|
|
41
|
+
name: index_1.default.string(),
|
|
42
|
+
age: index_1.default.number(),
|
|
43
|
+
});
|
|
44
|
+
const result = schema.describe();
|
|
45
|
+
expect(result).toEqual({
|
|
46
|
+
name: { type: 'string' },
|
|
47
|
+
age: { type: 'number' },
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('Schema hydrate', () => {
|
|
52
|
+
it('should describe a string', () => {
|
|
53
|
+
const description = {
|
|
54
|
+
name: { type: 'string' },
|
|
55
|
+
age: { type: 'number' },
|
|
56
|
+
};
|
|
57
|
+
const schema = index_1.default.from(description);
|
|
58
|
+
const result = schema.describe();
|
|
59
|
+
expect(result).toEqual({
|
|
60
|
+
name: { type: 'string' },
|
|
61
|
+
age: { type: 'number' },
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('Schema validation errors', () => {
|
|
66
|
+
it('should reject invalid string type', () => {
|
|
67
|
+
const schema = new index_1.default({
|
|
68
|
+
name: index_1.default.string(),
|
|
69
|
+
});
|
|
70
|
+
const result = schema.validate({
|
|
71
|
+
name: 123, // number instead of string
|
|
72
|
+
});
|
|
73
|
+
expect(result).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
it('should reject invalid number type', () => {
|
|
76
|
+
const schema = new index_1.default({
|
|
77
|
+
age: index_1.default.number(),
|
|
78
|
+
});
|
|
79
|
+
const result = schema.validate({
|
|
80
|
+
age: 'not a number',
|
|
81
|
+
});
|
|
82
|
+
expect(result).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
// describe('Schema optional fields', () => {
|
|
86
|
+
// it('should validate with missing optional field', () => {
|
|
87
|
+
// const schema = new Schema({
|
|
88
|
+
// name: Schema.string(),
|
|
89
|
+
// age: Schema.number().optional(),
|
|
90
|
+
// })
|
|
91
|
+
// const result = schema.validate({
|
|
92
|
+
// name: 'World',
|
|
93
|
+
// })
|
|
94
|
+
// expect(result).toBe(true)
|
|
95
|
+
// })
|
|
96
|
+
// })
|
|
97
|
+
describe('Schema arrays', () => {
|
|
98
|
+
it('should validate array of strings', () => {
|
|
99
|
+
const schema = new index_1.default({
|
|
100
|
+
tags: index_1.default.array(index_1.default.string()),
|
|
101
|
+
});
|
|
102
|
+
const result = schema.validate({
|
|
103
|
+
tags: ['tag1', 'tag2', 'tag3'],
|
|
104
|
+
});
|
|
105
|
+
expect(result).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
it('should validate array of numbers', () => {
|
|
108
|
+
const schema = new index_1.default({
|
|
109
|
+
scores: index_1.default.array(index_1.default.number()),
|
|
110
|
+
});
|
|
111
|
+
const result = schema.validate({
|
|
112
|
+
scores: [1, 2, 3, 4, 5],
|
|
113
|
+
});
|
|
114
|
+
expect(result).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
it('should reject invalid array types', () => {
|
|
117
|
+
const schema = new index_1.default({
|
|
118
|
+
tags: index_1.default.array(index_1.default.string()),
|
|
119
|
+
});
|
|
120
|
+
const result = schema.validate({
|
|
121
|
+
tags: ['tag1', 123, 'tag3'], // mixed types
|
|
122
|
+
});
|
|
123
|
+
expect(result).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
it('should validate empty arrays', () => {
|
|
126
|
+
const schema = new index_1.default({
|
|
127
|
+
tags: index_1.default.array(index_1.default.string()),
|
|
128
|
+
});
|
|
129
|
+
const result = schema.validate({
|
|
130
|
+
tags: [],
|
|
131
|
+
});
|
|
132
|
+
expect(result).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
it('should describe array schema', () => {
|
|
135
|
+
const schema = new index_1.default({
|
|
136
|
+
tags: index_1.default.array(index_1.default.string()),
|
|
137
|
+
scores: index_1.default.array(index_1.default.number()),
|
|
138
|
+
});
|
|
139
|
+
const result = schema.describe();
|
|
140
|
+
expect(result).toEqual({
|
|
141
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
142
|
+
scores: { type: 'array', items: { type: 'number' } },
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
it('should hydrate array schema from description', () => {
|
|
146
|
+
const description = {
|
|
147
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
148
|
+
scores: { type: 'array', items: { type: 'number' } },
|
|
149
|
+
};
|
|
150
|
+
const schema = index_1.default.from(description);
|
|
151
|
+
const result = schema.describe();
|
|
152
|
+
expect(result).toEqual({
|
|
153
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
154
|
+
scores: { type: 'array', items: { type: 'number' } },
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('Schema custom validation', () => {
|
|
159
|
+
it('should validate with custom rules', () => {
|
|
160
|
+
const schema = new index_1.default({
|
|
161
|
+
age: index_1.default.number().min(0).max(120),
|
|
162
|
+
email: index_1.default.string().email(),
|
|
163
|
+
});
|
|
164
|
+
const result = schema.validate({
|
|
165
|
+
age: 25,
|
|
166
|
+
email: 'test@example.com',
|
|
167
|
+
});
|
|
168
|
+
expect(result).toBe(true);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const index_1 = require("../index");
|
|
4
|
+
describe('Schema Custom Validations', () => {
|
|
5
|
+
describe('Optional Fields', () => {
|
|
6
|
+
it('should handle optional string fields', () => {
|
|
7
|
+
const schema = index_1.Schema.from({
|
|
8
|
+
name: { type: 'string', optional: true },
|
|
9
|
+
age: { type: 'number' }
|
|
10
|
+
});
|
|
11
|
+
// Valid with optional field present
|
|
12
|
+
expect(schema.validate({ name: 'John', age: 30 })).toBe(true);
|
|
13
|
+
// Valid with optional field missing
|
|
14
|
+
expect(schema.validate({ age: 30 })).toBe(true);
|
|
15
|
+
// Invalid - missing required field
|
|
16
|
+
expect(schema.validate({ name: 'John' })).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
it('should handle optional array fields', () => {
|
|
19
|
+
const schema = index_1.Schema.from({
|
|
20
|
+
tags: { type: 'array', items: { type: 'string' }, optional: true },
|
|
21
|
+
scores: { type: 'array', items: { type: 'number' } }
|
|
22
|
+
});
|
|
23
|
+
// Valid with optional array present
|
|
24
|
+
expect(schema.validate({ tags: ['tag1', 'tag2'], scores: [1, 2, 3] })).toBe(true);
|
|
25
|
+
// Valid with optional array missing
|
|
26
|
+
expect(schema.validate({ scores: [1, 2, 3] })).toBe(true);
|
|
27
|
+
// Invalid - missing required array
|
|
28
|
+
expect(schema.validate({ tags: ['tag1'] })).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
it('should correctly describe optional fields', () => {
|
|
31
|
+
const schema = index_1.Schema.from({
|
|
32
|
+
name: { type: 'string', optional: true },
|
|
33
|
+
age: { type: 'number' },
|
|
34
|
+
tags: { type: 'array', items: { type: 'string' }, optional: true }
|
|
35
|
+
});
|
|
36
|
+
const description = schema.describe();
|
|
37
|
+
expect(description.name).toEqual({ type: 'string', optional: true });
|
|
38
|
+
expect(description.age).toEqual({ type: 'number' });
|
|
39
|
+
expect(description.tags).toEqual({ type: 'array', items: { type: 'string' }, optional: true });
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe('Schema Roundtrip', () => {
|
|
43
|
+
it('should maintain optional fields through describe/from cycle', () => {
|
|
44
|
+
const originalSchema = index_1.Schema.from({
|
|
45
|
+
name: { type: 'string', optional: true },
|
|
46
|
+
age: { type: 'number' },
|
|
47
|
+
tags: { type: 'array', items: { type: 'string' }, optional: true }
|
|
48
|
+
});
|
|
49
|
+
const description = originalSchema.describe();
|
|
50
|
+
const reconstructedSchema = index_1.Schema.from(description);
|
|
51
|
+
// Both schemas should validate the same data
|
|
52
|
+
const validData = { name: 'John', age: 30, tags: ['tag1'] };
|
|
53
|
+
const dataWithoutOptional = { age: 30 };
|
|
54
|
+
expect(originalSchema.validate(validData)).toBe(true);
|
|
55
|
+
expect(reconstructedSchema.validate(validData)).toBe(true);
|
|
56
|
+
expect(originalSchema.validate(dataWithoutOptional)).toBe(true);
|
|
57
|
+
expect(reconstructedSchema.validate(dataWithoutOptional)).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|