@quanxiaoxiao/datav 0.4.0 → 0.5.1

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 (64) hide show
  1. package/README.md +680 -165
  2. package/dist/createArrayAccessor.d.ts +2 -0
  3. package/dist/createArrayAccessor.d.ts.map +1 -0
  4. package/dist/createArrayAccessor.js +38 -0
  5. package/dist/createArrayAccessor.js.map +1 -0
  6. package/dist/createDataAccessor.d.ts +2 -0
  7. package/dist/createDataAccessor.d.ts.map +1 -0
  8. package/dist/createDataAccessor.js +23 -0
  9. package/dist/createDataAccessor.js.map +1 -0
  10. package/dist/createDataTransformer.d.ts +14 -0
  11. package/dist/createDataTransformer.d.ts.map +1 -0
  12. package/dist/createDataTransformer.js +124 -0
  13. package/dist/createDataTransformer.js.map +1 -0
  14. package/dist/createPathAccessor.d.ts +2 -0
  15. package/dist/createPathAccessor.d.ts.map +1 -0
  16. package/dist/createPathAccessor.js +38 -0
  17. package/dist/createPathAccessor.js.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +5 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/parseDotPath.d.ts +2 -0
  23. package/dist/parseDotPath.d.ts.map +1 -0
  24. package/dist/parseDotPath.js +14 -0
  25. package/dist/parseDotPath.js.map +1 -0
  26. package/dist/parseValueByType.d.ts +11 -0
  27. package/dist/parseValueByType.d.ts.map +1 -0
  28. package/dist/parseValueByType.js +122 -0
  29. package/dist/parseValueByType.js.map +1 -0
  30. package/dist/utils.d.ts +3 -0
  31. package/dist/utils.d.ts.map +1 -0
  32. package/dist/utils.js +22 -0
  33. package/dist/utils.js.map +1 -0
  34. package/dist/validateExpressSchema.d.ts +7 -0
  35. package/dist/validateExpressSchema.d.ts.map +1 -0
  36. package/dist/validateExpressSchema.js +50 -0
  37. package/dist/validateExpressSchema.js.map +1 -0
  38. package/package.json +46 -8
  39. package/src/createArrayAccessor.test.ts +181 -0
  40. package/src/createArrayAccessor.ts +48 -0
  41. package/src/createDataAccessor.test.ts +220 -0
  42. package/src/createDataAccessor.ts +26 -0
  43. package/src/createDataTransformer.test.ts +847 -0
  44. package/src/createDataTransformer.ts +173 -0
  45. package/src/createPathAccessor.test.ts +217 -0
  46. package/src/createPathAccessor.ts +45 -0
  47. package/src/index.ts +11 -0
  48. package/src/parseDotPath.test.ts +132 -0
  49. package/src/parseDotPath.ts +13 -0
  50. package/src/parseValueByType.test.ts +342 -0
  51. package/src/parseValueByType.ts +165 -0
  52. package/src/utils.test.ts +85 -0
  53. package/src/utils.ts +22 -0
  54. package/src/validateExpressSchema.test.ts +295 -0
  55. package/src/validateExpressSchema.ts +59 -0
  56. package/.editorconfig +0 -13
  57. package/eslint.config.mjs +0 -89
  58. package/src/checkout.mjs +0 -131
  59. package/src/checkout.test.mjs +0 -144
  60. package/src/index.mjs +0 -7
  61. package/src/select/check.mjs +0 -63
  62. package/src/select/check.test.mjs +0 -76
  63. package/src/select/index.mjs +0 -117
  64. package/src/select/index.test.mjs +0 -1145
@@ -0,0 +1,847 @@
1
+ import * as assert from 'node:assert';
2
+ import { mock, test } from 'node:test';
3
+
4
+ import { createDataTransformer } from './createDataTransformer.js';
5
+
6
+ // 测试辅助函数
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ const expectTransform = (schema: any, input: any, expected: any) => {
9
+ const result = createDataTransformer(schema)(input);
10
+ assert.deepEqual(result, expected);
11
+ };
12
+
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ const expectThrows = (schema: any) => {
15
+ assert.throws(() => createDataTransformer(schema));
16
+ };
17
+
18
+ test('基础类型转换', async (t) => {
19
+ await t.test('应该拒绝无效的 schema 格式', () => {
20
+ expectThrows('number');
21
+ expectThrows(['name']);
22
+ expectThrows(['name', []]);
23
+ expectThrows(['name', 'xxx']);
24
+ });
25
+
26
+ await t.test('number 类型转换', () => {
27
+ const schema = { type: 'number' };
28
+ expectTransform(schema, '1', 1);
29
+ expectTransform(schema, '1.1', 1.1);
30
+ expectTransform(schema, 1.1, 1.1);
31
+ expectTransform(schema, '33.3', 33.3);
32
+ });
33
+
34
+ await t.test('integer 类型转换', () => {
35
+ const schema = { type: 'integer' };
36
+ expectTransform(schema, '1.1', 1);
37
+ expectTransform(schema, '33.3', 33);
38
+ });
39
+
40
+ await t.test('boolean 类型转换', () => {
41
+ const schema = { type: 'boolean' };
42
+ expectTransform(schema, 'true', true);
43
+ expectTransform(schema, 'false', false);
44
+ expectTransform(schema, 'true1', null);
45
+ });
46
+
47
+ await t.test('string 类型转换', () => {
48
+ const schema = { type: 'string' };
49
+ expectTransform(schema, 123, '123');
50
+ expectTransform(schema, 'hello', 'hello');
51
+ expectTransform(schema, null, null);
52
+ });
53
+ });
54
+
55
+ test('路径访问 (pathname)', async (t) => {
56
+ await t.test('基本路径访问', () => {
57
+ expectTransform(
58
+ ['age', { type: 'integer' }],
59
+ { age: '33.3' },
60
+ 33,
61
+ );
62
+
63
+ expectTransform(
64
+ ['sub.age', { type: 'integer' }],
65
+ { name: 'quan', sub: { age: 33.3 } },
66
+ 33,
67
+ );
68
+ });
69
+
70
+ await t.test('嵌套对象路径', () => {
71
+ expectTransform(
72
+ ['obj.age', { type: 'integer' }],
73
+ { obj: { age: '33.33' } },
74
+ 33,
75
+ );
76
+ });
77
+
78
+ await t.test('路径不存在时返回 null', () => {
79
+ expectTransform(
80
+ ['ages', { type: 'integer' }],
81
+ { age: '2.2' },
82
+ null,
83
+ );
84
+ });
85
+
86
+ await t.test('数组索引访问', () => {
87
+ expectTransform(
88
+ ['1', { type: 'number' }],
89
+ ['44', '33.3'],
90
+ 33.3,
91
+ );
92
+
93
+ expectTransform(
94
+ ['1.age', { type: 'number' }],
95
+ ['44', '33.3'],
96
+ null,
97
+ );
98
+ });
99
+
100
+ await t.test('根路径访问 ($)', () => {
101
+ expectTransform(
102
+ {
103
+ type: 'array',
104
+ properties: ['$age', { type: 'integer' }],
105
+ },
106
+ { name: 'aa', age: '44.4' },
107
+ [44],
108
+ );
109
+
110
+ expectTransform(
111
+ {
112
+ type: 'array',
113
+ properties: ['$ages', { type: 'integer' }],
114
+ },
115
+ { name: 'aa', age: '44.4' },
116
+ [],
117
+ );
118
+ });
119
+
120
+ await t.test('点号路径 (.)', () => {
121
+ expectTransform(
122
+ ['.data.key', { type: 'string' }],
123
+ { data: { key: 'aaaabbb' } },
124
+ 'aaaabbb',
125
+ );
126
+ });
127
+ });
128
+
129
+ test('对象类型转换', async (t) => {
130
+ await t.test('应该拒绝无效的对象 schema', () => {
131
+ expectThrows({ type: 'object' });
132
+ expectThrows({ type: 'object', properties: [] });
133
+ expectThrows(['obj', { type: 'object' }]);
134
+ expectThrows({ type: 'object', properties: 'xxx' });
135
+ });
136
+
137
+ await t.test('基本对象转换', () => {
138
+ expectTransform(
139
+ {
140
+ type: 'object',
141
+ properties: {
142
+ name: { type: 'string' },
143
+ age: { type: 'integer' },
144
+ },
145
+ },
146
+ { name: 'quan', age: '22.2', foo: 'bar' },
147
+ { name: 'quan', age: 22 },
148
+ );
149
+ });
150
+
151
+ await t.test('嵌套对象转换', () => {
152
+ expectTransform(
153
+ {
154
+ type: 'object',
155
+ properties: {
156
+ name: { type: 'string' },
157
+ age: { type: 'number' },
158
+ obj: {
159
+ type: 'object',
160
+ properties: {
161
+ name: { type: 'string' },
162
+ age: { type: 'integer' },
163
+ },
164
+ },
165
+ },
166
+ },
167
+ {
168
+ name: 'quan',
169
+ age: '22.5',
170
+ obj: { name: 'xxx', age: '33.3', big: 'foo' },
171
+ },
172
+ {
173
+ name: 'quan',
174
+ age: 22.5,
175
+ obj: { name: 'xxx', age: 33 },
176
+ },
177
+ );
178
+ });
179
+
180
+ await t.test('空 properties 保留所有字段', () => {
181
+ expectTransform(
182
+ { type: 'object', properties: {} },
183
+ { name: 'quan', age: '22.5', obj: 'aaa' },
184
+ { name: 'quan', age: '22.5', obj: 'aaa' },
185
+ );
186
+ });
187
+
188
+ await t.test('对象路径访问', () => {
189
+ expectTransform(
190
+ ['obj', {
191
+ type: 'object',
192
+ properties: {
193
+ name: { type: 'string' },
194
+ age: { type: 'integer' },
195
+ },
196
+ }],
197
+ {
198
+ name: 'quan',
199
+ age: '22.5',
200
+ obj: { name: 'xxx', age: '33.3', big: 'foo' },
201
+ },
202
+ { name: 'xxx', age: 33 },
203
+ );
204
+ });
205
+
206
+ await t.test('对象字段重命名', () => {
207
+ expectTransform(
208
+ {
209
+ type: 'object',
210
+ properties: {
211
+ name: { type: 'string' },
212
+ ding: ['age', { type: 'integer' }],
213
+ },
214
+ },
215
+ { name: 'quan', age: '22.5' },
216
+ { name: 'quan', ding: 22 },
217
+ );
218
+ });
219
+
220
+ await t.test('复杂嵌套对象转换', () => {
221
+ expectTransform(
222
+ {
223
+ type: 'object',
224
+ properties: {
225
+ name: { type: 'string' },
226
+ age: { type: 'number' },
227
+ ddd: ['obj.name', { type: 'string' }],
228
+ sub: ['obj', {
229
+ type: 'object',
230
+ properties: {
231
+ name: { type: 'string' },
232
+ age: { type: 'integer' },
233
+ cqq: ['big', { type: 'string' }],
234
+ },
235
+ }],
236
+ },
237
+ },
238
+ {
239
+ name: 'quan',
240
+ age: '22.5',
241
+ obj: { name: 'xxx', age: '33.3', big: 'foo' },
242
+ },
243
+ {
244
+ name: 'quan',
245
+ age: 22.5,
246
+ ddd: 'xxx',
247
+ sub: { name: 'xxx', age: 33, cqq: 'foo' },
248
+ },
249
+ );
250
+ });
251
+
252
+ await t.test('数组索引转对象', () => {
253
+ expectTransform(
254
+ ['0', {
255
+ type: 'object',
256
+ properties: {
257
+ name: { type: 'string' },
258
+ id: { type: 'number' },
259
+ _id: ['id', { type: 'string' }],
260
+ },
261
+ }],
262
+ [{ name: 'quan', id: 11 }],
263
+ { name: 'quan', id: 11, _id: '11' },
264
+ );
265
+
266
+ expectTransform(
267
+ ['0', {
268
+ type: 'object',
269
+ properties: {
270
+ name: { type: 'string' },
271
+ id: { type: 'number' },
272
+ _id: ['id', { type: 'string' }],
273
+ },
274
+ }],
275
+ [],
276
+ { name: null, id: null, _id: null },
277
+ );
278
+ });
279
+
280
+ await t.test('根路径引用', () => {
281
+ expectTransform(
282
+ {
283
+ type: 'object',
284
+ properties: {
285
+ name: { type: 'string' },
286
+ quan: ['foo.big', {
287
+ type: 'object',
288
+ properties: {
289
+ name: { type: 'string' },
290
+ age: { type: 'integer' },
291
+ ding: ['$cqq', { type: 'number' }],
292
+ jj: ['$other.age', { type: 'integer' }],
293
+ },
294
+ }],
295
+ },
296
+ },
297
+ {
298
+ name: 'aaa',
299
+ cqq: '44.44',
300
+ other: { age: '66.6' },
301
+ foo: {
302
+ name: 'bbb',
303
+ dd: 'ee',
304
+ big: { name: 'cccc', age: '33.3' },
305
+ },
306
+ },
307
+ {
308
+ name: 'aaa',
309
+ quan: {
310
+ name: 'cccc',
311
+ age: 33,
312
+ jj: 66,
313
+ ding: 44.44,
314
+ },
315
+ },
316
+ );
317
+ });
318
+ });
319
+
320
+ test('数组类型转换', async (t) => {
321
+ await t.test('基本数组元素转换', () => {
322
+ expectTransform(
323
+ {
324
+ type: 'array',
325
+ properties: ['.', { type: 'integer' }],
326
+ },
327
+ ['33.3', '22.8'],
328
+ [33, 22],
329
+ );
330
+
331
+ expectTransform(
332
+ {
333
+ type: 'array',
334
+ properties: ['.', { type: 'integer' }],
335
+ },
336
+ ['1.1', '3', '4'],
337
+ [1, 3, 4],
338
+ );
339
+ });
340
+
341
+ await t.test('对象数组转换', () => {
342
+ expectTransform(
343
+ {
344
+ type: 'array',
345
+ properties: { age: { type: 'integer' } },
346
+ },
347
+ [{ age: '33.3' }],
348
+ [{ age: 33 }],
349
+ );
350
+
351
+ expectTransform(
352
+ {
353
+ type: 'array',
354
+ properties: { age: { type: 'integer' } },
355
+ },
356
+ { age: '33.3' },
357
+ [{ age: 33 }],
358
+ );
359
+ });
360
+
361
+ await t.test('提取数组元素字段', () => {
362
+ expectTransform(
363
+ {
364
+ type: 'array',
365
+ properties: ['age', { type: 'integer' }],
366
+ },
367
+ [{ age: '1.1' }, { age: '3' }, { age: '4' }],
368
+ [1, 3, 4],
369
+ );
370
+ });
371
+
372
+ await t.test('使用根路径填充数组', () => {
373
+ expectTransform(
374
+ {
375
+ type: 'object',
376
+ properties: {
377
+ name: { type: 'string' },
378
+ arr: {
379
+ type: 'array',
380
+ properties: {
381
+ name: ['$foo.name', { type: 'string' }],
382
+ age: ['$big.age', { type: 'integer' }],
383
+ },
384
+ },
385
+ },
386
+ },
387
+ {
388
+ name: 'aaa',
389
+ foo: { name: 'bbb' },
390
+ big: { age: '99.99' },
391
+ },
392
+ {
393
+ name: 'aaa',
394
+ arr: [{ name: 'bbb', age: 99 }],
395
+ },
396
+ );
397
+ });
398
+
399
+ await t.test('嵌套数组转换', () => {
400
+ expectTransform(
401
+ {
402
+ type: 'array',
403
+ properties: ['.', {
404
+ type: 'array',
405
+ properties: ['.', {
406
+ type: 'array',
407
+ properties: ['.', { type: 'number' }],
408
+ }],
409
+ }],
410
+ },
411
+ [[['11', 22], ['33', 44]], [[1], [2]]],
412
+ [[[11, 22], [33, 44]], [[1], [2]]],
413
+ );
414
+ });
415
+
416
+ await t.test('数组路径提取', () => {
417
+ expectTransform(
418
+ ['.data', {
419
+ type: 'array',
420
+ properties: { name: { type: 'string' } },
421
+ }],
422
+ {
423
+ data: [
424
+ { name: 'aa', age: 22 },
425
+ { name: 'bb', age: 33 },
426
+ ],
427
+ },
428
+ [{ name: 'aa' }, { name: 'bb' }],
429
+ );
430
+ });
431
+
432
+ await t.test('数组字段重映射', () => {
433
+ expectTransform(
434
+ ['.recordList', {
435
+ type: 'array',
436
+ properties: {
437
+ dateTimeNameStart: ['.startTime', { type: 'string' }],
438
+ dateTimeNameEnd: ['.endTime', { type: 'string' }],
439
+ },
440
+ }],
441
+ {
442
+ deviceId: '101007351946',
443
+ recordList: [
444
+ { deviceId: '101007351946', startTime: '2024-06-25 10:09:37', endTime: '2024-06-25 13:45:32' },
445
+ { deviceId: '101007351946', startTime: '2024-06-25 13:47:16', endTime: '2024-06-25 17:01:19' },
446
+ ],
447
+ },
448
+ [
449
+ { dateTimeNameStart: '2024-06-25 10:09:37', dateTimeNameEnd: '2024-06-25 13:45:32' },
450
+ { dateTimeNameStart: '2024-06-25 13:47:16', dateTimeNameEnd: '2024-06-25 17:01:19' },
451
+ ],
452
+ );
453
+ });
454
+ });
455
+
456
+ test('resolve 函数', async (t) => {
457
+ await t.test('基本类型 resolve', () => {
458
+ expectTransform(
459
+ {
460
+ type: 'integer',
461
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
462
+ resolve: (v: any) => v + 1,
463
+ },
464
+ 88,
465
+ 89,
466
+ );
467
+
468
+ expectTransform(
469
+ ['age', {
470
+ type: 'integer',
471
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
472
+ resolve: (v: any) => `${v + 1}`,
473
+ }],
474
+ { age: 88 },
475
+ 89,
476
+ );
477
+ });
478
+
479
+ await t.test('对象中的 resolve', () => {
480
+ expectTransform(
481
+ {
482
+ type: 'object',
483
+ properties: {
484
+ name: {
485
+ type: 'string',
486
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
487
+ resolve: (a: any, b: any) => `${a}_${b.aa}`,
488
+ },
489
+ age: {
490
+ type: 'integer',
491
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
492
+ resolve: (a: any) => a + 1,
493
+ },
494
+ },
495
+ },
496
+ { name: 'quan', aa: 'xx', age: 33 },
497
+ { name: 'quan_xx', age: 34 },
498
+ );
499
+ });
500
+
501
+ await t.test('resolve 提供默认值', () => {
502
+ expectTransform(
503
+ {
504
+ type: 'object',
505
+ properties: {
506
+ name: { type: 'string' },
507
+ age: {
508
+ type: 'integer',
509
+ resolve: () => 99,
510
+ },
511
+ },
512
+ },
513
+ { name: 'quan' },
514
+ { name: 'quan', age: 99 },
515
+ );
516
+ });
517
+
518
+ await t.test('嵌套对象 resolve', () => {
519
+ expectTransform(
520
+ {
521
+ type: 'object',
522
+ properties: {
523
+ name: {
524
+ type: 'string',
525
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
526
+ resolve: (a: any, b: any) => `${a}_${b.aa}`,
527
+ },
528
+ obj: {
529
+ type: 'object',
530
+ properties: {
531
+ name: { type: 'string' },
532
+ age: ['big', {
533
+ type: 'integer',
534
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
535
+ resolve: (a: any) => a + 1,
536
+ }],
537
+ ding: {
538
+ type: 'string',
539
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
540
+ resolve: (a: any, b: any) => `${b.name}_${a}`,
541
+ },
542
+ },
543
+ },
544
+ },
545
+ },
546
+ {
547
+ name: 'quan',
548
+ aa: 'xx',
549
+ obj: { name: 'rice', big: 33, ding: 'aaa' },
550
+ },
551
+ {
552
+ name: 'quan_xx',
553
+ obj: { name: 'rice', age: 34, ding: 'quan_aaa' },
554
+ },
555
+ );
556
+ });
557
+
558
+ await t.test('resolve 不覆盖字段名', () => {
559
+ expectTransform(
560
+ { type: 'object', properties: { name: { type: 'string' }, resolve: { type: 'string' } } },
561
+ { name: 'aaa', resolve: 'resolve' },
562
+ { name: 'aaa', resolve: 'resolve' },
563
+ );
564
+ });
565
+
566
+ await t.test('数组中的 resolve', () => {
567
+ expectTransform(
568
+ {
569
+ type: 'object',
570
+ properties: {
571
+ count: { type: 'integer' },
572
+ list: {
573
+ type: 'array',
574
+ properties: {
575
+ token: ['.', {
576
+ type: 'string',
577
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
578
+ resolve: (d: any) => `${d.name}_${d.age}`,
579
+ }],
580
+ name: { type: 'string' },
581
+ },
582
+ },
583
+ },
584
+ },
585
+ {
586
+ count: 20,
587
+ list: [
588
+ { name: 'big', age: 11 },
589
+ { name: 'bar', age: 22 },
590
+ ],
591
+ },
592
+ {
593
+ count: 20,
594
+ list: [
595
+ { name: 'big', token: 'big_11' },
596
+ { name: 'bar', token: 'bar_22' },
597
+ ],
598
+ },
599
+ );
600
+ });
601
+
602
+ await t.test('数组提取路径', () => {
603
+ expectTransform(
604
+ {
605
+ type: 'object',
606
+ properties: {
607
+ count: { type: 'integer' },
608
+ list: {
609
+ type: 'array',
610
+ properties: ['.name', { type: 'string' }],
611
+ },
612
+ },
613
+ },
614
+ {
615
+ count: 20,
616
+ list: [
617
+ { name: 'big', age: 11 },
618
+ { name: 'bar', age: 22 },
619
+ ],
620
+ },
621
+ { count: 20, list: ['big', 'bar'] },
622
+ );
623
+ });
624
+ });
625
+
626
+ test('实际场景测试', async (t) => {
627
+ await t.test('从嵌套结构提取数据', () => {
628
+ expectTransform(
629
+ ['obj', {
630
+ type: 'object',
631
+ properties: { name: { type: 'string' } },
632
+ }],
633
+ { name: 'quan', age: '22.5', obj: { name: 'xxx', age: '33.3', big: 'foo' } },
634
+ { name: 'xxx' },
635
+ );
636
+ });
637
+
638
+ await t.test('根路径创建数组', () => {
639
+ expectTransform(
640
+ {
641
+ type: 'object',
642
+ properties: {
643
+ chl: {
644
+ type: 'array',
645
+ properties: ['$channel', { type: 'string' }],
646
+ },
647
+ },
648
+ },
649
+ { channel: '1' },
650
+ { chl: ['1'] },
651
+ );
652
+ });
653
+
654
+ await t.test('构建参数数组', () => {
655
+ expectTransform(
656
+ {
657
+ type: 'object',
658
+ properties: {
659
+ key: { type: 'string' },
660
+ params: {
661
+ type: 'array',
662
+ properties: {
663
+ task: ['$taskId', { type: 'number' }],
664
+ date: ['$dateName', { type: 'string' }],
665
+ },
666
+ },
667
+ },
668
+ },
669
+ { key: '123', taskId: '999', dateName: '2024-06-06' },
670
+ {
671
+ key: '123',
672
+ params: [{ task: '999', date: '2024-06-06' }],
673
+ },
674
+ );
675
+ });
676
+
677
+ await t.test('提取数组首项', () => {
678
+ expectTransform(
679
+ {
680
+ type: 'object',
681
+ properties: {
682
+ dir: ['.data.0.dir', { type: 'string' }],
683
+ name: ['.data.0.name', { type: 'string' }],
684
+ },
685
+ },
686
+ {
687
+ data: [
688
+ { dir: 'QzpcVmlkZW9ccXExMjM0XDIwMTctMDYtMTlccmVjb3JkXDE=', name: 'qq1234-170619-000000-002000-01p401000000.264' },
689
+ { dir: 'QzpcVmlkZW9ccXExMjM0XDIwMTctMDYtMTlccmVjb3JkXDE=', name: 'qq1234-170619-000000-002000-01p401000000.mp4' },
690
+ ],
691
+ errorcode: 200,
692
+ },
693
+ {
694
+ dir: 'QzpcVmlkZW9ccXExMjM0XDIwMTctMDYtMTlccmVjb3JkXDE=',
695
+ name: 'qq1234-170619-000000-002000-01p401000000.264',
696
+ },
697
+ );
698
+ });
699
+
700
+ await t.test('保留原始对象结构', () => {
701
+ expectTransform(
702
+ {
703
+ type: 'object',
704
+ properties: {
705
+ route: ['.data', {
706
+ type: 'object',
707
+ properties: {},
708
+ }],
709
+ },
710
+ },
711
+ {
712
+ code: 0,
713
+ data: {
714
+ name: 'data111',
715
+ '/aaa': { name: '123', '/ccc': { name: 'ccc' } },
716
+ '/sss': { name: '999' },
717
+ },
718
+ },
719
+ {
720
+ route: {
721
+ name: 'data111',
722
+ '/aaa': { name: '123', '/ccc': { name: 'ccc' } },
723
+ '/sss': { name: '999' },
724
+ },
725
+ },
726
+ );
727
+ });
728
+
729
+ await t.test('包装单值为对象', () => {
730
+ expectTransform(
731
+ {
732
+ type: 'object',
733
+ properties: { name: ['.', { type: 'string' }] },
734
+ },
735
+ '111222',
736
+ { name: '111222' },
737
+ );
738
+ });
739
+
740
+ await t.test('处理根路径数组', () => {
741
+ expectTransform(
742
+ ['.data', {
743
+ type: 'array',
744
+ properties: ['.', { type: 'string' }],
745
+ }],
746
+ { data: ['222', '333'] },
747
+ ['222', '333'],
748
+ );
749
+ });
750
+ });
751
+
752
+ test('边界情况', async (t) => {
753
+ await t.test('处理 null 值', () => {
754
+ const schema = { type: 'string' };
755
+ expectTransform(schema, null, null);
756
+ });
757
+
758
+ await t.test('处理空数组', () => {
759
+ expectTransform(
760
+ {
761
+ type: 'array',
762
+ properties: { name: { type: 'string' } },
763
+ },
764
+ [],
765
+ [],
766
+ );
767
+ });
768
+
769
+ await t.test('处理包含 null 的数组', () => {
770
+ expectTransform(
771
+ {
772
+ type: 'array',
773
+ properties: ['.', { type: 'string' }],
774
+ },
775
+ [null, 'test', undefined],
776
+ [null, 'test', null],
777
+ );
778
+ });
779
+
780
+ await t.test('处理缺失字段', () => {
781
+ const result = createDataTransformer({
782
+ type: 'object',
783
+ properties: {
784
+ name: { type: 'string' },
785
+ age: { type: 'number' },
786
+ email: { type: 'string' },
787
+ },
788
+ })({ name: 'Alice' });
789
+
790
+ assert.strictEqual(result.name, 'Alice');
791
+ assert.strictEqual(result.age, null);
792
+ assert.strictEqual(result.email, null);
793
+ });
794
+
795
+ await t.test('非数组输入转数组', () => {
796
+ expectTransform(
797
+ { type: 'array', properties: { name: { type: 'string' } } },
798
+ { names: 'quan' },
799
+ [{ name: null }],
800
+ );
801
+
802
+ expectTransform(
803
+ { type: 'array', properties: { name: { type: 'string' } } },
804
+ { name: 'quan' },
805
+ [{ name: 'quan' }],
806
+ );
807
+ });
808
+
809
+ await t.test('空 properties 返回空数组', () => {
810
+ expectTransform(
811
+ { type: 'array', properties: {} },
812
+ { names: 'quan' },
813
+ [],
814
+ );
815
+ });
816
+
817
+ await t.test('非对象输入转对象', () => {
818
+ expectTransform(
819
+ ['obj', {
820
+ type: 'object',
821
+ properties: {},
822
+ }],
823
+ { name: 'quan', age: '22.5', obj: 'aaa' },
824
+ {},
825
+ );
826
+ });
827
+ });
828
+
829
+ test('错误处理', async (t) => {
830
+ await t.test('警告数组/对象使用 resolve', () => {
831
+ const consoleWarnSpy = mock.method(console, 'warn');
832
+
833
+ createDataTransformer({
834
+ type: 'array',
835
+ properties: {},
836
+ resolve: () => {},
837
+ });
838
+
839
+ assert.strictEqual(consoleWarnSpy.mock.calls.length, 1);
840
+ assert.match(
841
+ consoleWarnSpy.mock.calls[0].arguments[0] as string,
842
+ /does not support resolve/,
843
+ );
844
+
845
+ consoleWarnSpy.mock.restore();
846
+ });
847
+ });