befly 3.8.27 → 3.8.30

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 (65) hide show
  1. package/README.md +8 -6
  2. package/checks/checkApi.ts +2 -1
  3. package/checks/checkTable.ts +3 -2
  4. package/hooks/parser.ts +5 -3
  5. package/hooks/permission.ts +12 -5
  6. package/lib/cacheHelper.ts +76 -62
  7. package/lib/connect.ts +8 -35
  8. package/lib/dbHelper.ts +14 -11
  9. package/lib/jwt.ts +58 -437
  10. package/lib/logger.ts +76 -197
  11. package/lib/redisHelper.ts +163 -1
  12. package/lib/sqlBuilder.ts +2 -1
  13. package/lib/validator.ts +9 -8
  14. package/loader/loadApis.ts +4 -7
  15. package/loader/loadHooks.ts +2 -2
  16. package/loader/loadPlugins.ts +4 -4
  17. package/main.ts +4 -17
  18. package/package.json +10 -9
  19. package/paths.ts +0 -6
  20. package/plugins/db.ts +2 -2
  21. package/plugins/jwt.ts +5 -5
  22. package/plugins/redis.ts +1 -1
  23. package/router/api.ts +2 -2
  24. package/router/static.ts +1 -2
  25. package/sync/syncAll.ts +2 -2
  26. package/sync/syncApi.ts +10 -7
  27. package/sync/syncDb/apply.ts +11 -11
  28. package/sync/syncDb/constants.ts +61 -12
  29. package/sync/syncDb/ddl.ts +7 -7
  30. package/sync/syncDb/helpers.ts +3 -3
  31. package/sync/syncDb/schema.ts +16 -19
  32. package/sync/syncDb/table.ts +6 -5
  33. package/sync/syncDb/tableCreate.ts +7 -7
  34. package/sync/syncDb/types.ts +3 -2
  35. package/sync/syncDb/version.ts +4 -4
  36. package/sync/syncDb.ts +11 -10
  37. package/sync/syncDev.ts +10 -48
  38. package/sync/syncMenu.ts +11 -8
  39. package/tests/cacheHelper.test.ts +327 -0
  40. package/tests/dbHelper-columns.test.ts +5 -20
  41. package/tests/dbHelper-execute.test.ts +14 -68
  42. package/tests/fields-redis-cache.test.ts +5 -3
  43. package/tests/integration.test.ts +15 -26
  44. package/tests/jwt.test.ts +36 -94
  45. package/tests/logger.test.ts +32 -34
  46. package/tests/redisHelper.test.ts +270 -0
  47. package/tests/redisKeys.test.ts +76 -0
  48. package/tests/sync-connection.test.ts +0 -6
  49. package/tests/syncDb-apply.test.ts +3 -2
  50. package/tests/syncDb-constants.test.ts +15 -14
  51. package/tests/syncDb-ddl.test.ts +3 -2
  52. package/tests/syncDb-helpers.test.ts +3 -2
  53. package/tests/syncDb-schema.test.ts +3 -3
  54. package/tests/syncDb-types.test.ts +3 -2
  55. package/tests/util.test.ts +5 -1
  56. package/types/befly.d.ts +2 -15
  57. package/types/common.d.ts +11 -93
  58. package/types/database.d.ts +216 -5
  59. package/types/index.ts +1 -0
  60. package/types/logger.d.ts +11 -41
  61. package/types/table.d.ts +213 -0
  62. package/hooks/_rateLimit.ts +0 -64
  63. package/lib/regexAliases.ts +0 -59
  64. package/lib/xml.ts +0 -383
  65. package/tests/xml.test.ts +0 -101
@@ -0,0 +1,327 @@
1
+ /**
2
+ * CacheHelper 单元测试
3
+ */
4
+ import { describe, it, expect, beforeEach, afterEach, mock } from 'bun:test';
5
+ import { CacheHelper } from '../lib/cacheHelper.js';
6
+ import { Logger, setMockLogger } from '../lib/logger.js';
7
+ import { RedisKeys } from 'befly-shared/redisKeys';
8
+
9
+ import type { BeflyContext } from '../types/befly.js';
10
+
11
+ // Mock pino logger
12
+ const mockPino = {
13
+ info: mock(() => {}),
14
+ warn: mock(() => {}),
15
+ error: mock(() => {}),
16
+ debug: mock(() => {}),
17
+ fatal: mock(() => {}),
18
+ trace: mock(() => {}),
19
+ silent: mock(() => {}),
20
+ child: mock(() => mockPino),
21
+ level: 'info'
22
+ };
23
+
24
+ describe('CacheHelper', () => {
25
+ let cacheHelper: CacheHelper;
26
+ let mockBefly: BeflyContext;
27
+ let mockDb: any;
28
+ let mockRedis: any;
29
+
30
+ beforeEach(() => {
31
+ // 设置 mock logger
32
+ setMockLogger(mockPino as any);
33
+
34
+ // Mock 数据库方法
35
+ mockDb = {
36
+ tableExists: mock(() => Promise.resolve(true)),
37
+ getAll: mock(() => Promise.resolve([]))
38
+ };
39
+
40
+ // Mock Redis 方法
41
+ mockRedis = {
42
+ setObject: mock(() => Promise.resolve('OK')),
43
+ getObject: mock(() => Promise.resolve(null)),
44
+ sadd: mock(() => Promise.resolve(1)),
45
+ smembers: mock(() => Promise.resolve([])),
46
+ sismember: mock(() => Promise.resolve(0)),
47
+ del: mock(() => Promise.resolve(1))
48
+ };
49
+
50
+ // 创建 mock befly context
51
+ mockBefly = {
52
+ db: mockDb,
53
+ redis: mockRedis
54
+ } as unknown as BeflyContext;
55
+
56
+ cacheHelper = new CacheHelper(mockBefly);
57
+ });
58
+
59
+ afterEach(() => {
60
+ // 重置 mock logger
61
+ setMockLogger(null);
62
+ });
63
+
64
+ describe('cacheApis', () => {
65
+ it('表不存在时跳过缓存', async () => {
66
+ mockDb.tableExists = mock(() => Promise.resolve(false));
67
+
68
+ await cacheHelper.cacheApis();
69
+
70
+ expect(mockDb.tableExists).toHaveBeenCalledWith('addon_admin_api');
71
+ expect(mockDb.getAll).not.toHaveBeenCalled();
72
+ });
73
+
74
+ it('正常缓存接口列表', async () => {
75
+ const apis = [
76
+ { id: 1, name: '登录', path: '/api/login', method: 'POST' },
77
+ { id: 2, name: '用户列表', path: '/api/user/list', method: 'GET' }
78
+ ];
79
+ mockDb.getAll = mock(() => Promise.resolve(apis));
80
+
81
+ await cacheHelper.cacheApis();
82
+
83
+ expect(mockRedis.setObject).toHaveBeenCalledWith(RedisKeys.apisAll(), apis);
84
+ });
85
+
86
+ it('缓存失败时记录警告', async () => {
87
+ mockRedis.setObject = mock(() => Promise.resolve(null));
88
+
89
+ await cacheHelper.cacheApis();
90
+
91
+ expect(mockPino.warn).toHaveBeenCalled();
92
+ });
93
+
94
+ it('异常时记录错误', async () => {
95
+ mockDb.getAll = mock(() => Promise.reject(new Error('DB Error')));
96
+
97
+ await cacheHelper.cacheApis();
98
+
99
+ expect(mockPino.error).toHaveBeenCalled();
100
+ });
101
+ });
102
+
103
+ describe('cacheMenus', () => {
104
+ it('表不存在时跳过缓存', async () => {
105
+ mockDb.tableExists = mock(() => Promise.resolve(false));
106
+
107
+ await cacheHelper.cacheMenus();
108
+
109
+ expect(mockDb.tableExists).toHaveBeenCalledWith('addon_admin_menu');
110
+ expect(mockDb.getAll).not.toHaveBeenCalled();
111
+ });
112
+
113
+ it('正常缓存菜单列表', async () => {
114
+ const menus = [
115
+ { id: 1, pid: 0, name: '首页', path: '/home', sort: 1 },
116
+ { id: 2, pid: 0, name: '用户管理', path: '/user', sort: 2 }
117
+ ];
118
+ mockDb.getAll = mock(() => Promise.resolve(menus));
119
+
120
+ await cacheHelper.cacheMenus();
121
+
122
+ expect(mockRedis.setObject).toHaveBeenCalledWith(RedisKeys.menusAll(), menus);
123
+ });
124
+ });
125
+
126
+ describe('cacheRolePermissions', () => {
127
+ it('表不存在时跳过缓存', async () => {
128
+ mockDb.tableExists = mock((table: string) => {
129
+ if (table === 'addon_admin_api') return Promise.resolve(true);
130
+ if (table === 'addon_admin_role') return Promise.resolve(false);
131
+ return Promise.resolve(false);
132
+ });
133
+
134
+ await cacheHelper.cacheRolePermissions();
135
+
136
+ expect(mockPino.warn).toHaveBeenCalled();
137
+ });
138
+
139
+ it('正常缓存角色权限', async () => {
140
+ const roles = [
141
+ { id: 1, code: 'admin', apis: '1,2,3' },
142
+ { id: 2, code: 'user', apis: '1' }
143
+ ];
144
+ const apis = [
145
+ { id: 1, path: '/api/login', method: 'POST' },
146
+ { id: 2, path: '/api/user/list', method: 'GET' },
147
+ { id: 3, path: '/api/user/del', method: 'POST' }
148
+ ];
149
+
150
+ mockDb.getAll = mock((opts: any) => {
151
+ if (opts.table === 'addon_admin_role') return Promise.resolve(roles);
152
+ if (opts.table === 'addon_admin_api') return Promise.resolve(apis);
153
+ return Promise.resolve([]);
154
+ });
155
+
156
+ await cacheHelper.cacheRolePermissions();
157
+
158
+ // 验证批量删除调用
159
+ expect(mockRedis.del).toHaveBeenCalledTimes(2);
160
+
161
+ // 验证批量添加调用
162
+ expect(mockRedis.sadd).toHaveBeenCalledTimes(2);
163
+ expect(mockRedis.sadd).toHaveBeenCalledWith(RedisKeys.roleApis('admin'), ['POST/api/login', 'GET/api/user/list', 'POST/api/user/del']);
164
+ expect(mockRedis.sadd).toHaveBeenCalledWith(RedisKeys.roleApis('user'), ['POST/api/login']);
165
+ });
166
+
167
+ it('无有效角色时不执行缓存', async () => {
168
+ const roles = [{ id: 1, code: 'empty', apis: null }];
169
+ const apis = [{ id: 1, path: '/api/login', method: 'POST' }];
170
+
171
+ mockDb.getAll = mock((opts: any) => {
172
+ if (opts.table === 'addon_admin_role') return Promise.resolve(roles);
173
+ if (opts.table === 'addon_admin_api') return Promise.resolve(apis);
174
+ return Promise.resolve([]);
175
+ });
176
+
177
+ await cacheHelper.cacheRolePermissions();
178
+
179
+ expect(mockRedis.sadd).not.toHaveBeenCalled();
180
+ });
181
+
182
+ it('使用 Promise.all 并行执行', async () => {
183
+ const roles = [
184
+ { id: 1, code: 'role1', apis: '1' },
185
+ { id: 2, code: 'role2', apis: '1' },
186
+ { id: 3, code: 'role3', apis: '1' }
187
+ ];
188
+ const apis = [{ id: 1, path: '/api/test', method: 'GET' }];
189
+
190
+ mockDb.getAll = mock((opts: any) => {
191
+ if (opts.table === 'addon_admin_role') return Promise.resolve(roles);
192
+ if (opts.table === 'addon_admin_api') return Promise.resolve(apis);
193
+ return Promise.resolve([]);
194
+ });
195
+
196
+ await cacheHelper.cacheRolePermissions();
197
+
198
+ // 3 个角色应该有 3 次删除和 3 次添加操作
199
+ expect(mockRedis.del).toHaveBeenCalledTimes(3);
200
+ expect(mockRedis.sadd).toHaveBeenCalledTimes(3);
201
+ });
202
+ });
203
+
204
+ describe('getApis', () => {
205
+ it('返回缓存的接口列表', async () => {
206
+ const apis = [{ id: 1, name: '登录' }];
207
+ mockRedis.getObject = mock(() => Promise.resolve(apis));
208
+
209
+ const result = await cacheHelper.getApis();
210
+
211
+ expect(result).toEqual(apis);
212
+ });
213
+
214
+ it('缓存不存在时返回空数组', async () => {
215
+ mockRedis.getObject = mock(() => Promise.resolve(null));
216
+
217
+ const result = await cacheHelper.getApis();
218
+
219
+ expect(result).toEqual([]);
220
+ });
221
+ });
222
+
223
+ describe('getMenus', () => {
224
+ it('返回缓存的菜单列表', async () => {
225
+ const menus = [{ id: 1, name: '首页' }];
226
+ mockRedis.getObject = mock(() => Promise.resolve(menus));
227
+
228
+ const result = await cacheHelper.getMenus();
229
+
230
+ expect(result).toEqual(menus);
231
+ });
232
+
233
+ it('缓存不存在时返回空数组', async () => {
234
+ mockRedis.getObject = mock(() => Promise.resolve(null));
235
+
236
+ const result = await cacheHelper.getMenus();
237
+
238
+ expect(result).toEqual([]);
239
+ });
240
+ });
241
+
242
+ describe('getRolePermissions', () => {
243
+ it('返回角色的权限列表', async () => {
244
+ const permissions = ['POST/api/login', 'GET/api/user/list'];
245
+ mockRedis.smembers = mock(() => Promise.resolve(permissions));
246
+
247
+ const result = await cacheHelper.getRolePermissions('admin');
248
+
249
+ expect(mockRedis.smembers).toHaveBeenCalledWith(RedisKeys.roleApis('admin'));
250
+ expect(result).toEqual(permissions);
251
+ });
252
+
253
+ it('权限不存在时返回空数组', async () => {
254
+ mockRedis.smembers = mock(() => Promise.resolve(null));
255
+
256
+ const result = await cacheHelper.getRolePermissions('unknown');
257
+
258
+ expect(result).toEqual([]);
259
+ });
260
+ });
261
+
262
+ describe('checkRolePermission', () => {
263
+ it('有权限时返回 true', async () => {
264
+ mockRedis.sismember = mock(() => Promise.resolve(1));
265
+
266
+ const result = await cacheHelper.checkRolePermission('admin', 'POST/api/login');
267
+
268
+ expect(mockRedis.sismember).toHaveBeenCalledWith(RedisKeys.roleApis('admin'), 'POST/api/login');
269
+ expect(result).toBe(true);
270
+ });
271
+
272
+ it('无权限时返回 false', async () => {
273
+ mockRedis.sismember = mock(() => Promise.resolve(0));
274
+
275
+ const result = await cacheHelper.checkRolePermission('user', 'POST/api/admin/del');
276
+
277
+ expect(result).toBe(false);
278
+ });
279
+ });
280
+
281
+ describe('deleteRolePermissions', () => {
282
+ it('删除成功返回 true', async () => {
283
+ mockRedis.del = mock(() => Promise.resolve(1));
284
+
285
+ const result = await cacheHelper.deleteRolePermissions('admin');
286
+
287
+ expect(mockRedis.del).toHaveBeenCalledWith(RedisKeys.roleApis('admin'));
288
+ expect(result).toBe(true);
289
+ });
290
+
291
+ it('不存在时返回 false', async () => {
292
+ mockRedis.del = mock(() => Promise.resolve(0));
293
+
294
+ const result = await cacheHelper.deleteRolePermissions('unknown');
295
+
296
+ expect(result).toBe(false);
297
+ });
298
+ });
299
+
300
+ describe('cacheAll', () => {
301
+ it('按顺序调用三个缓存方法', async () => {
302
+ const callOrder: string[] = [];
303
+
304
+ // 使用 spy 记录调用顺序
305
+ const originalCacheApis = cacheHelper.cacheApis.bind(cacheHelper);
306
+ const originalCacheMenus = cacheHelper.cacheMenus.bind(cacheHelper);
307
+ const originalCacheRolePermissions = cacheHelper.cacheRolePermissions.bind(cacheHelper);
308
+
309
+ cacheHelper.cacheApis = async () => {
310
+ callOrder.push('apis');
311
+ await originalCacheApis();
312
+ };
313
+ cacheHelper.cacheMenus = async () => {
314
+ callOrder.push('menus');
315
+ await originalCacheMenus();
316
+ };
317
+ cacheHelper.cacheRolePermissions = async () => {
318
+ callOrder.push('permissions');
319
+ await originalCacheRolePermissions();
320
+ };
321
+
322
+ await cacheHelper.cacheAll();
323
+
324
+ expect(callOrder).toEqual(['apis', 'menus', 'permissions']);
325
+ });
326
+ });
327
+ });
@@ -3,24 +3,9 @@
3
3
  * 测试表字段查询、Redis 缓存、SQL 语法修复等功能
4
4
  */
5
5
 
6
- import { test, expect, mock, beforeEach, afterEach } from 'bun:test';
6
+ import { test, expect, mock } from 'bun:test';
7
7
  import { DbHelper } from '../lib/dbHelper.js';
8
- import { Logger } from '../lib/logger.js';
9
-
10
- // Mock Logger
11
- const originalLoggerError = Logger.error;
12
- let errorLogs: string[] = [];
13
-
14
- beforeEach(() => {
15
- errorLogs = [];
16
- Logger.error = mock((msg: string) => {
17
- errorLogs.push(msg);
18
- });
19
- });
20
-
21
- afterEach(() => {
22
- Logger.error = originalLoggerError;
23
- });
8
+ import { RedisKeys } from 'befly-shared/redisKeys';
24
9
 
25
10
  // 创建 Mock Befly 上下文
26
11
  function createMockBefly(sqlMock: any, redisMock?: any) {
@@ -58,7 +43,7 @@ test('getTableColumns - 正常查询表字段', async () => {
58
43
 
59
44
  expect(columns).toEqual(['id', 'username', 'email', 'created_at']);
60
45
  expect(sqlMock.unsafe).toHaveBeenCalledTimes(1);
61
- expect(redisMock.getObject).toHaveBeenCalledWith('table:columns:users');
46
+ expect(redisMock.getObject).toHaveBeenCalledWith(RedisKeys.tableColumns('users'));
62
47
  expect(redisMock.setObject).toHaveBeenCalled();
63
48
  });
64
49
 
@@ -82,7 +67,7 @@ test('getTableColumns - Redis 缓存命中', async () => {
82
67
  const columns = await (dbHelper as any).getTableColumns('users');
83
68
 
84
69
  expect(columns).toEqual(cachedColumns);
85
- expect(redisMock.getObject).toHaveBeenCalledWith('table:columns:users');
70
+ expect(redisMock.getObject).toHaveBeenCalledWith(RedisKeys.tableColumns('users'));
86
71
  expect(sqlMock.unsafe).not.toHaveBeenCalled(); // SQL 不应该被调用
87
72
  expect(redisMock.setObject).not.toHaveBeenCalled(); // 不需要写缓存
88
73
  });
@@ -159,7 +144,7 @@ test('getTableColumns - 缓存键格式正确', async () => {
159
144
  getObject: mock(async () => null),
160
145
  setObject: mock(async (key: string, value: any, seconds: number) => {
161
146
  // 验证缓存键格式
162
- expect(key).toBe('table:columns:test_table');
147
+ expect(key).toBe(RedisKeys.tableColumns('test_table'));
163
148
  // 验证缓存值格式
164
149
  expect(Array.isArray(value)).toBe(true);
165
150
  // 验证过期时间
@@ -1,33 +1,10 @@
1
1
  /**
2
2
  * DbHelper executeWithConn 方法单元测试
3
- * 测试 SQL 执行、错误处理、慢查询日志等功能
3
+ * 测试 SQL 执行、错误处理等功能
4
4
  */
5
5
 
6
- import { test, expect, mock, beforeEach, afterEach } from 'bun:test';
6
+ import { test, expect, mock } from 'bun:test';
7
7
  import { DbHelper } from '../lib/dbHelper.js';
8
- import { Logger } from '../lib/logger.js';
9
-
10
- // Mock Logger
11
- const originalLoggerError = Logger.error;
12
- const originalLoggerWarn = Logger.warn;
13
- let errorLogs: string[] = [];
14
- let warnLogs: string[] = [];
15
-
16
- beforeEach(() => {
17
- errorLogs = [];
18
- warnLogs = [];
19
- Logger.error = mock((msg: string) => {
20
- errorLogs.push(msg);
21
- });
22
- Logger.warn = mock((msg: string) => {
23
- warnLogs.push(msg);
24
- });
25
- });
26
-
27
- afterEach(() => {
28
- Logger.error = originalLoggerError;
29
- Logger.warn = originalLoggerWarn;
30
- });
31
8
 
32
9
  // 创建 Mock Befly 上下文
33
10
  function createMockBefly(sqlMock: any) {
@@ -55,7 +32,6 @@ test('executeWithConn - 正常执行(无参数)', async () => {
55
32
 
56
33
  expect(result).toEqual(mockResult);
57
34
  expect(sqlMock.unsafe).toHaveBeenCalledWith('SELECT * FROM users');
58
- expect(errorLogs.length).toBe(0);
59
35
  });
60
36
 
61
37
  test('executeWithConn - 正常执行(带参数)', async () => {
@@ -71,7 +47,6 @@ test('executeWithConn - 正常执行(带参数)', async () => {
71
47
 
72
48
  expect(result).toEqual(mockResult);
73
49
  expect(sqlMock.unsafe).toHaveBeenCalledWith('SELECT * FROM users WHERE id = ?', [1]);
74
- expect(errorLogs.length).toBe(0);
75
50
  });
76
51
 
77
52
  test('executeWithConn - SQL 错误捕获', async () => {
@@ -95,16 +70,10 @@ test('executeWithConn - SQL 错误捕获', async () => {
95
70
  expect(error.sql).toBe('SELECT * FROM invalid_table');
96
71
  expect(error.params).toEqual([]);
97
72
  expect(error.duration).toBeGreaterThanOrEqual(0);
98
-
99
- // 验证错误日志
100
- expect(errorLogs.length).toBeGreaterThan(0);
101
- expect(errorLogs.some((log) => log.includes('SQL 执行错误'))).toBe(true);
102
- expect(errorLogs.some((log) => log.includes('SELECT * FROM invalid_table'))).toBe(true);
103
- expect(errorLogs.some((log) => log.includes('You have an error in your SQL syntax'))).toBe(true);
104
73
  }
105
74
  });
106
75
 
107
- test('executeWithConn - 错误日志包含完整信息', async () => {
76
+ test('executeWithConn - 错误信息包含完整信息', async () => {
108
77
  const sqlMock = {
109
78
  unsafe: mock(async () => {
110
79
  throw new Error('Syntax error near "??"');
@@ -124,20 +93,11 @@ test('executeWithConn - 错误日志包含完整信息', async () => {
124
93
  expect(error.sql).toBe(testSql);
125
94
  expect(error.params).toEqual(testParams);
126
95
  expect(typeof error.duration).toBe('number');
127
-
128
- // 验证日志内容
129
- const allLogs = errorLogs.join('\n');
130
- expect(allLogs).toContain('SQL 语句:');
131
- expect(allLogs).toContain('SHOW COLUMNS FROM ??');
132
- expect(allLogs).toContain('参数列表:');
133
- expect(allLogs).toContain('["users"]');
134
- expect(allLogs).toContain('执行耗时:');
135
- expect(allLogs).toContain('错误信息:');
136
- expect(allLogs).toContain('Syntax error near "??"');
96
+ expect(error.originalError.message).toBe('Syntax error near "??"');
137
97
  }
138
98
  });
139
99
 
140
- test('executeWithConn - 超长 SQL 截断', async () => {
100
+ test('executeWithConn - 超长 SQL 保留在错误对象中', async () => {
141
101
  const longSql = 'SELECT * FROM users WHERE ' + 'id = ? AND '.repeat(50) + 'name = ?';
142
102
  const sqlMock = {
143
103
  unsafe: mock(async () => {
@@ -151,20 +111,13 @@ test('executeWithConn - 超长 SQL 截断', async () => {
151
111
  try {
152
112
  await (dbHelper as any).executeWithConn(longSql);
153
113
  } catch (error: any) {
154
- // SQL 应该被截断
155
- expect(error.sql).toBe(longSql); // 完整保存在错误对象中
156
-
157
- // 日志中应该截断并加 ...
158
- const sqlLog = errorLogs.find((log) => log.includes('SQL 语句:'));
159
- expect(sqlLog).toBeDefined();
160
- if (sqlLog) {
161
- expect(sqlLog.length).toBeLessThan(longSql.length + 50); // 截断后应该更短
162
- expect(sqlLog).toContain('...');
163
- }
114
+ // SQL 完整保存在错误对象中
115
+ expect(error.sql).toBe(longSql);
116
+ expect(error.params).toEqual([]);
164
117
  }
165
118
  });
166
119
 
167
- test('executeWithConn - 慢查询日志(>1000ms)', async () => {
120
+ test('executeWithConn - 慢查询检测(>1000ms)', async () => {
168
121
  const mockResult = [{ id: 1 }];
169
122
  const sqlMock = {
170
123
  unsafe: mock(async () => {
@@ -179,10 +132,8 @@ test('executeWithConn - 慢查询日志(>1000ms)', async () => {
179
132
 
180
133
  const result = await (dbHelper as any).executeWithConn('SELECT SLEEP(1)');
181
134
 
135
+ // 功能仍正常返回结果
182
136
  expect(result).toEqual(mockResult);
183
- expect(warnLogs.length).toBeGreaterThan(0);
184
- expect(warnLogs.some((log) => log.includes('🐌 检测到慢查询'))).toBe(true);
185
- expect(warnLogs.some((log) => log.includes('ms'))).toBe(true);
186
137
  });
187
138
 
188
139
  test('executeWithConn - 数据库未连接错误', async () => {
@@ -213,7 +164,7 @@ test('executeWithConn - 空参数数组', async () => {
213
164
  expect(sqlMock.unsafe).toHaveBeenCalledWith('SELECT COUNT(*) as count FROM users');
214
165
  });
215
166
 
216
- test('executeWithConn - 参数 JSON 序列化', async () => {
167
+ test('executeWithConn - 复杂参数处理', async () => {
217
168
  const sqlMock = {
218
169
  unsafe: mock(async () => {
219
170
  throw new Error('Test error');
@@ -228,13 +179,8 @@ test('executeWithConn - 参数 JSON 序列化', async () => {
228
179
  try {
229
180
  await (dbHelper as any).executeWithConn('SELECT ?', complexParams);
230
181
  } catch (error: any) {
231
- // 验证参数被正确序列化
232
- const paramsLog = errorLogs.find((log) => log.includes('参数列表:'));
233
- expect(paramsLog).toBeDefined();
234
- if (paramsLog) {
235
- // JSON.stringify 应该能处理复杂参数
236
- expect(paramsLog).toContain('参数列表:');
237
- expect(() => JSON.parse(paramsLog.split('参数列表:')[1].trim())).not.toThrow();
238
- }
182
+ // 验证参数被正确保存
183
+ expect(error.params).toEqual(complexParams);
184
+ expect(error.sql).toBe('SELECT ?');
239
185
  }
240
186
  });
@@ -2,6 +2,8 @@
2
2
  * 验证 Redis 缓存的字段查询功能
3
3
  */
4
4
 
5
+ import { RedisKeys, RedisTTL } from 'befly-shared/redisKeys';
6
+
5
7
  console.log('\n========== Redis 缓存验证 ==========\n');
6
8
 
7
9
  // 模拟 Redis 缓存逻辑
@@ -38,7 +40,7 @@ async function queryDatabase(table: string): Promise<string[]> {
38
40
  // 模拟 getTableColumns 方法
39
41
  async function getTableColumns(redis: MockRedis, table: string): Promise<string[]> {
40
42
  // 1. 先查 Redis 缓存
41
- const cacheKey = `table:columns:${table}`;
43
+ const cacheKey = RedisKeys.tableColumns(table);
42
44
  let columns = await redis.getObject<string[]>(cacheKey);
43
45
 
44
46
  if (columns && columns.length > 0) {
@@ -48,8 +50,8 @@ async function getTableColumns(redis: MockRedis, table: string): Promise<string[
48
50
  // 2. 缓存未命中,查询数据库
49
51
  columns = await queryDatabase(table);
50
52
 
51
- // 3. 写入 Redis 缓存(1小时过期)
52
- await redis.setObject(cacheKey, columns, 3600);
53
+ // 3. 写入 Redis 缓存
54
+ await redis.setObject(cacheKey, columns, RedisTTL.tableColumns);
53
55
 
54
56
  return columns;
55
57
  }
@@ -1,10 +1,11 @@
1
- import { describe, test, expect, beforeAll } from 'bun:test';
1
+ import { describe, test, expect } from 'bun:test';
2
+ import { XMLParser } from 'fast-xml-parser';
2
3
  import { Cipher } from '../lib/cipher';
3
4
  import { Jwt } from '../lib/jwt';
4
5
  import { Validator } from '../lib/validator';
5
6
  import { SqlBuilder } from '../lib/sqlBuilder';
6
- import { Xml } from '../lib/xml';
7
- import { keysToCamel, keysToSnake } from 'befly-util';
7
+ import { keysToCamel } from 'befly-shared/keysToCamel';
8
+ import { keysToSnake } from 'befly-shared/keysToSnake';
8
9
 
9
10
  describe('Integration - 密码验证流程', () => {
10
11
  test('用户注册:密码加密 + 验证', async () => {
@@ -26,15 +27,13 @@ describe('Integration - 密码验证流程', () => {
26
27
  });
27
28
 
28
29
  describe('Integration - JWT + 权限验证', () => {
29
- beforeAll(() => {
30
- Jwt.configure({
31
- secret: 'test-integration-secret',
32
- algorithm: 'HS256',
33
- expiresIn: '1h'
34
- });
30
+ const jwt = new Jwt({
31
+ secret: 'test-integration-secret',
32
+ algorithm: 'HS256',
33
+ expiresIn: '1h'
35
34
  });
36
35
 
37
- test('用户登录:JWT 签名 + 权限检查', async () => {
36
+ test('用户登录:JWT 签名 + 验证', () => {
38
37
  // 1. 用户登录生成 token
39
38
  const payload = {
40
39
  userId: 123,
@@ -43,26 +42,16 @@ describe('Integration - JWT + 权限验证', () => {
43
42
  permissions: ['read', 'write', 'delete']
44
43
  };
45
44
 
46
- const token = await Jwt.sign(payload);
45
+ const token = jwt.sign(payload);
47
46
  expect(token).toBeDefined();
48
47
  expect(typeof token).toBe('string');
49
48
 
50
49
  // 2. 验证 token
51
- const verified = await Jwt.verify(token);
50
+ const verified = jwt.verify(token);
52
51
  expect(verified.userId).toBe(123);
53
52
  expect(verified.username).toBe('john');
54
-
55
- // 3. 检查角色
56
- expect(Jwt.hasRole(verified, 'admin')).toBe(true);
57
- expect(Jwt.hasRole(verified, 'guest')).toBe(false);
58
-
59
- // 4. 检查权限
60
- expect(Jwt.hasPermission(verified, 'write')).toBe(true);
61
- expect(Jwt.hasPermission(verified, 'admin')).toBe(false);
62
-
63
- // 5. 检查所有权限
64
- expect(Jwt.hasAllPermissions(verified, ['read', 'write'])).toBe(true);
65
- expect(Jwt.hasAllPermissions(verified, ['read', 'admin'])).toBe(false);
53
+ expect(verified.roles).toContain('admin');
54
+ expect(verified.permissions).toContain('write');
66
55
  });
67
56
  });
68
57
 
@@ -133,11 +122,11 @@ describe('Integration - 数据验证 + SQL 构建', () => {
133
122
 
134
123
  describe('Integration - XML 解析 + 数据转换', () => {
135
124
  test('XML API 响应:解析 + 字段转换', () => {
136
- const xml = new Xml();
125
+ const xmlParser = new XMLParser();
137
126
 
138
127
  // 1. 解析 XML 响应
139
128
  const xmlData = '<response><user_id>123</user_id><user_name>John</user_name><is_active>true</is_active></response>';
140
- const parsed = xml.parse(xmlData) as any;
129
+ const parsed = xmlParser.parse(xmlData).response as any;
141
130
 
142
131
  expect(parsed.user_id).toBe(123);
143
132
  expect(parsed.user_name).toBe('John');