befly 3.10.1 → 3.10.2

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 (66) hide show
  1. package/.gitignore +0 -0
  2. package/configs/presetFields.ts +10 -0
  3. package/configs/presetRegexp.ts +225 -0
  4. package/package.json +15 -16
  5. package/tests/_mocks/mockSqliteDb.ts +0 -204
  6. package/tests/addonHelper-cache.test.ts +0 -32
  7. package/tests/api-integration-array-number.test.ts +0 -282
  8. package/tests/apiHandler-routePath-only.test.ts +0 -32
  9. package/tests/befly-config-env.test.ts +0 -78
  10. package/tests/cacheHelper.test.ts +0 -323
  11. package/tests/cacheKeys.test.ts +0 -41
  12. package/tests/checkApi-routePath-strict.test.ts +0 -166
  13. package/tests/checkMenu.test.ts +0 -346
  14. package/tests/checkTable-smoke.test.ts +0 -157
  15. package/tests/cipher.test.ts +0 -249
  16. package/tests/dbDialect-cache.test.ts +0 -23
  17. package/tests/dbDialect.test.ts +0 -46
  18. package/tests/dbHelper-advanced.test.ts +0 -723
  19. package/tests/dbHelper-all-array-types.test.ts +0 -316
  20. package/tests/dbHelper-array-serialization.test.ts +0 -258
  21. package/tests/dbHelper-batch-write.test.ts +0 -90
  22. package/tests/dbHelper-columns.test.ts +0 -234
  23. package/tests/dbHelper-execute.test.ts +0 -187
  24. package/tests/dbHelper-joins.test.ts +0 -221
  25. package/tests/fields-redis-cache.test.ts +0 -127
  26. package/tests/fields-validate.test.ts +0 -99
  27. package/tests/fixtures/scanFilesAddon/node_modules/@befly-addon/demo/apis/sub/b.ts +0 -3
  28. package/tests/fixtures/scanFilesApis/a.ts +0 -3
  29. package/tests/fixtures/scanFilesApis/sub/b.ts +0 -3
  30. package/tests/getClientIp.test.ts +0 -54
  31. package/tests/integration.test.ts +0 -189
  32. package/tests/jwt.test.ts +0 -65
  33. package/tests/loadPlugins-order-smoke.test.ts +0 -75
  34. package/tests/logger.test.ts +0 -325
  35. package/tests/redisHelper.test.ts +0 -495
  36. package/tests/redisKeys.test.ts +0 -9
  37. package/tests/scanConfig.test.ts +0 -144
  38. package/tests/scanFiles-routePath.test.ts +0 -46
  39. package/tests/smoke-sql.test.ts +0 -24
  40. package/tests/sqlBuilder-advanced.test.ts +0 -608
  41. package/tests/sqlBuilder.test.ts +0 -209
  42. package/tests/sync-connection.test.ts +0 -183
  43. package/tests/sync-init-guard.test.ts +0 -105
  44. package/tests/syncApi-insBatch-fields-consistent.test.ts +0 -61
  45. package/tests/syncApi-obsolete-records.test.ts +0 -69
  46. package/tests/syncApi-type-compat.test.ts +0 -72
  47. package/tests/syncDev-permissions.test.ts +0 -81
  48. package/tests/syncMenu-disableMenus-hard-delete.test.ts +0 -88
  49. package/tests/syncMenu-duplicate-path.test.ts +0 -122
  50. package/tests/syncMenu-obsolete-records.test.ts +0 -161
  51. package/tests/syncMenu-parentPath-from-tree.test.ts +0 -75
  52. package/tests/syncMenu-paths.test.ts +0 -59
  53. package/tests/syncTable-apply.test.ts +0 -279
  54. package/tests/syncTable-array-number.test.ts +0 -160
  55. package/tests/syncTable-constants.test.ts +0 -101
  56. package/tests/syncTable-db-integration.test.ts +0 -237
  57. package/tests/syncTable-ddl.test.ts +0 -245
  58. package/tests/syncTable-helpers.test.ts +0 -99
  59. package/tests/syncTable-schema.test.ts +0 -99
  60. package/tests/syncTable-testkit.test.ts +0 -25
  61. package/tests/syncTable-types.test.ts +0 -122
  62. package/tests/tableRef-and-deserialize.test.ts +0 -67
  63. package/tests/util.test.ts +0 -100
  64. package/tests/validator-array-number.test.ts +0 -310
  65. package/tests/validator-default.test.ts +0 -373
  66. package/tests/validator.test.ts +0 -679
@@ -1,495 +0,0 @@
1
- /**
2
- * RedisHelper 测试
3
- * 测试 Redis 操作功能
4
- */
5
-
6
- import { describe, expect, test, beforeAll, afterAll } from "bun:test";
7
-
8
- import { Connect } from "../lib/connect.js";
9
- import { RedisHelper } from "../lib/redisHelper.js";
10
-
11
- let redis: RedisHelper;
12
-
13
- beforeAll(async () => {
14
- // 连接 Redis
15
- await Connect.connectRedis({
16
- host: "127.0.0.1",
17
- port: 6379,
18
- db: 0,
19
- prefix: "befly:"
20
- });
21
- redis = new RedisHelper();
22
- });
23
-
24
- afterAll(async () => {
25
- // 断开 Redis 连接
26
- await Connect.disconnectRedis();
27
- });
28
-
29
- describe("RedisHelper - 字符串操作", () => {
30
- test("setString - 设置字符串", async () => {
31
- const result = await redis.setString("test:string", "Hello Redis");
32
- expect(result).toBe("OK");
33
- });
34
-
35
- test("getString - 获取字符串", async () => {
36
- await redis.setString("test:string", "Hello Redis");
37
- const value = await redis.getString("test:string");
38
- expect(value).toBe("Hello Redis");
39
- });
40
-
41
- test("getString - 获取不存在的键", async () => {
42
- const value = await redis.getString("test:non_existent");
43
- expect(value).toBeNull();
44
- });
45
-
46
- test("setString - 设置带过期时间的字符串", async () => {
47
- const result = await redis.setString("test:ttl", "Expire Test", 2);
48
- expect(result).toBe("OK");
49
-
50
- const value = await redis.getString("test:ttl");
51
- expect(value).toBe("Expire Test");
52
-
53
- // 等待过期
54
- await new Promise((resolve) => setTimeout(resolve, 2100));
55
-
56
- const expiredValue = await redis.getString("test:ttl");
57
- expect(expiredValue).toBeNull();
58
- });
59
- });
60
-
61
- describe("RedisHelper - 对象操作", () => {
62
- test("setObject - 设置对象", async () => {
63
- const obj = { name: "Test", age: 25, tags: ["a", "b"] };
64
- const result = await redis.setObject("test:object", obj);
65
- expect(result).toBe("OK");
66
- });
67
-
68
- test("getObject - 获取对象", async () => {
69
- const obj = { name: "Test", age: 25, tags: ["a", "b"] };
70
- await redis.setObject("test:object", obj);
71
-
72
- const value = await redis.getObject<any>("test:object");
73
- expect(value).toEqual(obj);
74
- });
75
-
76
- test("getObject - 获取不存在的对象", async () => {
77
- const value = await redis.getObject("test:non_existent_obj");
78
- expect(value).toBeNull();
79
- });
80
-
81
- test("setObject - 设置带过期时间的对象", async () => {
82
- const obj = { data: "test" };
83
- const result = await redis.setObject("test:object:ttl", obj, 1);
84
- expect(result).toBe("OK");
85
-
86
- await new Promise((resolve) => setTimeout(resolve, 1100));
87
-
88
- const expiredValue = await redis.getObject("test:object:ttl");
89
- expect(expiredValue).toBeNull();
90
- });
91
-
92
- test("delObject - 删除对象", async () => {
93
- await redis.setObject("test:delete", { data: "test" });
94
- await redis.delObject("test:delete");
95
-
96
- const value = await redis.getObject("test:delete");
97
- expect(value).toBeNull();
98
- });
99
- });
100
-
101
- describe("RedisHelper - Set 操作", () => {
102
- test("sadd - 添加成员到 Set", async () => {
103
- // 先清除,确保测试隔离
104
- await redis.del("test:set:sadd");
105
- const count = await redis.sadd("test:set:sadd", ["member1", "member2", "member3"]);
106
- expect(count).toBeGreaterThan(0);
107
- await redis.del("test:set:sadd");
108
- });
109
-
110
- test("sismember - 检查成员是否存在", async () => {
111
- await redis.del("test:set");
112
- await redis.sadd("test:set", ["member1"]);
113
-
114
- const exists = await redis.sismember("test:set", "member1");
115
- expect(exists).toBe(true);
116
-
117
- const notExists = await redis.sismember("test:set", "non_existent");
118
- expect(notExists).toBe(false);
119
- });
120
-
121
- test("scard - 获取 Set 成员数量", async () => {
122
- await redis.del("test:set:count");
123
- await redis.sadd("test:set:count", ["m1", "m2", "m3"]);
124
-
125
- const count = await redis.scard("test:set:count");
126
- expect(count).toBe(3);
127
-
128
- await redis.del("test:set:count");
129
- });
130
-
131
- test("smembers - 获取所有成员", async () => {
132
- await redis.del("test:set:members");
133
- await redis.sadd("test:set:members", ["a", "b", "c"]);
134
-
135
- const members = await redis.smembers("test:set:members");
136
- expect(members.length).toBe(3);
137
- expect(members).toContain("a");
138
- expect(members).toContain("b");
139
- expect(members).toContain("c");
140
-
141
- await redis.del("test:set:members");
142
- });
143
-
144
- test("saddBatch - 批量向多个 Set 添加成员", async () => {
145
- const count = await redis.saddBatch([
146
- { key: "test:saddBatch:1", members: ["a", "b"] },
147
- { key: "test:saddBatch:2", members: ["c", "d", "e"] }
148
- ]);
149
- expect(count).toBe(5);
150
-
151
- // 验证
152
- const members1 = await redis.smembers("test:saddBatch:1");
153
- const members2 = await redis.smembers("test:saddBatch:2");
154
- expect(members1.length).toBe(2);
155
- expect(members2.length).toBe(3);
156
-
157
- // 清理
158
- await redis.delBatch(["test:saddBatch:1", "test:saddBatch:2"]);
159
- });
160
-
161
- test("saddBatch - 空数组返回 0", async () => {
162
- const count = await redis.saddBatch([]);
163
- expect(count).toBe(0);
164
- });
165
-
166
- test("sismemberBatch - 批量检查成员是否存在", async () => {
167
- await redis.sadd("test:sismemberBatch", ["a", "b", "c"]);
168
-
169
- const results = await redis.sismemberBatch([
170
- { key: "test:sismemberBatch", member: "a" },
171
- { key: "test:sismemberBatch", member: "b" },
172
- { key: "test:sismemberBatch", member: "x" }
173
- ]);
174
- expect(results).toEqual([true, true, false]);
175
-
176
- // 清理
177
- await redis.del("test:sismemberBatch");
178
- });
179
-
180
- test("sismemberBatch - 空数组返回空数组", async () => {
181
- const results = await redis.sismemberBatch([]);
182
- expect(results).toEqual([]);
183
- });
184
- });
185
-
186
- describe("RedisHelper - 键操作", () => {
187
- test("exists - 检查键是否存在", async () => {
188
- await redis.setString("test:exists", "value");
189
-
190
- const exists = await redis.exists("test:exists");
191
- expect(exists).toBe(true);
192
-
193
- const notExists = await redis.exists("test:not_exists");
194
- expect(notExists).toBe(false);
195
-
196
- await redis.del("test:exists");
197
- });
198
-
199
- test("expire - 设置过期时间", async () => {
200
- await redis.setString("test:expire", "value");
201
- const result = await redis.expire("test:expire", 1);
202
- expect(result).toBe(1);
203
-
204
- await new Promise((resolve) => setTimeout(resolve, 1100));
205
-
206
- const value = await redis.getString("test:expire");
207
- expect(value).toBeNull();
208
- });
209
-
210
- test("ttl - 获取剩余过期时间", async () => {
211
- await redis.setString("test:ttl:check", "value", 10);
212
-
213
- const ttl = await redis.ttl("test:ttl:check");
214
- expect(ttl).toBeGreaterThan(0);
215
- expect(ttl).toBeLessThanOrEqual(10);
216
-
217
- await redis.del("test:ttl:check");
218
- });
219
-
220
- test("ttlBatch - 批量获取剩余过期时间", async () => {
221
- await redis.setString("test:ttlBatch:1", "value1", 60);
222
- await redis.setString("test:ttlBatch:2", "value2", 120);
223
- // test:ttlBatch:3 不存在
224
-
225
- const results = await redis.ttlBatch(["test:ttlBatch:1", "test:ttlBatch:2", "test:ttlBatch:3"]);
226
- expect(results.length).toBe(3);
227
- expect(results[0]).toBeGreaterThan(0);
228
- expect(results[0]).toBeLessThanOrEqual(60);
229
- expect(results[1]).toBeGreaterThan(0);
230
- expect(results[1]).toBeLessThanOrEqual(120);
231
- expect(results[2]).toBe(-2); // 键不存在
232
-
233
- // 清理
234
- await redis.delBatch(["test:ttlBatch:1", "test:ttlBatch:2"]);
235
- });
236
-
237
- test("ttlBatch - 空数组返回空数组", async () => {
238
- const results = await redis.ttlBatch([]);
239
- expect(results).toEqual([]);
240
- });
241
-
242
- test("ttlBatch - 无过期时间返回 -1", async () => {
243
- await redis.setString("test:ttlBatch:nox", "value"); // 无 TTL
244
-
245
- const results = await redis.ttlBatch(["test:ttlBatch:nox"]);
246
- expect(results[0]).toBe(-1);
247
-
248
- // 清理
249
- await redis.del("test:ttlBatch:nox");
250
- });
251
-
252
- test("del - 删除键", async () => {
253
- await redis.setString("test:delete:key", "value");
254
-
255
- const count = await redis.del("test:delete:key");
256
- expect(count).toBe(1);
257
-
258
- const value = await redis.getString("test:delete:key");
259
- expect(value).toBeNull();
260
- });
261
-
262
- test("delBatch - 批量删除键", async () => {
263
- // 创建多个键
264
- await redis.setString("test:batch:1", "value1");
265
- await redis.setString("test:batch:2", "value2");
266
- await redis.setString("test:batch:3", "value3");
267
-
268
- // 批量删除
269
- const count = await redis.delBatch(["test:batch:1", "test:batch:2", "test:batch:3"]);
270
- expect(count).toBe(3);
271
-
272
- // 验证删除成功
273
- const value1 = await redis.getString("test:batch:1");
274
- const value2 = await redis.getString("test:batch:2");
275
- const value3 = await redis.getString("test:batch:3");
276
- expect(value1).toBeNull();
277
- expect(value2).toBeNull();
278
- expect(value3).toBeNull();
279
- });
280
-
281
- test("delBatch - 空数组返回 0", async () => {
282
- const count = await redis.delBatch([]);
283
- expect(count).toBe(0);
284
- });
285
-
286
- test("delBatch - 删除不存在的键返回 0", async () => {
287
- const count = await redis.delBatch(["test:non:existent:1", "test:non:existent:2"]);
288
- expect(count).toBe(0);
289
- });
290
-
291
- test("delBatch - 部分存在的键", async () => {
292
- await redis.setString("test:partial:1", "value1");
293
- await redis.setString("test:partial:2", "value2");
294
- // test:partial:3 不存在
295
-
296
- const count = await redis.delBatch(["test:partial:1", "test:partial:2", "test:partial:3"]);
297
- expect(count).toBe(2); // 只有 2 个键被删除
298
- });
299
-
300
- test("setBatch - 批量设置对象", async () => {
301
- const items = [
302
- { key: "test:setBatch:1", value: { name: "Alice" } },
303
- { key: "test:setBatch:2", value: { name: "Bob" } },
304
- { key: "test:setBatch:3", value: { name: "Charlie" } }
305
- ];
306
-
307
- const count = await redis.setBatch(items);
308
- expect(count).toBe(3);
309
-
310
- // 验证设置成功
311
- const value1 = await redis.getObject("test:setBatch:1");
312
- const value2 = await redis.getObject("test:setBatch:2");
313
- const value3 = await redis.getObject("test:setBatch:3");
314
- expect(value1).toEqual({ name: "Alice" });
315
- expect(value2).toEqual({ name: "Bob" });
316
- expect(value3).toEqual({ name: "Charlie" });
317
-
318
- // 清理
319
- await redis.delBatch(["test:setBatch:1", "test:setBatch:2", "test:setBatch:3"]);
320
- });
321
-
322
- test("setBatch - 空数组返回 0", async () => {
323
- const count = await redis.setBatch([]);
324
- expect(count).toBe(0);
325
- });
326
-
327
- test("setBatch - 带 TTL 的批量设置", async () => {
328
- const items = [
329
- { key: "test:setBatch:ttl:1", value: { data: 1 }, ttl: 10 },
330
- { key: "test:setBatch:ttl:2", value: { data: 2 }, ttl: 10 }
331
- ];
332
-
333
- const count = await redis.setBatch(items);
334
- expect(count).toBe(2);
335
-
336
- // 验证 TTL 已设置
337
- const ttl1 = await redis.ttl("test:setBatch:ttl:1");
338
- const ttl2 = await redis.ttl("test:setBatch:ttl:2");
339
- expect(ttl1).toBeGreaterThan(0);
340
- expect(ttl1).toBeLessThanOrEqual(10);
341
- expect(ttl2).toBeGreaterThan(0);
342
- expect(ttl2).toBeLessThanOrEqual(10);
343
-
344
- // 清理
345
- await redis.delBatch(["test:setBatch:ttl:1", "test:setBatch:ttl:2"]);
346
- });
347
-
348
- test("getBatch - 批量获取对象", async () => {
349
- // 设置测试数据
350
- await redis.setObject("test:getBatch:1", { name: "Alice" });
351
- await redis.setObject("test:getBatch:2", { name: "Bob" });
352
- await redis.setObject("test:getBatch:3", { name: "Charlie" });
353
-
354
- // 批量获取
355
- const results = await redis.getBatch(["test:getBatch:1", "test:getBatch:2", "test:getBatch:3"]);
356
- expect(results.length).toBe(3);
357
- expect(results[0]).toEqual({ name: "Alice" });
358
- expect(results[1]).toEqual({ name: "Bob" });
359
- expect(results[2]).toEqual({ name: "Charlie" });
360
-
361
- // 清理
362
- await redis.delBatch(["test:getBatch:1", "test:getBatch:2", "test:getBatch:3"]);
363
- });
364
-
365
- test("getBatch - 空数组返回空数组", async () => {
366
- const results = await redis.getBatch([]);
367
- expect(results).toEqual([]);
368
- });
369
-
370
- test("getBatch - 不存在的键返回 null", async () => {
371
- const results = await redis.getBatch(["test:non:existent:a", "test:non:existent:b"]);
372
- expect(results).toEqual([null, null]);
373
- });
374
-
375
- test("getBatch - 部分存在的键", async () => {
376
- await redis.setObject("test:partial:a", { data: "a" });
377
- // test:partial:b 不存在
378
-
379
- const results = await redis.getBatch(["test:partial:a", "test:partial:b"]);
380
- expect(results.length).toBe(2);
381
- expect(results[0]).toEqual({ data: "a" });
382
- expect(results[1]).toBeNull();
383
-
384
- // 清理
385
- await redis.del("test:partial:a");
386
- });
387
-
388
- test("existsBatch - 批量检查键是否存在", async () => {
389
- await redis.setString("test:existsBatch:1", "value1");
390
- await redis.setString("test:existsBatch:2", "value2");
391
- // test:existsBatch:3 不存在
392
-
393
- const results = await redis.existsBatch(["test:existsBatch:1", "test:existsBatch:2", "test:existsBatch:3"]);
394
- expect(results).toEqual([true, true, false]);
395
-
396
- // 清理
397
- await redis.delBatch(["test:existsBatch:1", "test:existsBatch:2"]);
398
- });
399
-
400
- test("existsBatch - 空数组返回空数组", async () => {
401
- const results = await redis.existsBatch([]);
402
- expect(results).toEqual([]);
403
- });
404
-
405
- test("existsBatch - 全部不存在", async () => {
406
- const results = await redis.existsBatch(["test:none:1", "test:none:2"]);
407
- expect(results).toEqual([false, false]);
408
- });
409
-
410
- test("expireBatch - 批量设置过期时间", async () => {
411
- await redis.setString("test:expireBatch:1", "value1");
412
- await redis.setString("test:expireBatch:2", "value2");
413
-
414
- const count = await redis.expireBatch([
415
- { key: "test:expireBatch:1", seconds: 60 },
416
- { key: "test:expireBatch:2", seconds: 120 }
417
- ]);
418
- expect(count).toBe(2);
419
-
420
- // 验证 TTL 已设置
421
- const ttl1 = await redis.ttl("test:expireBatch:1");
422
- const ttl2 = await redis.ttl("test:expireBatch:2");
423
- expect(ttl1).toBeGreaterThan(0);
424
- expect(ttl1).toBeLessThanOrEqual(60);
425
- expect(ttl2).toBeGreaterThan(0);
426
- expect(ttl2).toBeLessThanOrEqual(120);
427
-
428
- // 清理
429
- await redis.delBatch(["test:expireBatch:1", "test:expireBatch:2"]);
430
- });
431
-
432
- test("expireBatch - 空数组返回 0", async () => {
433
- const count = await redis.expireBatch([]);
434
- expect(count).toBe(0);
435
- });
436
-
437
- test("expireBatch - 不存在的键返回 0", async () => {
438
- const count = await redis.expireBatch([
439
- { key: "test:expire:none:1", seconds: 60 },
440
- { key: "test:expire:none:2", seconds: 60 }
441
- ]);
442
- expect(count).toBe(0);
443
- });
444
-
445
- test("expireBatch - 部分存在的键", async () => {
446
- await redis.setString("test:expire:partial:1", "value1");
447
- // test:expire:partial:2 不存在
448
-
449
- const count = await redis.expireBatch([
450
- { key: "test:expire:partial:1", seconds: 60 },
451
- { key: "test:expire:partial:2", seconds: 60 }
452
- ]);
453
- expect(count).toBe(1);
454
-
455
- // 清理
456
- await redis.del("test:expire:partial:1");
457
- });
458
- });
459
-
460
- describe("RedisHelper - ID 生成", () => {
461
- test("genTimeID - 生成唯一 ID", async () => {
462
- const id1 = await redis.genTimeID();
463
- const id2 = await redis.genTimeID();
464
-
465
- expect(typeof id1).toBe("number");
466
- expect(typeof id2).toBe("number");
467
- expect(id1).not.toBe(id2);
468
- expect(id1.toString().length).toBe(16);
469
-
470
- // 验证后缀在 100-999 范围内
471
- const suffix1 = id1 % 1000;
472
- const suffix2 = id2 % 1000;
473
- expect(suffix1).toBeGreaterThanOrEqual(100);
474
- expect(suffix1).toBeLessThan(1000);
475
- expect(suffix2).toBeGreaterThanOrEqual(100);
476
- expect(suffix2).toBeLessThan(1000);
477
- });
478
-
479
- test("genTimeID - 多次生成保持唯一", async () => {
480
- const ids: number[] = [];
481
- for (let i = 0; i < 10; i++) {
482
- ids.push(await redis.genTimeID());
483
- }
484
-
485
- const uniqueIds = new Set(ids);
486
- expect(uniqueIds.size).toBe(10);
487
- });
488
- });
489
-
490
- describe("RedisHelper - 连接测试", () => {
491
- test("ping - 测试连接", async () => {
492
- const result = await redis.ping();
493
- expect(result).toBe("PONG");
494
- });
495
- });
@@ -1,9 +0,0 @@
1
- /**
2
- * 已弃用:原文件内容与 cacheKeys.test.ts 重复。
3
- *
4
- * 说明:
5
- * - 优先保留并维护 cacheKeys.test.ts
6
- * - 这里保留空文件仅为兼容历史文件名(避免某些工具/脚本引用路径时报错)
7
- */
8
-
9
- export {};
@@ -1,144 +0,0 @@
1
- import { describe, expect, test, beforeAll, afterAll, beforeEach } from "bun:test";
2
- import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
- import { join } from "node:path";
4
-
5
- import { scanConfig } from "../utils/scanConfig";
6
-
7
- const tempRootDir = join(process.cwd(), "temp", "test-scan-config");
8
-
9
- function writeJson(filePath: string, data: Record<string, any>) {
10
- const json = JSON.stringify(data, null, 4);
11
- writeFileSync(filePath, json, { encoding: "utf8" });
12
- }
13
-
14
- function createCaseDirs(): { caseRootDir: string; dirA: string; dirB: string } {
15
- const caseRootDir = join(tempRootDir, `case-${Date.now()}-${Math.random().toString(16).slice(2)}`);
16
- const dirA = join(caseRootDir, "a");
17
- const dirB = join(caseRootDir, "b");
18
-
19
- if (!existsSync(dirA)) {
20
- mkdirSync(dirA, { recursive: true });
21
- }
22
- if (!existsSync(dirB)) {
23
- mkdirSync(dirB, { recursive: true });
24
- }
25
-
26
- return {
27
- caseRootDir: caseRootDir,
28
- dirA: dirA,
29
- dirB: dirB
30
- };
31
- }
32
-
33
- afterAll(() => {
34
- if (existsSync(tempRootDir)) {
35
- rmSync(tempRootDir, { recursive: true, force: true });
36
- }
37
- });
38
-
39
- describe("utils - scanConfig", () => {
40
- beforeAll(() => {
41
- if (!existsSync(tempRootDir)) {
42
- mkdirSync(tempRootDir, { recursive: true });
43
- }
44
- });
45
-
46
- beforeEach(() => {
47
- // 无需清空:每个用例使用唯一目录,避免 import cache 干扰
48
- });
49
-
50
- test("mode=first:返回搜索到的第一个配置(按 dirs 顺序)", async () => {
51
- const { caseRootDir, dirA, dirB } = createCaseDirs();
52
-
53
- writeJson(join(dirA, "cfg.json"), {
54
- foo: 1,
55
- database: { host: "a" }
56
- });
57
- writeJson(join(dirB, "cfg.json"), {
58
- foo: 2,
59
- database: { host: "b" }
60
- });
61
-
62
- const config = await scanConfig({
63
- cwd: caseRootDir,
64
- dirs: ["a", "b"],
65
- files: ["cfg"],
66
- extensions: [".json"],
67
- mode: "first"
68
- });
69
-
70
- expect(config).toEqual({
71
- foo: 1,
72
- database: { host: "a" }
73
- });
74
- });
75
-
76
- test("mode=merge:按 defaults ← a ← b 合并(数组拼接,标量后者覆盖)", async () => {
77
- const { caseRootDir, dirA, dirB } = createCaseDirs();
78
-
79
- writeJson(join(dirA, "cfg.json"), {
80
- foo: 1,
81
- menus: ["a"],
82
- database: { host: "a", port: 3306 },
83
- nested: { a: 1 }
84
- });
85
- writeJson(join(dirB, "cfg.json"), {
86
- foo: 2,
87
- menus: ["b"],
88
- database: { host: "b" },
89
- nested: { b: 2 }
90
- });
91
-
92
- const config = await scanConfig({
93
- cwd: caseRootDir,
94
- dirs: ["a", "b"],
95
- files: ["cfg"],
96
- extensions: [".json"],
97
- mode: "merge",
98
- defaults: {
99
- foo: 0,
100
- menus: ["default"],
101
- database: { host: "default", port: 1111 },
102
- nested: { z: 9 }
103
- }
104
- });
105
-
106
- // merge-anything 的 mergeAndConcat:数组拼接;对象深合并;标量后者覆盖
107
- expect(config.foo).toBe(2);
108
- expect(config.menus).toEqual(["default", "a", "b"]);
109
- expect(config.database).toEqual({
110
- host: "b",
111
- port: 3306
112
- });
113
- expect(config.nested).toEqual({
114
- z: 9,
115
- a: 1,
116
- b: 2
117
- });
118
- });
119
-
120
- test("paths:只返回指定路径字段", async () => {
121
- const { caseRootDir, dirA } = createCaseDirs();
122
-
123
- writeJson(join(dirA, "cfg.json"), {
124
- foo: 1,
125
- menus: ["a"],
126
- database: { host: "a", port: 3306 },
127
- secret: "should-not-return"
128
- });
129
-
130
- const config = await scanConfig({
131
- cwd: caseRootDir,
132
- dirs: ["a"],
133
- files: ["cfg"],
134
- extensions: [".json"],
135
- mode: "first",
136
- paths: ["menus", "database.host", "not.exists"]
137
- });
138
-
139
- expect(config).toEqual({
140
- menus: ["a"],
141
- database: { host: "a" }
142
- });
143
- });
144
- });
@@ -1,46 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { fileURLToPath } from "node:url";
3
-
4
- import { scanFiles } from "../utils/scanFiles.js";
5
-
6
- describe("scanFiles - api routePath formatting", () => {
7
- test("routePrefix 应为 /core|/app|/addon 且 routePath 不应出现 /api//", async () => {
8
- const fixturesDir = fileURLToPath(new URL("./fixtures/scanFilesApis", import.meta.url));
9
- const addonApisDir = fileURLToPath(new URL("./fixtures/scanFilesAddon/node_modules/@befly-addon/demo/apis", import.meta.url));
10
-
11
- const coreApis = await scanFiles(fixturesDir, "core", "api", "**/*.ts");
12
- const appApis = await scanFiles(fixturesDir, "app", "api", "**/*.ts");
13
- const addonApis = await scanFiles(addonApisDir, "addon", "api", "**/*.ts");
14
-
15
- const all = ([] as any[]).concat(coreApis as any, appApis as any, addonApis as any);
16
- expect(all.length).toBeGreaterThan(0);
17
-
18
- for (const api of all) {
19
- expect(typeof api.routePrefix).toBe("string");
20
- expect(typeof api.routePath).toBe("string");
21
-
22
- if (api.source === "addon") {
23
- expect(api.routePrefix.startsWith("/addon/")).toBe(true);
24
- expect(typeof api.addonName).toBe("string");
25
- expect(api.addonName.length > 0).toBe(true);
26
- expect(api.routePrefix).toBe(`/addon/${api.addonName}`);
27
- } else {
28
- expect(["/core", "/app"].includes(api.routePrefix)).toBe(true);
29
- }
30
- expect(api.routePath.includes("/api//")).toBe(false);
31
- }
32
-
33
- const coreB = (coreApis as any[]).find((item) => item.relativePath === "sub/b");
34
- expect(coreB.routePrefix).toBe("/core");
35
- expect(coreB.routePath).toBe("/api/core/sub/b");
36
-
37
- const appB = (appApis as any[]).find((item) => item.relativePath === "sub/b");
38
- expect(appB.routePrefix).toBe("/app");
39
- expect(appB.routePath).toBe("/api/app/sub/b");
40
-
41
- const addonB = (addonApis as any[]).find((item) => item.relativePath === "sub/b");
42
- expect(addonB.addonName).toBe("demo");
43
- expect(addonB.routePrefix).toBe("/addon/demo");
44
- expect(addonB.routePath).toBe("/api/addon/demo/sub/b");
45
- });
46
- });