@dangao/bun-server 1.7.1 → 1.8.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.
Files changed (116) hide show
  1. package/README.md +129 -21
  2. package/dist/di/decorators.d.ts +37 -0
  3. package/dist/di/decorators.d.ts.map +1 -1
  4. package/dist/di/index.d.ts +1 -1
  5. package/dist/di/index.d.ts.map +1 -1
  6. package/dist/di/module-registry.d.ts +17 -0
  7. package/dist/di/module-registry.d.ts.map +1 -1
  8. package/dist/events/decorators.d.ts +52 -0
  9. package/dist/events/decorators.d.ts.map +1 -0
  10. package/dist/events/event-module.d.ts +97 -0
  11. package/dist/events/event-module.d.ts.map +1 -0
  12. package/dist/events/index.d.ts +5 -0
  13. package/dist/events/index.d.ts.map +1 -0
  14. package/dist/events/service.d.ts +76 -0
  15. package/dist/events/service.d.ts.map +1 -0
  16. package/dist/events/types.d.ts +184 -0
  17. package/dist/events/types.d.ts.map +1 -0
  18. package/dist/index.d.ts +5 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1511 -11
  21. package/dist/security/filter.d.ts +23 -0
  22. package/dist/security/filter.d.ts.map +1 -1
  23. package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
  24. package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
  25. package/dist/security/guards/builtin/index.d.ts +3 -0
  26. package/dist/security/guards/builtin/index.d.ts.map +1 -0
  27. package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
  28. package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
  29. package/dist/security/guards/decorators.d.ts +50 -0
  30. package/dist/security/guards/decorators.d.ts.map +1 -0
  31. package/dist/security/guards/execution-context.d.ts +56 -0
  32. package/dist/security/guards/execution-context.d.ts.map +1 -0
  33. package/dist/security/guards/guard-registry.d.ts +67 -0
  34. package/dist/security/guards/guard-registry.d.ts.map +1 -0
  35. package/dist/security/guards/index.d.ts +7 -0
  36. package/dist/security/guards/index.d.ts.map +1 -0
  37. package/dist/security/guards/reflector.d.ts +57 -0
  38. package/dist/security/guards/reflector.d.ts.map +1 -0
  39. package/dist/security/guards/types.d.ts +126 -0
  40. package/dist/security/guards/types.d.ts.map +1 -0
  41. package/dist/security/index.d.ts +1 -0
  42. package/dist/security/index.d.ts.map +1 -1
  43. package/dist/security/security-module.d.ts +20 -0
  44. package/dist/security/security-module.d.ts.map +1 -1
  45. package/dist/validation/class-validator.d.ts +108 -0
  46. package/dist/validation/class-validator.d.ts.map +1 -0
  47. package/dist/validation/custom-validator.d.ts +130 -0
  48. package/dist/validation/custom-validator.d.ts.map +1 -0
  49. package/dist/validation/errors.d.ts +22 -2
  50. package/dist/validation/errors.d.ts.map +1 -1
  51. package/dist/validation/index.d.ts +7 -1
  52. package/dist/validation/index.d.ts.map +1 -1
  53. package/dist/validation/rules/array.d.ts +33 -0
  54. package/dist/validation/rules/array.d.ts.map +1 -0
  55. package/dist/validation/rules/common.d.ts +90 -0
  56. package/dist/validation/rules/common.d.ts.map +1 -0
  57. package/dist/validation/rules/conditional.d.ts +30 -0
  58. package/dist/validation/rules/conditional.d.ts.map +1 -0
  59. package/dist/validation/rules/index.d.ts +5 -0
  60. package/dist/validation/rules/index.d.ts.map +1 -0
  61. package/dist/validation/rules/object.d.ts +30 -0
  62. package/dist/validation/rules/object.d.ts.map +1 -0
  63. package/dist/validation/types.d.ts +52 -1
  64. package/dist/validation/types.d.ts.map +1 -1
  65. package/docs/events.md +494 -0
  66. package/docs/guards.md +376 -0
  67. package/docs/guide.md +309 -1
  68. package/docs/request-lifecycle.md +444 -0
  69. package/docs/validation.md +407 -0
  70. package/docs/zh/events.md +494 -0
  71. package/docs/zh/guards.md +376 -0
  72. package/docs/zh/guide.md +309 -1
  73. package/docs/zh/request-lifecycle.md +444 -0
  74. package/docs/zh/validation.md +407 -0
  75. package/package.json +1 -1
  76. package/src/di/decorators.ts +46 -0
  77. package/src/di/index.ts +10 -1
  78. package/src/di/module-registry.ts +39 -0
  79. package/src/events/decorators.ts +103 -0
  80. package/src/events/event-module.ts +272 -0
  81. package/src/events/index.ts +32 -0
  82. package/src/events/service.ts +352 -0
  83. package/src/events/types.ts +223 -0
  84. package/src/index.ts +133 -1
  85. package/src/security/filter.ts +88 -8
  86. package/src/security/guards/builtin/auth-guard.ts +68 -0
  87. package/src/security/guards/builtin/index.ts +3 -0
  88. package/src/security/guards/builtin/roles-guard.ts +165 -0
  89. package/src/security/guards/decorators.ts +124 -0
  90. package/src/security/guards/execution-context.ts +152 -0
  91. package/src/security/guards/guard-registry.ts +164 -0
  92. package/src/security/guards/index.ts +7 -0
  93. package/src/security/guards/reflector.ts +99 -0
  94. package/src/security/guards/types.ts +144 -0
  95. package/src/security/index.ts +1 -0
  96. package/src/security/security-module.ts +72 -2
  97. package/src/validation/class-validator.ts +322 -0
  98. package/src/validation/custom-validator.ts +289 -0
  99. package/src/validation/errors.ts +50 -2
  100. package/src/validation/index.ts +103 -1
  101. package/src/validation/rules/array.ts +118 -0
  102. package/src/validation/rules/common.ts +286 -0
  103. package/src/validation/rules/conditional.ts +52 -0
  104. package/src/validation/rules/index.ts +51 -0
  105. package/src/validation/rules/object.ts +86 -0
  106. package/src/validation/types.ts +61 -1
  107. package/tests/di/global-module.test.ts +487 -0
  108. package/tests/events/event-decorators.test.ts +173 -0
  109. package/tests/events/event-emitter.test.ts +373 -0
  110. package/tests/events/event-module.test.ts +373 -0
  111. package/tests/security/guards/guards-integration.test.ts +371 -0
  112. package/tests/security/guards/guards.test.ts +775 -0
  113. package/tests/security/security-module.test.ts +2 -2
  114. package/tests/validation/class-validator.test.ts +349 -0
  115. package/tests/validation/custom-validator.test.ts +335 -0
  116. package/tests/validation/rules.test.ts +543 -0
@@ -0,0 +1,543 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import {
4
+ // 对象规则
5
+ IsObject,
6
+ IsNotEmpty,
7
+ IsNotEmptyObject,
8
+ ValidateNested,
9
+ // 数组规则
10
+ IsArray,
11
+ ArrayMinSize,
12
+ ArrayMaxSize,
13
+ ArrayUnique,
14
+ ArrayContains,
15
+ ArrayNotContains,
16
+ ArrayNotEmpty,
17
+ // 通用规则
18
+ IsBoolean,
19
+ IsInt,
20
+ IsPositive,
21
+ IsNegative,
22
+ Min,
23
+ Max,
24
+ IsDate,
25
+ IsUUID,
26
+ Length,
27
+ MaxLength,
28
+ Matches,
29
+ IsIn,
30
+ IsNotIn,
31
+ IsUrl,
32
+ IsJSON,
33
+ Equals,
34
+ NotEquals,
35
+ IsDefined,
36
+ IsAlphanumeric,
37
+ IsAlpha,
38
+ IsNumberString,
39
+ // 条件规则
40
+ ValidateIf,
41
+ Transform,
42
+ } from '../../src/validation';
43
+
44
+ describe('Object Validation Rules', () => {
45
+ describe('IsObject', () => {
46
+ const rule = IsObject();
47
+
48
+ test('should pass for plain object', () => {
49
+ expect(rule.validate({})).toBe(true);
50
+ expect(rule.validate({ a: 1 })).toBe(true);
51
+ });
52
+
53
+ test('should fail for non-objects', () => {
54
+ expect(rule.validate(null)).toBe(false);
55
+ expect(rule.validate(undefined)).toBe(false);
56
+ expect(rule.validate([])).toBe(false);
57
+ expect(rule.validate('string')).toBe(false);
58
+ expect(rule.validate(123)).toBe(false);
59
+ });
60
+ });
61
+
62
+ describe('IsNotEmpty', () => {
63
+ const rule = IsNotEmpty();
64
+
65
+ test('should pass for non-empty values', () => {
66
+ expect(rule.validate('hello')).toBe(true);
67
+ expect(rule.validate([1])).toBe(true);
68
+ expect(rule.validate({ a: 1 })).toBe(true);
69
+ expect(rule.validate(0)).toBe(true);
70
+ expect(rule.validate(false)).toBe(true);
71
+ });
72
+
73
+ test('should fail for empty values', () => {
74
+ expect(rule.validate(null)).toBe(false);
75
+ expect(rule.validate(undefined)).toBe(false);
76
+ expect(rule.validate('')).toBe(false);
77
+ expect(rule.validate(' ')).toBe(false);
78
+ expect(rule.validate([])).toBe(false);
79
+ expect(rule.validate({})).toBe(false);
80
+ });
81
+ });
82
+
83
+ describe('IsNotEmptyObject', () => {
84
+ const rule = IsNotEmptyObject();
85
+
86
+ test('should pass for non-empty object', () => {
87
+ expect(rule.validate({ a: 1 })).toBe(true);
88
+ expect(rule.validate({ key: 'value' })).toBe(true);
89
+ });
90
+
91
+ test('should fail for empty object and non-objects', () => {
92
+ expect(rule.validate({})).toBe(false);
93
+ expect(rule.validate(null)).toBe(false);
94
+ expect(rule.validate([])).toBe(false);
95
+ expect(rule.validate('string')).toBe(false);
96
+ });
97
+ });
98
+
99
+ describe('ValidateNested', () => {
100
+ test('should return nested rule definition', () => {
101
+ const rule = ValidateNested();
102
+ expect(rule.nested).toBe(true);
103
+ expect(rule.each).toBe(false);
104
+ expect(rule.validate({ a: 1 })).toBe(true);
105
+ expect(rule.validate([])).toBe(false);
106
+ });
107
+
108
+ test('should support each option for arrays', () => {
109
+ const rule = ValidateNested({ each: true });
110
+ expect(rule.nested).toBe(true);
111
+ expect(rule.each).toBe(true);
112
+ expect(rule.validate([{ a: 1 }])).toBe(true);
113
+ expect(rule.validate({ a: 1 })).toBe(false);
114
+ });
115
+ });
116
+ });
117
+
118
+ describe('Array Validation Rules', () => {
119
+ describe('IsArray', () => {
120
+ const rule = IsArray();
121
+
122
+ test('should pass for arrays', () => {
123
+ expect(rule.validate([])).toBe(true);
124
+ expect(rule.validate([1, 2, 3])).toBe(true);
125
+ });
126
+
127
+ test('should fail for non-arrays', () => {
128
+ expect(rule.validate({})).toBe(false);
129
+ expect(rule.validate('string')).toBe(false);
130
+ expect(rule.validate(null)).toBe(false);
131
+ });
132
+ });
133
+
134
+ describe('ArrayMinSize', () => {
135
+ const rule = ArrayMinSize(2);
136
+
137
+ test('should pass for arrays with sufficient length', () => {
138
+ expect(rule.validate([1, 2])).toBe(true);
139
+ expect(rule.validate([1, 2, 3])).toBe(true);
140
+ });
141
+
142
+ test('should fail for arrays with insufficient length', () => {
143
+ expect(rule.validate([])).toBe(false);
144
+ expect(rule.validate([1])).toBe(false);
145
+ });
146
+ });
147
+
148
+ describe('ArrayMaxSize', () => {
149
+ const rule = ArrayMaxSize(3);
150
+
151
+ test('should pass for arrays within limit', () => {
152
+ expect(rule.validate([])).toBe(true);
153
+ expect(rule.validate([1, 2, 3])).toBe(true);
154
+ });
155
+
156
+ test('should fail for arrays exceeding limit', () => {
157
+ expect(rule.validate([1, 2, 3, 4])).toBe(false);
158
+ });
159
+ });
160
+
161
+ describe('ArrayUnique', () => {
162
+ const rule = ArrayUnique();
163
+
164
+ test('should pass for arrays with unique elements', () => {
165
+ expect(rule.validate([1, 2, 3])).toBe(true);
166
+ expect(rule.validate(['a', 'b', 'c'])).toBe(true);
167
+ expect(rule.validate([{ a: 1 }, { a: 2 }])).toBe(true);
168
+ });
169
+
170
+ test('should fail for arrays with duplicates', () => {
171
+ expect(rule.validate([1, 1, 2])).toBe(false);
172
+ expect(rule.validate(['a', 'a'])).toBe(false);
173
+ expect(rule.validate([{ a: 1 }, { a: 1 }])).toBe(false);
174
+ });
175
+ });
176
+
177
+ describe('ArrayContains', () => {
178
+ const rule = ArrayContains([1, 2]);
179
+
180
+ test('should pass when array contains all values', () => {
181
+ expect(rule.validate([1, 2, 3])).toBe(true);
182
+ expect(rule.validate([2, 1])).toBe(true);
183
+ });
184
+
185
+ test('should fail when array does not contain all values', () => {
186
+ expect(rule.validate([1])).toBe(false);
187
+ expect(rule.validate([3, 4])).toBe(false);
188
+ });
189
+ });
190
+
191
+ describe('ArrayNotContains', () => {
192
+ const rule = ArrayNotContains([1, 2]);
193
+
194
+ test('should pass when array does not contain values', () => {
195
+ expect(rule.validate([3, 4, 5])).toBe(true);
196
+ });
197
+
198
+ test('should fail when array contains any of the values', () => {
199
+ expect(rule.validate([1, 3])).toBe(false);
200
+ expect(rule.validate([2, 4])).toBe(false);
201
+ });
202
+ });
203
+
204
+ describe('ArrayNotEmpty', () => {
205
+ const rule = ArrayNotEmpty();
206
+
207
+ test('should pass for non-empty arrays', () => {
208
+ expect(rule.validate([1])).toBe(true);
209
+ });
210
+
211
+ test('should fail for empty arrays', () => {
212
+ expect(rule.validate([])).toBe(false);
213
+ });
214
+ });
215
+ });
216
+
217
+ describe('Common Validation Rules', () => {
218
+ describe('IsBoolean', () => {
219
+ const rule = IsBoolean();
220
+
221
+ test('should pass for booleans', () => {
222
+ expect(rule.validate(true)).toBe(true);
223
+ expect(rule.validate(false)).toBe(true);
224
+ });
225
+
226
+ test('should fail for non-booleans', () => {
227
+ expect(rule.validate(1)).toBe(false);
228
+ expect(rule.validate('true')).toBe(false);
229
+ expect(rule.validate(null)).toBe(false);
230
+ });
231
+ });
232
+
233
+ describe('IsInt', () => {
234
+ const rule = IsInt();
235
+
236
+ test('should pass for integers', () => {
237
+ expect(rule.validate(0)).toBe(true);
238
+ expect(rule.validate(42)).toBe(true);
239
+ expect(rule.validate(-10)).toBe(true);
240
+ });
241
+
242
+ test('should fail for non-integers', () => {
243
+ expect(rule.validate(3.14)).toBe(false);
244
+ expect(rule.validate('42')).toBe(false);
245
+ expect(rule.validate(NaN)).toBe(false);
246
+ });
247
+ });
248
+
249
+ describe('IsPositive', () => {
250
+ const rule = IsPositive();
251
+
252
+ test('should pass for positive numbers', () => {
253
+ expect(rule.validate(1)).toBe(true);
254
+ expect(rule.validate(0.1)).toBe(true);
255
+ });
256
+
257
+ test('should fail for non-positive numbers', () => {
258
+ expect(rule.validate(0)).toBe(false);
259
+ expect(rule.validate(-1)).toBe(false);
260
+ });
261
+ });
262
+
263
+ describe('IsNegative', () => {
264
+ const rule = IsNegative();
265
+
266
+ test('should pass for negative numbers', () => {
267
+ expect(rule.validate(-1)).toBe(true);
268
+ expect(rule.validate(-0.1)).toBe(true);
269
+ });
270
+
271
+ test('should fail for non-negative numbers', () => {
272
+ expect(rule.validate(0)).toBe(false);
273
+ expect(rule.validate(1)).toBe(false);
274
+ });
275
+ });
276
+
277
+ describe('Min', () => {
278
+ const rule = Min(5);
279
+
280
+ test('should pass for numbers >= min', () => {
281
+ expect(rule.validate(5)).toBe(true);
282
+ expect(rule.validate(10)).toBe(true);
283
+ });
284
+
285
+ test('should fail for numbers < min', () => {
286
+ expect(rule.validate(4)).toBe(false);
287
+ expect(rule.validate(-1)).toBe(false);
288
+ });
289
+ });
290
+
291
+ describe('Max', () => {
292
+ const rule = Max(10);
293
+
294
+ test('should pass for numbers <= max', () => {
295
+ expect(rule.validate(10)).toBe(true);
296
+ expect(rule.validate(5)).toBe(true);
297
+ });
298
+
299
+ test('should fail for numbers > max', () => {
300
+ expect(rule.validate(11)).toBe(false);
301
+ });
302
+ });
303
+
304
+ describe('IsDate', () => {
305
+ const rule = IsDate();
306
+
307
+ test('should pass for valid dates', () => {
308
+ expect(rule.validate(new Date())).toBe(true);
309
+ expect(rule.validate('2023-01-01')).toBe(true);
310
+ expect(rule.validate(1672531200000)).toBe(true);
311
+ });
312
+
313
+ test('should fail for invalid dates', () => {
314
+ expect(rule.validate('invalid')).toBe(false);
315
+ expect(rule.validate(new Date('invalid'))).toBe(false);
316
+ expect(rule.validate(null)).toBe(false);
317
+ });
318
+ });
319
+
320
+ describe('IsUUID', () => {
321
+ test('should validate UUID v4', () => {
322
+ const rule = IsUUID('4');
323
+ expect(rule.validate('550e8400-e29b-41d4-a716-446655440000')).toBe(true);
324
+ expect(rule.validate('invalid-uuid')).toBe(false);
325
+ });
326
+
327
+ test('should validate any UUID version', () => {
328
+ const rule = IsUUID('all');
329
+ expect(rule.validate('550e8400-e29b-11d4-a716-446655440000')).toBe(true);
330
+ expect(rule.validate('550e8400-e29b-21d4-a716-446655440000')).toBe(true);
331
+ });
332
+ });
333
+
334
+ describe('Length', () => {
335
+ test('should validate min length', () => {
336
+ const rule = Length(2);
337
+ expect(rule.validate('ab')).toBe(true);
338
+ expect(rule.validate('a')).toBe(false);
339
+ });
340
+
341
+ test('should validate min and max length', () => {
342
+ const rule = Length(2, 5);
343
+ expect(rule.validate('ab')).toBe(true);
344
+ expect(rule.validate('abcde')).toBe(true);
345
+ expect(rule.validate('a')).toBe(false);
346
+ expect(rule.validate('abcdef')).toBe(false);
347
+ });
348
+ });
349
+
350
+ describe('MaxLength', () => {
351
+ const rule = MaxLength(5);
352
+
353
+ test('should pass for strings within limit', () => {
354
+ expect(rule.validate('hello')).toBe(true);
355
+ expect(rule.validate('hi')).toBe(true);
356
+ });
357
+
358
+ test('should fail for strings exceeding limit', () => {
359
+ expect(rule.validate('hello!')).toBe(false);
360
+ });
361
+ });
362
+
363
+ describe('Matches', () => {
364
+ const rule = Matches(/^\d{3}-\d{4}$/);
365
+
366
+ test('should pass for matching strings', () => {
367
+ expect(rule.validate('123-4567')).toBe(true);
368
+ });
369
+
370
+ test('should fail for non-matching strings', () => {
371
+ expect(rule.validate('1234567')).toBe(false);
372
+ expect(rule.validate('abc-defg')).toBe(false);
373
+ });
374
+ });
375
+
376
+ describe('IsIn', () => {
377
+ const rule = IsIn(['a', 'b', 'c']);
378
+
379
+ test('should pass for values in list', () => {
380
+ expect(rule.validate('a')).toBe(true);
381
+ expect(rule.validate('b')).toBe(true);
382
+ });
383
+
384
+ test('should fail for values not in list', () => {
385
+ expect(rule.validate('d')).toBe(false);
386
+ expect(rule.validate(1)).toBe(false);
387
+ });
388
+ });
389
+
390
+ describe('IsNotIn', () => {
391
+ const rule = IsNotIn(['x', 'y', 'z']);
392
+
393
+ test('should pass for values not in list', () => {
394
+ expect(rule.validate('a')).toBe(true);
395
+ });
396
+
397
+ test('should fail for values in list', () => {
398
+ expect(rule.validate('x')).toBe(false);
399
+ });
400
+ });
401
+
402
+ describe('IsUrl', () => {
403
+ const rule = IsUrl();
404
+
405
+ test('should pass for valid URLs', () => {
406
+ expect(rule.validate('https://example.com')).toBe(true);
407
+ expect(rule.validate('http://localhost:3000')).toBe(true);
408
+ });
409
+
410
+ test('should fail for invalid URLs', () => {
411
+ expect(rule.validate('not-a-url')).toBe(false);
412
+ expect(rule.validate('example.com')).toBe(false);
413
+ });
414
+ });
415
+
416
+ describe('IsJSON', () => {
417
+ const rule = IsJSON();
418
+
419
+ test('should pass for valid JSON strings', () => {
420
+ expect(rule.validate('{"a":1}')).toBe(true);
421
+ expect(rule.validate('[]')).toBe(true);
422
+ expect(rule.validate('"string"')).toBe(true);
423
+ });
424
+
425
+ test('should fail for invalid JSON strings', () => {
426
+ expect(rule.validate('{a:1}')).toBe(false);
427
+ expect(rule.validate('undefined')).toBe(false);
428
+ });
429
+ });
430
+
431
+ describe('Equals', () => {
432
+ const rule = Equals('test');
433
+
434
+ test('should pass for equal values', () => {
435
+ expect(rule.validate('test')).toBe(true);
436
+ });
437
+
438
+ test('should fail for non-equal values', () => {
439
+ expect(rule.validate('other')).toBe(false);
440
+ });
441
+ });
442
+
443
+ describe('NotEquals', () => {
444
+ const rule = NotEquals('test');
445
+
446
+ test('should pass for non-equal values', () => {
447
+ expect(rule.validate('other')).toBe(true);
448
+ });
449
+
450
+ test('should fail for equal values', () => {
451
+ expect(rule.validate('test')).toBe(false);
452
+ });
453
+ });
454
+
455
+ describe('IsDefined', () => {
456
+ const rule = IsDefined();
457
+
458
+ test('should pass for defined values', () => {
459
+ expect(rule.validate('')).toBe(true);
460
+ expect(rule.validate(0)).toBe(true);
461
+ expect(rule.validate(false)).toBe(true);
462
+ });
463
+
464
+ test('should fail for null or undefined', () => {
465
+ expect(rule.validate(null)).toBe(false);
466
+ expect(rule.validate(undefined)).toBe(false);
467
+ });
468
+ });
469
+
470
+ describe('IsAlphanumeric', () => {
471
+ const rule = IsAlphanumeric();
472
+
473
+ test('should pass for alphanumeric strings', () => {
474
+ expect(rule.validate('abc123')).toBe(true);
475
+ expect(rule.validate('ABC')).toBe(true);
476
+ });
477
+
478
+ test('should fail for non-alphanumeric strings', () => {
479
+ expect(rule.validate('abc-123')).toBe(false);
480
+ expect(rule.validate('abc 123')).toBe(false);
481
+ });
482
+ });
483
+
484
+ describe('IsAlpha', () => {
485
+ const rule = IsAlpha();
486
+
487
+ test('should pass for alphabetic strings', () => {
488
+ expect(rule.validate('abc')).toBe(true);
489
+ expect(rule.validate('ABC')).toBe(true);
490
+ });
491
+
492
+ test('should fail for non-alphabetic strings', () => {
493
+ expect(rule.validate('abc123')).toBe(false);
494
+ });
495
+ });
496
+
497
+ describe('IsNumberString', () => {
498
+ const rule = IsNumberString();
499
+
500
+ test('should pass for numeric strings', () => {
501
+ expect(rule.validate('123')).toBe(true);
502
+ expect(rule.validate('0')).toBe(true);
503
+ });
504
+
505
+ test('should fail for non-numeric strings', () => {
506
+ expect(rule.validate('12.3')).toBe(false);
507
+ expect(rule.validate('abc')).toBe(false);
508
+ });
509
+ });
510
+ });
511
+
512
+ describe('Conditional Validation Rules', () => {
513
+ describe('ValidateIf', () => {
514
+ test('should have condition function', () => {
515
+ const condition = (value: unknown, obj: unknown) =>
516
+ (obj as { type?: string })?.type === 'premium';
517
+ const rule = ValidateIf(condition);
518
+
519
+ expect(rule.condition).toBeDefined();
520
+ expect(rule.condition!('value', { type: 'premium' })).toBe(true);
521
+ expect(rule.condition!('value', { type: 'basic' })).toBe(false);
522
+ });
523
+
524
+ test('validate should always return true', () => {
525
+ const rule = ValidateIf(() => false);
526
+ expect(rule.validate('any')).toBe(true);
527
+ });
528
+ });
529
+
530
+ describe('Transform', () => {
531
+ test('should have transform function', () => {
532
+ const rule = Transform((value) => String(value).trim());
533
+
534
+ expect(rule.transform).toBeDefined();
535
+ expect(rule.transform!(' hello ')).toBe('hello');
536
+ });
537
+
538
+ test('validate should always return true', () => {
539
+ const rule = Transform((value) => value);
540
+ expect(rule.validate('any')).toBe(true);
541
+ });
542
+ });
543
+ });