@objectql/core 4.0.2 → 4.0.3
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/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +18 -0
- package/README.md +4 -4
- package/dist/app.d.ts +9 -6
- package/dist/app.js +151 -29
- package/dist/app.js.map +1 -1
- package/dist/index.d.ts +6 -9
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/optimizations/CompiledHookManager.d.ts +55 -0
- package/dist/optimizations/CompiledHookManager.js +164 -0
- package/dist/optimizations/CompiledHookManager.js.map +1 -0
- package/dist/optimizations/DependencyGraph.d.ts +82 -0
- package/dist/optimizations/DependencyGraph.js +211 -0
- package/dist/optimizations/DependencyGraph.js.map +1 -0
- package/dist/optimizations/GlobalConnectionPool.d.ts +89 -0
- package/dist/optimizations/GlobalConnectionPool.js +193 -0
- package/dist/optimizations/GlobalConnectionPool.js.map +1 -0
- package/dist/optimizations/LazyMetadataLoader.d.ts +75 -0
- package/dist/optimizations/LazyMetadataLoader.js +149 -0
- package/dist/optimizations/LazyMetadataLoader.js.map +1 -0
- package/dist/optimizations/OptimizedMetadataRegistry.d.ts +26 -0
- package/dist/optimizations/OptimizedMetadataRegistry.js +117 -0
- package/dist/optimizations/OptimizedMetadataRegistry.js.map +1 -0
- package/dist/optimizations/OptimizedValidationEngine.d.ts +73 -0
- package/dist/optimizations/OptimizedValidationEngine.js +141 -0
- package/dist/optimizations/OptimizedValidationEngine.js.map +1 -0
- package/dist/optimizations/QueryCompiler.d.ts +51 -0
- package/dist/optimizations/QueryCompiler.js +216 -0
- package/dist/optimizations/QueryCompiler.js.map +1 -0
- package/dist/optimizations/SQLQueryOptimizer.d.ts +96 -0
- package/dist/optimizations/SQLQueryOptimizer.js +265 -0
- package/dist/optimizations/SQLQueryOptimizer.js.map +1 -0
- package/dist/optimizations/index.d.ts +32 -0
- package/dist/optimizations/index.js +44 -0
- package/dist/optimizations/index.js.map +1 -0
- package/dist/plugin.d.ts +6 -7
- package/dist/plugin.js +39 -22
- package/dist/plugin.js.map +1 -1
- package/dist/query/query-analyzer.js.map +1 -1
- package/dist/query/query-builder.d.ts +6 -1
- package/dist/query/query-builder.js +21 -5
- package/dist/query/query-builder.js.map +1 -1
- package/dist/query/query-service.js.map +1 -1
- package/dist/repository.d.ts +2 -0
- package/dist/repository.js +15 -9
- package/dist/repository.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +8 -5
- package/src/app.ts +173 -47
- package/src/index.ts +8 -9
- package/src/optimizations/CompiledHookManager.ts +185 -0
- package/src/optimizations/DependencyGraph.ts +255 -0
- package/src/optimizations/GlobalConnectionPool.ts +251 -0
- package/src/optimizations/LazyMetadataLoader.ts +180 -0
- package/src/optimizations/OptimizedMetadataRegistry.ts +132 -0
- package/src/optimizations/OptimizedValidationEngine.ts +172 -0
- package/src/optimizations/QueryCompiler.ts +242 -0
- package/src/optimizations/SQLQueryOptimizer.ts +329 -0
- package/src/optimizations/index.ts +34 -0
- package/src/plugin.ts +51 -28
- package/src/query/query-analyzer.ts +1 -1
- package/src/query/query-builder.ts +21 -7
- package/src/query/query-service.ts +1 -1
- package/src/repository.ts +25 -13
- package/test/__mocks__/@objectstack/runtime.ts +8 -8
- package/test/app.test.ts +9 -7
- package/test/optimizations.test.ts +440 -0
- package/test/plugin-integration.test.ts +30 -19
- package/tsconfig.json +4 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/ai-agent.d.ts +0 -176
- package/dist/ai-agent.js +0 -722
- package/dist/ai-agent.js.map +0 -1
- package/dist/formula-engine.d.ts +0 -102
- package/dist/formula-engine.js +0 -433
- package/dist/formula-engine.js.map +0 -1
- package/dist/formula-plugin.d.ts +0 -52
- package/dist/formula-plugin.js +0 -107
- package/dist/formula-plugin.js.map +0 -1
- package/dist/validator-plugin.d.ts +0 -56
- package/dist/validator-plugin.js +0 -106
- package/dist/validator-plugin.js.map +0 -1
- package/dist/validator.d.ts +0 -80
- package/dist/validator.js +0 -625
- package/dist/validator.js.map +0 -1
- package/src/ai-agent.ts +0 -868
- package/src/formula-engine.ts +0 -572
- package/src/formula-plugin.ts +0 -141
- package/src/validator-plugin.ts +0 -140
- package/src/validator.ts +0 -743
- package/test/formula-engine.test.ts +0 -725
- package/test/formula-integration.test.ts +0 -286
- package/test/formula-plugin.test.ts +0 -197
- package/test/formula-spec-compliance.test.ts +0 -258
- package/test/validation-spec-compliance.test.ts +0 -440
- package/test/validator-plugin.test.ts +0 -126
- package/test/validator.test.ts +0 -440
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectQL Validator Plugin Tests
|
|
3
|
-
* Copyright (c) 2026-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { ValidatorPlugin } from '../src/validator-plugin';
|
|
10
|
-
import { ObjectStackKernel } from '@objectql/runtime';
|
|
11
|
-
|
|
12
|
-
describe('ValidatorPlugin', () => {
|
|
13
|
-
let plugin: ValidatorPlugin;
|
|
14
|
-
let mockKernel: any;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
// Create a mock kernel with middleware support
|
|
18
|
-
mockKernel = {
|
|
19
|
-
use: jest.fn(),
|
|
20
|
-
};
|
|
21
|
-
plugin = new ValidatorPlugin();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe('Plugin Metadata', () => {
|
|
25
|
-
it('should have correct name and version', () => {
|
|
26
|
-
expect(plugin.name).toBe('@objectql/validator');
|
|
27
|
-
expect(plugin.version).toBe('4.0.0');
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe('Constructor', () => {
|
|
32
|
-
it('should create plugin with default config', () => {
|
|
33
|
-
const defaultPlugin = new ValidatorPlugin();
|
|
34
|
-
expect(defaultPlugin).toBeDefined();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should create plugin with custom config', () => {
|
|
38
|
-
const customPlugin = new ValidatorPlugin({
|
|
39
|
-
language: 'zh-CN',
|
|
40
|
-
enableQueryValidation: false,
|
|
41
|
-
enableMutationValidation: true,
|
|
42
|
-
});
|
|
43
|
-
expect(customPlugin).toBeDefined();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should accept language options', () => {
|
|
47
|
-
const customPlugin = new ValidatorPlugin({
|
|
48
|
-
language: 'fr',
|
|
49
|
-
languageFallback: ['en', 'zh-CN'],
|
|
50
|
-
});
|
|
51
|
-
expect(customPlugin).toBeDefined();
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe('Installation', () => {
|
|
56
|
-
it('should install successfully with mock kernel', async () => {
|
|
57
|
-
const ctx = { engine: mockKernel };
|
|
58
|
-
await plugin.install(ctx);
|
|
59
|
-
|
|
60
|
-
// Verify that middleware hooks were registered
|
|
61
|
-
expect(mockKernel.use).toHaveBeenCalled();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should register query validation when enabled', async () => {
|
|
65
|
-
const pluginWithQuery = new ValidatorPlugin({ enableQueryValidation: true });
|
|
66
|
-
const ctx = { engine: mockKernel };
|
|
67
|
-
|
|
68
|
-
await pluginWithQuery.install(ctx);
|
|
69
|
-
|
|
70
|
-
// Check that use was called (for query validation)
|
|
71
|
-
expect(mockKernel.use).toHaveBeenCalledWith('beforeQuery', expect.any(Function));
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should register mutation validation when enabled', async () => {
|
|
75
|
-
const pluginWithMutation = new ValidatorPlugin({ enableMutationValidation: true });
|
|
76
|
-
const ctx = { engine: mockKernel };
|
|
77
|
-
|
|
78
|
-
await pluginWithMutation.install(ctx);
|
|
79
|
-
|
|
80
|
-
// Check that use was called (for mutation validation)
|
|
81
|
-
expect(mockKernel.use).toHaveBeenCalledWith('beforeMutation', expect.any(Function));
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should not register query validation when disabled', async () => {
|
|
85
|
-
const pluginNoQuery = new ValidatorPlugin({ enableQueryValidation: false });
|
|
86
|
-
const ctx = { engine: mockKernel };
|
|
87
|
-
|
|
88
|
-
await pluginNoQuery.install(ctx);
|
|
89
|
-
|
|
90
|
-
// Should not have registered beforeQuery hook
|
|
91
|
-
const beforeQueryCalls = mockKernel.use.mock.calls.filter(
|
|
92
|
-
(call: any[]) => call[0] === 'beforeQuery'
|
|
93
|
-
);
|
|
94
|
-
expect(beforeQueryCalls.length).toBe(0);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should not register mutation validation when disabled', async () => {
|
|
98
|
-
const pluginNoMutation = new ValidatorPlugin({ enableMutationValidation: false });
|
|
99
|
-
const ctx = { engine: mockKernel };
|
|
100
|
-
|
|
101
|
-
await pluginNoMutation.install(ctx);
|
|
102
|
-
|
|
103
|
-
// Should not have registered beforeMutation hook
|
|
104
|
-
const beforeMutationCalls = mockKernel.use.mock.calls.filter(
|
|
105
|
-
(call: any[]) => call[0] === 'beforeMutation'
|
|
106
|
-
);
|
|
107
|
-
expect(beforeMutationCalls.length).toBe(0);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should handle kernel without middleware support', async () => {
|
|
111
|
-
const kernelNoMiddleware = {};
|
|
112
|
-
const ctx = { engine: kernelNoMiddleware };
|
|
113
|
-
|
|
114
|
-
// Should not throw error
|
|
115
|
-
await expect(plugin.install(ctx)).resolves.not.toThrow();
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe('Validator Access', () => {
|
|
120
|
-
it('should expose validator instance', () => {
|
|
121
|
-
const validator = plugin.getValidator();
|
|
122
|
-
expect(validator).toBeDefined();
|
|
123
|
-
expect(typeof validator.validate).toBe('function');
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
});
|
package/test/validator.test.ts
DELETED
|
@@ -1,440 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectQL
|
|
3
|
-
* Copyright (c) 2026-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { Validator } from '../src/validator';
|
|
10
|
-
import {
|
|
11
|
-
ValidationContext,
|
|
12
|
-
CrossFieldValidationRule,
|
|
13
|
-
StateMachineValidationRule,
|
|
14
|
-
UniquenessValidationRule,
|
|
15
|
-
BusinessRuleValidationRule,
|
|
16
|
-
CustomValidationRule,
|
|
17
|
-
FieldConfig,
|
|
18
|
-
} from '@objectql/types';
|
|
19
|
-
|
|
20
|
-
describe('Validator', () => {
|
|
21
|
-
let validator: Validator;
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
validator = new Validator();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('Constructor', () => {
|
|
28
|
-
it('should create validator with default options', () => {
|
|
29
|
-
expect(validator).toBeDefined();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should accept custom language option', () => {
|
|
33
|
-
const customValidator = new Validator({ language: 'zh-CN' });
|
|
34
|
-
expect(customValidator).toBeDefined();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should accept language fallback option', () => {
|
|
38
|
-
const customValidator = new Validator({
|
|
39
|
-
language: 'fr',
|
|
40
|
-
languageFallback: ['en', 'zh-CN']
|
|
41
|
-
});
|
|
42
|
-
expect(customValidator).toBeDefined();
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('Field Validation', () => {
|
|
47
|
-
describe('Required Fields', () => {
|
|
48
|
-
it('should validate required field with value', async () => {
|
|
49
|
-
const fieldConfig: FieldConfig = {
|
|
50
|
-
type: 'text',
|
|
51
|
-
required: true,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const context: ValidationContext = {
|
|
55
|
-
operation: 'create',
|
|
56
|
-
data: { name: 'John' },
|
|
57
|
-
objectName: 'user',
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const results = await validator.validateField('name', fieldConfig, 'John', context);
|
|
61
|
-
expect(results).toEqual([]);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should fail validation for missing required field', async () => {
|
|
65
|
-
const fieldConfig: FieldConfig = {
|
|
66
|
-
type: 'text',
|
|
67
|
-
required: true,
|
|
68
|
-
label: 'Name',
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const context: ValidationContext = {
|
|
72
|
-
operation: 'create',
|
|
73
|
-
data: {},
|
|
74
|
-
objectName: 'user',
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const results = await validator.validateField('name', fieldConfig, undefined, context);
|
|
78
|
-
expect(results.length).toBeGreaterThan(0);
|
|
79
|
-
expect(results[0].valid).toBe(false);
|
|
80
|
-
expect(results[0].message).toContain('Name is required');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should fail validation for empty string in required field', async () => {
|
|
84
|
-
const fieldConfig: FieldConfig = {
|
|
85
|
-
type: 'text',
|
|
86
|
-
required: true,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const context: ValidationContext = {
|
|
90
|
-
operation: 'create',
|
|
91
|
-
data: { name: '' },
|
|
92
|
-
objectName: 'user',
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const results = await validator.validateField('name', fieldConfig, '', context);
|
|
96
|
-
expect(results.length).toBeGreaterThan(0);
|
|
97
|
-
expect(results[0].valid).toBe(false);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should fail validation for null in required field', async () => {
|
|
101
|
-
const fieldConfig: FieldConfig = {
|
|
102
|
-
type: 'text',
|
|
103
|
-
required: true,
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const context: ValidationContext = {
|
|
107
|
-
operation: 'create',
|
|
108
|
-
data: { name: null },
|
|
109
|
-
objectName: 'user',
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const results = await validator.validateField('name', fieldConfig, null, context);
|
|
113
|
-
expect(results.length).toBeGreaterThan(0);
|
|
114
|
-
expect(results[0].valid).toBe(false);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
describe('Email Validation', () => {
|
|
119
|
-
it('should validate correct email format', async () => {
|
|
120
|
-
const fieldConfig: FieldConfig = {
|
|
121
|
-
type: 'email',
|
|
122
|
-
validation: { format: 'email' },
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
const context: ValidationContext = {
|
|
126
|
-
operation: 'create',
|
|
127
|
-
data: { email: 'test@example.com' },
|
|
128
|
-
objectName: 'user',
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const results = await validator.validateField('email', fieldConfig, 'test@example.com', context);
|
|
132
|
-
const errors = results.filter(r => !r.valid);
|
|
133
|
-
expect(errors.length).toBe(0);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('should reject invalid email format', async () => {
|
|
137
|
-
const fieldConfig: FieldConfig = {
|
|
138
|
-
type: 'email',
|
|
139
|
-
validation: { format: 'email' },
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const context: ValidationContext = {
|
|
143
|
-
operation: 'create',
|
|
144
|
-
data: { email: 'invalid-email' },
|
|
145
|
-
objectName: 'user',
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const results = await validator.validateField('email', fieldConfig, 'invalid-email', context);
|
|
149
|
-
const errors = results.filter(r => !r.valid);
|
|
150
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should reject email without @', async () => {
|
|
154
|
-
const fieldConfig: FieldConfig = {
|
|
155
|
-
type: 'email',
|
|
156
|
-
validation: { format: 'email' },
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const context: ValidationContext = {
|
|
160
|
-
operation: 'create',
|
|
161
|
-
data: { email: 'testexample.com' },
|
|
162
|
-
objectName: 'user',
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const results = await validator.validateField('email', fieldConfig, 'testexample.com', context);
|
|
166
|
-
const errors = results.filter(r => !r.valid);
|
|
167
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should reject email without domain', async () => {
|
|
171
|
-
const fieldConfig: FieldConfig = {
|
|
172
|
-
type: 'email',
|
|
173
|
-
validation: { format: 'email' },
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const context: ValidationContext = {
|
|
177
|
-
operation: 'create',
|
|
178
|
-
data: { email: 'test@' },
|
|
179
|
-
objectName: 'user',
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const results = await validator.validateField('email', fieldConfig, 'test@', context);
|
|
183
|
-
const errors = results.filter(r => !r.valid);
|
|
184
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
describe('Length Validation', () => {
|
|
189
|
-
it('should validate string within min and max length', async () => {
|
|
190
|
-
const fieldConfig: FieldConfig = {
|
|
191
|
-
type: 'text',
|
|
192
|
-
validation: {
|
|
193
|
-
min_length: 3,
|
|
194
|
-
max_length: 10,
|
|
195
|
-
},
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
const context: ValidationContext = {
|
|
199
|
-
operation: 'create',
|
|
200
|
-
data: { name: 'John' },
|
|
201
|
-
objectName: 'user',
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const results = await validator.validateField('name', fieldConfig, 'John', context);
|
|
205
|
-
const errors = results.filter(r => !r.valid);
|
|
206
|
-
expect(errors.length).toBe(0);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('should reject string shorter than min_length', async () => {
|
|
210
|
-
const fieldConfig: FieldConfig = {
|
|
211
|
-
type: 'text',
|
|
212
|
-
validation: {
|
|
213
|
-
min_length: 5,
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
const context: ValidationContext = {
|
|
218
|
-
operation: 'create',
|
|
219
|
-
data: { name: 'Bob' },
|
|
220
|
-
objectName: 'user',
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
const results = await validator.validateField('name', fieldConfig, 'Bob', context);
|
|
224
|
-
const errors = results.filter(r => !r.valid);
|
|
225
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it('should reject string longer than max_length', async () => {
|
|
229
|
-
const fieldConfig: FieldConfig = {
|
|
230
|
-
type: 'text',
|
|
231
|
-
validation: {
|
|
232
|
-
max_length: 5,
|
|
233
|
-
},
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const context: ValidationContext = {
|
|
237
|
-
operation: 'create',
|
|
238
|
-
data: { name: 'Alexander' },
|
|
239
|
-
objectName: 'user',
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const results = await validator.validateField('name', fieldConfig, 'Alexander', context);
|
|
243
|
-
const errors = results.filter(r => !r.valid);
|
|
244
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
describe('Numeric Validation', () => {
|
|
249
|
-
it('should validate number within min and max range', async () => {
|
|
250
|
-
const fieldConfig: FieldConfig = {
|
|
251
|
-
type: 'number',
|
|
252
|
-
validation: {
|
|
253
|
-
min: 0,
|
|
254
|
-
max: 100,
|
|
255
|
-
},
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
const context: ValidationContext = {
|
|
259
|
-
operation: 'create',
|
|
260
|
-
data: { age: 25 },
|
|
261
|
-
objectName: 'user',
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const results = await validator.validateField('age', fieldConfig, 25, context);
|
|
265
|
-
const errors = results.filter(r => !r.valid);
|
|
266
|
-
expect(errors.length).toBe(0);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it('should reject number below min', async () => {
|
|
270
|
-
const fieldConfig: FieldConfig = {
|
|
271
|
-
type: 'number',
|
|
272
|
-
validation: {
|
|
273
|
-
min: 18,
|
|
274
|
-
},
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const context: ValidationContext = {
|
|
278
|
-
operation: 'create',
|
|
279
|
-
data: { age: 15 },
|
|
280
|
-
objectName: 'user',
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
const results = await validator.validateField('age', fieldConfig, 15, context);
|
|
284
|
-
const errors = results.filter(r => !r.valid);
|
|
285
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('should reject number above max', async () => {
|
|
289
|
-
const fieldConfig: FieldConfig = {
|
|
290
|
-
type: 'number',
|
|
291
|
-
validation: {
|
|
292
|
-
max: 120,
|
|
293
|
-
},
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
const context: ValidationContext = {
|
|
297
|
-
operation: 'create',
|
|
298
|
-
data: { age: 150 },
|
|
299
|
-
objectName: 'user',
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const results = await validator.validateField('age', fieldConfig, 150, context);
|
|
303
|
-
const errors = results.filter(r => !r.valid);
|
|
304
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
305
|
-
});
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
describe('Pattern Validation', () => {
|
|
309
|
-
it('should validate value matching pattern', async () => {
|
|
310
|
-
const fieldConfig: FieldConfig = {
|
|
311
|
-
type: 'text',
|
|
312
|
-
validation: {
|
|
313
|
-
pattern: '^[A-Z][0-9]{3}$', // e.g., A123
|
|
314
|
-
},
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
const context: ValidationContext = {
|
|
318
|
-
operation: 'create',
|
|
319
|
-
data: { code: 'A123' },
|
|
320
|
-
objectName: 'product',
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
const results = await validator.validateField('code', fieldConfig, 'A123', context);
|
|
324
|
-
const errors = results.filter(r => !r.valid);
|
|
325
|
-
expect(errors.length).toBe(0);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
it('should reject value not matching pattern', async () => {
|
|
329
|
-
const fieldConfig: FieldConfig = {
|
|
330
|
-
type: 'text',
|
|
331
|
-
validation: {
|
|
332
|
-
pattern: '^[A-Z][0-9]{3}$',
|
|
333
|
-
},
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
const context: ValidationContext = {
|
|
337
|
-
operation: 'create',
|
|
338
|
-
data: { code: 'a123' },
|
|
339
|
-
objectName: 'product',
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
const results = await validator.validateField('code', fieldConfig, 'a123', context);
|
|
343
|
-
const errors = results.filter(r => !r.valid);
|
|
344
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
345
|
-
});
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
describe('URL Validation', () => {
|
|
349
|
-
it('should validate correct URL format', async () => {
|
|
350
|
-
const fieldConfig: FieldConfig = {
|
|
351
|
-
type: 'url',
|
|
352
|
-
validation: { format: 'url' },
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
const context: ValidationContext = {
|
|
356
|
-
operation: 'create',
|
|
357
|
-
data: { website: 'https://example.com' },
|
|
358
|
-
objectName: 'user',
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
const results = await validator.validateField('website', fieldConfig, 'https://example.com', context);
|
|
362
|
-
const errors = results.filter(r => !r.valid);
|
|
363
|
-
expect(errors.length).toBe(0);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it('should reject invalid URL format', async () => {
|
|
367
|
-
const fieldConfig: FieldConfig = {
|
|
368
|
-
type: 'url',
|
|
369
|
-
validation: { format: 'url' },
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
const context: ValidationContext = {
|
|
373
|
-
operation: 'create',
|
|
374
|
-
data: { website: 'not-a-url' },
|
|
375
|
-
objectName: 'user',
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
const results = await validator.validateField('website', fieldConfig, 'not-a-url', context);
|
|
379
|
-
const errors = results.filter(r => !r.valid);
|
|
380
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
describe('Empty Value Handling', () => {
|
|
386
|
-
it('should skip validation for empty optional fields', async () => {
|
|
387
|
-
const fieldConfig: FieldConfig = {
|
|
388
|
-
type: 'text',
|
|
389
|
-
validation: {
|
|
390
|
-
min_length: 5,
|
|
391
|
-
},
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
const context: ValidationContext = {
|
|
395
|
-
operation: 'create',
|
|
396
|
-
data: {},
|
|
397
|
-
objectName: 'user',
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
const results = await validator.validateField('nickname', fieldConfig, undefined, context);
|
|
401
|
-
expect(results.length).toBe(0);
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
it('should skip validation for null optional fields', async () => {
|
|
405
|
-
const fieldConfig: FieldConfig = {
|
|
406
|
-
type: 'text',
|
|
407
|
-
validation: {
|
|
408
|
-
min_length: 5,
|
|
409
|
-
},
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
const context: ValidationContext = {
|
|
413
|
-
operation: 'create',
|
|
414
|
-
data: { nickname: null },
|
|
415
|
-
objectName: 'user',
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
const results = await validator.validateField('nickname', fieldConfig, null, context);
|
|
419
|
-
expect(results.length).toBe(0);
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
it('should skip validation for empty string optional fields', async () => {
|
|
423
|
-
const fieldConfig: FieldConfig = {
|
|
424
|
-
type: 'text',
|
|
425
|
-
validation: {
|
|
426
|
-
min_length: 5,
|
|
427
|
-
},
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
const context: ValidationContext = {
|
|
431
|
-
operation: 'create',
|
|
432
|
-
data: { nickname: '' },
|
|
433
|
-
objectName: 'user',
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
const results = await validator.validateField('nickname', fieldConfig, '', context);
|
|
437
|
-
expect(results.length).toBe(0);
|
|
438
|
-
});
|
|
439
|
-
});
|
|
440
|
-
});
|