befly 3.9.38 → 3.9.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -38
- package/befly.config.ts +62 -40
- package/checks/checkApi.ts +16 -16
- package/checks/checkApp.ts +19 -25
- package/checks/checkTable.ts +42 -42
- package/docs/README.md +42 -35
- package/docs/{api.md → api/api.md} +223 -231
- package/docs/cipher.md +71 -69
- package/docs/database.md +143 -141
- package/docs/{examples.md → guide/examples.md} +181 -181
- package/docs/guide/quickstart.md +331 -0
- package/docs/hooks/auth.md +38 -0
- package/docs/hooks/cors.md +28 -0
- package/docs/{hook.md → hooks/hook.md} +140 -57
- package/docs/hooks/parser.md +19 -0
- package/docs/hooks/rateLimit.md +47 -0
- package/docs/{redis.md → infra/redis.md} +84 -93
- package/docs/plugins/cipher.md +61 -0
- package/docs/plugins/database.md +128 -0
- package/docs/{plugin.md → plugins/plugin.md} +83 -81
- package/docs/quickstart.md +26 -26
- package/docs/{addon.md → reference/addon.md} +46 -46
- package/docs/{config.md → reference/config.md} +32 -80
- package/docs/{logger.md → reference/logger.md} +52 -52
- package/docs/{sync.md → reference/sync.md} +32 -35
- package/docs/{table.md → reference/table.md} +1 -1
- package/docs/{validator.md → reference/validator.md} +57 -57
- package/hooks/auth.ts +8 -4
- package/hooks/cors.ts +13 -13
- package/hooks/parser.ts +37 -17
- package/hooks/permission.ts +26 -14
- package/hooks/rateLimit.ts +276 -0
- package/hooks/validator.ts +8 -8
- package/lib/asyncContext.ts +43 -0
- package/lib/cacheHelper.ts +212 -77
- package/lib/cacheKeys.ts +38 -0
- package/lib/cipher.ts +30 -30
- package/lib/connect.ts +28 -28
- package/lib/dbHelper.ts +183 -102
- package/lib/jwt.ts +16 -16
- package/lib/logger.ts +610 -19
- package/lib/redisHelper.ts +185 -44
- package/lib/sqlBuilder.ts +90 -91
- package/lib/validator.ts +59 -39
- package/loader/loadApis.ts +48 -44
- package/loader/loadHooks.ts +40 -14
- package/loader/loadPlugins.ts +16 -17
- package/main.ts +57 -47
- package/package.json +47 -45
- package/paths.ts +15 -14
- package/plugins/cache.ts +5 -4
- package/plugins/cipher.ts +3 -3
- package/plugins/config.ts +2 -2
- package/plugins/db.ts +9 -9
- package/plugins/jwt.ts +3 -3
- package/plugins/logger.ts +8 -12
- package/plugins/redis.ts +8 -8
- package/plugins/tool.ts +6 -6
- package/router/api.ts +85 -56
- package/router/static.ts +12 -12
- package/sync/syncAll.ts +12 -12
- package/sync/syncApi.ts +55 -52
- package/sync/syncDb/apply.ts +20 -19
- package/sync/syncDb/constants.ts +25 -23
- package/sync/syncDb/ddl.ts +35 -36
- package/sync/syncDb/helpers.ts +6 -9
- package/sync/syncDb/schema.ts +10 -9
- package/sync/syncDb/sqlite.ts +7 -8
- package/sync/syncDb/table.ts +37 -35
- package/sync/syncDb/tableCreate.ts +21 -20
- package/sync/syncDb/types.ts +23 -20
- package/sync/syncDb/version.ts +10 -10
- package/sync/syncDb.ts +43 -36
- package/sync/syncDev.ts +74 -65
- package/sync/syncMenu.ts +190 -55
- package/tests/api-integration-array-number.test.ts +282 -0
- package/tests/befly-config-env.test.ts +78 -0
- package/tests/cacheHelper.test.ts +135 -104
- package/tests/cacheKeys.test.ts +41 -0
- package/tests/cipher.test.ts +90 -89
- package/tests/dbHelper-advanced.test.ts +140 -134
- package/tests/dbHelper-all-array-types.test.ts +316 -0
- package/tests/dbHelper-array-serialization.test.ts +258 -0
- package/tests/dbHelper-columns.test.ts +56 -55
- package/tests/dbHelper-execute.test.ts +45 -44
- package/tests/dbHelper-joins.test.ts +124 -119
- package/tests/fields-redis-cache.test.ts +29 -27
- package/tests/fields-validate.test.ts +38 -38
- package/tests/getClientIp.test.ts +54 -0
- package/tests/integration.test.ts +69 -67
- package/tests/jwt.test.ts +27 -26
- package/tests/logger.test.ts +267 -34
- package/tests/rateLimit-hook.test.ts +477 -0
- package/tests/redisHelper.test.ts +187 -188
- package/tests/redisKeys.test.ts +6 -73
- package/tests/scanConfig.test.ts +144 -0
- package/tests/sqlBuilder-advanced.test.ts +217 -215
- package/tests/sqlBuilder.test.ts +92 -91
- package/tests/sync-connection.test.ts +29 -29
- package/tests/syncDb-apply.test.ts +97 -96
- package/tests/syncDb-array-number.test.ts +160 -0
- package/tests/syncDb-constants.test.ts +48 -47
- package/tests/syncDb-ddl.test.ts +99 -98
- package/tests/syncDb-helpers.test.ts +29 -28
- package/tests/syncDb-schema.test.ts +61 -60
- package/tests/syncDb-types.test.ts +60 -59
- package/tests/syncMenu-paths.test.ts +68 -0
- package/tests/util.test.ts +42 -41
- package/tests/validator-array-number.test.ts +310 -0
- package/tests/validator-default.test.ts +373 -0
- package/tests/validator.test.ts +271 -266
- package/tsconfig.json +4 -5
- package/types/api.d.ts +7 -12
- package/types/befly.d.ts +60 -13
- package/types/cache.d.ts +8 -4
- package/types/common.d.ts +17 -9
- package/types/context.d.ts +2 -2
- package/types/crypto.d.ts +23 -0
- package/types/database.d.ts +19 -19
- package/types/hook.d.ts +2 -2
- package/types/jwt.d.ts +118 -0
- package/types/logger.d.ts +30 -0
- package/types/plugin.d.ts +4 -4
- package/types/redis.d.ts +7 -3
- package/types/roleApisCache.ts +23 -0
- package/types/sync.d.ts +10 -10
- package/types/table.d.ts +50 -9
- package/types/validate.d.ts +69 -0
- package/utils/addonHelper.ts +90 -0
- package/utils/arrayKeysToCamel.ts +18 -0
- package/utils/calcPerfTime.ts +13 -0
- package/utils/configTypes.ts +3 -0
- package/utils/cors.ts +19 -0
- package/utils/fieldClear.ts +75 -0
- package/utils/genShortId.ts +12 -0
- package/utils/getClientIp.ts +45 -0
- package/utils/keysToCamel.ts +22 -0
- package/utils/keysToSnake.ts +22 -0
- package/utils/modules.ts +98 -0
- package/utils/pickFields.ts +19 -0
- package/utils/process.ts +56 -0
- package/utils/regex.ts +225 -0
- package/utils/response.ts +115 -0
- package/utils/route.ts +23 -0
- package/utils/scanConfig.ts +142 -0
- package/utils/scanFiles.ts +48 -0
- package/.prettierignore +0 -2
- package/.prettierrc +0 -12
- package/docs/1-/345/237/272/346/234/254/344/273/213/347/273/215.md +0 -35
- package/docs/2-/345/210/235/346/255/245/344/275/223/351/252/214.md +0 -64
- package/docs/3-/347/254/254/344/270/200/344/270/252/346/216/245/345/217/243.md +0 -46
- package/docs/4-/346/223/215/344/275/234/346/225/260/346/215/256/345/272/223.md +0 -172
- package/hooks/requestLogger.ts +0 -84
- package/types/index.ts +0 -24
- package/util.ts +0 -283
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* 测试表字段查询、Redis 缓存、SQL 语法修复等功能
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { test, expect, mock } from
|
|
7
|
-
|
|
8
|
-
import {
|
|
6
|
+
import { test, expect, mock } from "bun:test";
|
|
7
|
+
|
|
8
|
+
import { CacheKeys } from "../lib/cacheKeys.js";
|
|
9
|
+
import { DbHelper } from "../lib/dbHelper.js";
|
|
9
10
|
|
|
10
11
|
// 创建 Mock Befly 上下文
|
|
11
12
|
function createMockBefly(sqlMock: any, redisMock?: any) {
|
|
@@ -19,13 +20,13 @@ function createMockBefly(sqlMock: any, redisMock?: any) {
|
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
test(
|
|
23
|
-
const mockColumns = [{ Field:
|
|
23
|
+
test("getTableColumns - 正常查询表字段", async () => {
|
|
24
|
+
const mockColumns = [{ Field: "id" }, { Field: "username" }, { Field: "email" }, { Field: "created_at" }];
|
|
24
25
|
|
|
25
26
|
const sqlMock = {
|
|
26
27
|
unsafe: mock(async (sql: string) => {
|
|
27
28
|
// 验证 SQL 语法正确(使用反引号)
|
|
28
|
-
expect(sql).toBe(
|
|
29
|
+
expect(sql).toBe("SHOW COLUMNS FROM `users`");
|
|
29
30
|
return mockColumns;
|
|
30
31
|
})
|
|
31
32
|
};
|
|
@@ -39,16 +40,16 @@ test('getTableColumns - 正常查询表字段', async () => {
|
|
|
39
40
|
const befly = createMockBefly(sqlMock, redisMock);
|
|
40
41
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
41
42
|
|
|
42
|
-
const columns = await (dbHelper as any).getTableColumns(
|
|
43
|
+
const columns = await (dbHelper as any).getTableColumns("users");
|
|
43
44
|
|
|
44
|
-
expect(columns).toEqual([
|
|
45
|
+
expect(columns).toEqual(["id", "username", "email", "created_at"]);
|
|
45
46
|
expect(sqlMock.unsafe).toHaveBeenCalledTimes(1);
|
|
46
|
-
expect(redisMock.getObject).toHaveBeenCalledWith(
|
|
47
|
+
expect(redisMock.getObject).toHaveBeenCalledWith(CacheKeys.tableColumns("users"));
|
|
47
48
|
expect(redisMock.setObject).toHaveBeenCalled();
|
|
48
49
|
});
|
|
49
50
|
|
|
50
|
-
test(
|
|
51
|
-
const cachedColumns = [
|
|
51
|
+
test("getTableColumns - Redis 缓存命中", async () => {
|
|
52
|
+
const cachedColumns = ["id", "name", "email"];
|
|
52
53
|
const redisMock = {
|
|
53
54
|
getObject: mock(async () => cachedColumns),
|
|
54
55
|
setObject: mock(async () => true),
|
|
@@ -57,22 +58,22 @@ test('getTableColumns - Redis 缓存命中', async () => {
|
|
|
57
58
|
|
|
58
59
|
const sqlMock = {
|
|
59
60
|
unsafe: mock(async () => {
|
|
60
|
-
throw new Error(
|
|
61
|
+
throw new Error("不应该执行 SQL 查询");
|
|
61
62
|
})
|
|
62
63
|
};
|
|
63
64
|
|
|
64
65
|
const befly = createMockBefly(sqlMock, redisMock);
|
|
65
66
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
66
67
|
|
|
67
|
-
const columns = await (dbHelper as any).getTableColumns(
|
|
68
|
+
const columns = await (dbHelper as any).getTableColumns("users");
|
|
68
69
|
|
|
69
70
|
expect(columns).toEqual(cachedColumns);
|
|
70
|
-
expect(redisMock.getObject).toHaveBeenCalledWith(
|
|
71
|
+
expect(redisMock.getObject).toHaveBeenCalledWith(CacheKeys.tableColumns("users"));
|
|
71
72
|
expect(sqlMock.unsafe).not.toHaveBeenCalled(); // SQL 不应该被调用
|
|
72
73
|
expect(redisMock.setObject).not.toHaveBeenCalled(); // 不需要写缓存
|
|
73
74
|
});
|
|
74
75
|
|
|
75
|
-
test(
|
|
76
|
+
test("getTableColumns - 表不存在错误", async () => {
|
|
76
77
|
const sqlMock = {
|
|
77
78
|
unsafe: mock(async () => []) // 返回空结果
|
|
78
79
|
};
|
|
@@ -87,16 +88,16 @@ test('getTableColumns - 表不存在错误', async () => {
|
|
|
87
88
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
88
89
|
|
|
89
90
|
try {
|
|
90
|
-
await (dbHelper as any).getTableColumns(
|
|
91
|
+
await (dbHelper as any).getTableColumns("non_existent_table");
|
|
91
92
|
expect(true).toBe(false); // 不应该执行到这里
|
|
92
93
|
} catch (error: any) {
|
|
93
|
-
expect(error.message).toContain(
|
|
94
|
+
expect(error.message).toContain("表 non_existent_table 不存在或没有字段");
|
|
94
95
|
}
|
|
95
96
|
});
|
|
96
97
|
|
|
97
|
-
test(
|
|
98
|
-
const mockColumns = [{ Field:
|
|
99
|
-
let capturedSql =
|
|
98
|
+
test("getTableColumns - SQL 语法使用反引号", async () => {
|
|
99
|
+
const mockColumns = [{ Field: "id" }];
|
|
100
|
+
let capturedSql = "";
|
|
100
101
|
|
|
101
102
|
const sqlMock = {
|
|
102
103
|
unsafe: mock(async (sql: string) => {
|
|
@@ -108,16 +109,16 @@ test('getTableColumns - SQL 语法使用反引号', async () => {
|
|
|
108
109
|
const befly = createMockBefly(sqlMock);
|
|
109
110
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
110
111
|
|
|
111
|
-
await (dbHelper as any).getTableColumns(
|
|
112
|
+
await (dbHelper as any).getTableColumns("addon_admin_user");
|
|
112
113
|
|
|
113
114
|
// 验证 SQL 语法
|
|
114
|
-
expect(capturedSql).toBe(
|
|
115
|
-
expect(capturedSql).not.toContain(
|
|
116
|
-
expect(capturedSql).toContain(
|
|
115
|
+
expect(capturedSql).toBe("SHOW COLUMNS FROM `addon_admin_user`");
|
|
116
|
+
expect(capturedSql).not.toContain("??"); // 不应该包含占位符
|
|
117
|
+
expect(capturedSql).toContain("`"); // 应该使用反引号
|
|
117
118
|
});
|
|
118
119
|
|
|
119
|
-
test(
|
|
120
|
-
const mockColumns = [{ Field:
|
|
120
|
+
test("getTableColumns - 表名特殊字符处理", async () => {
|
|
121
|
+
const mockColumns = [{ Field: "id" }];
|
|
121
122
|
const sqlMock = {
|
|
122
123
|
unsafe: mock(async () => mockColumns)
|
|
123
124
|
};
|
|
@@ -126,16 +127,16 @@ test('getTableColumns - 表名特殊字符处理', async () => {
|
|
|
126
127
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
127
128
|
|
|
128
129
|
// 测试下划线表名
|
|
129
|
-
await (dbHelper as any).getTableColumns(
|
|
130
|
-
expect(sqlMock.unsafe).toHaveBeenLastCalledWith(
|
|
130
|
+
await (dbHelper as any).getTableColumns("addon_admin_user");
|
|
131
|
+
expect(sqlMock.unsafe).toHaveBeenLastCalledWith("SHOW COLUMNS FROM `addon_admin_user`");
|
|
131
132
|
|
|
132
133
|
// 测试普通表名
|
|
133
|
-
await (dbHelper as any).getTableColumns(
|
|
134
|
-
expect(sqlMock.unsafe).toHaveBeenLastCalledWith(
|
|
134
|
+
await (dbHelper as any).getTableColumns("users");
|
|
135
|
+
expect(sqlMock.unsafe).toHaveBeenLastCalledWith("SHOW COLUMNS FROM `users`");
|
|
135
136
|
});
|
|
136
137
|
|
|
137
|
-
test(
|
|
138
|
-
const mockColumns = [{ Field:
|
|
138
|
+
test("getTableColumns - 缓存键格式正确", async () => {
|
|
139
|
+
const mockColumns = [{ Field: "id" }];
|
|
139
140
|
const sqlMock = {
|
|
140
141
|
unsafe: mock(async () => mockColumns)
|
|
141
142
|
};
|
|
@@ -144,7 +145,7 @@ test('getTableColumns - 缓存键格式正确', async () => {
|
|
|
144
145
|
getObject: mock(async () => null),
|
|
145
146
|
setObject: mock(async (key: string, value: any, seconds: number) => {
|
|
146
147
|
// 验证缓存键格式
|
|
147
|
-
expect(key).toBe(
|
|
148
|
+
expect(key).toBe(CacheKeys.tableColumns("test_table"));
|
|
148
149
|
// 验证缓存值格式
|
|
149
150
|
expect(Array.isArray(value)).toBe(true);
|
|
150
151
|
// 验证过期时间
|
|
@@ -157,13 +158,13 @@ test('getTableColumns - 缓存键格式正确', async () => {
|
|
|
157
158
|
const befly = createMockBefly(sqlMock, redisMock);
|
|
158
159
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
159
160
|
|
|
160
|
-
await (dbHelper as any).getTableColumns(
|
|
161
|
+
await (dbHelper as any).getTableColumns("test_table");
|
|
161
162
|
|
|
162
163
|
expect(redisMock.setObject).toHaveBeenCalled();
|
|
163
164
|
});
|
|
164
165
|
|
|
165
|
-
test(
|
|
166
|
-
const mockColumns = [{ Field:
|
|
166
|
+
test("getTableColumns - 多次调用相同表(缓存效果)", async () => {
|
|
167
|
+
const mockColumns = [{ Field: "id" }, { Field: "name" }];
|
|
167
168
|
let sqlCallCount = 0;
|
|
168
169
|
|
|
169
170
|
const sqlMock = {
|
|
@@ -187,30 +188,30 @@ test('getTableColumns - 多次调用相同表(缓存效果)', async () => {
|
|
|
187
188
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
188
189
|
|
|
189
190
|
// 第一次调用 - 应该查询数据库
|
|
190
|
-
const columns1 = await (dbHelper as any).getTableColumns(
|
|
191
|
-
expect(columns1).toEqual([
|
|
191
|
+
const columns1 = await (dbHelper as any).getTableColumns("users");
|
|
192
|
+
expect(columns1).toEqual(["id", "name"]);
|
|
192
193
|
expect(sqlCallCount).toBe(1);
|
|
193
194
|
|
|
194
195
|
// 第二次调用 - 应该从缓存读取
|
|
195
|
-
const columns2 = await (dbHelper as any).getTableColumns(
|
|
196
|
-
expect(columns2).toEqual([
|
|
196
|
+
const columns2 = await (dbHelper as any).getTableColumns("users");
|
|
197
|
+
expect(columns2).toEqual(["id", "name"]);
|
|
197
198
|
expect(sqlCallCount).toBe(1); // SQL 调用次数不变
|
|
198
199
|
|
|
199
200
|
// 第三次调用 - 仍然从缓存读取
|
|
200
|
-
const columns3 = await (dbHelper as any).getTableColumns(
|
|
201
|
-
expect(columns3).toEqual([
|
|
201
|
+
const columns3 = await (dbHelper as any).getTableColumns("users");
|
|
202
|
+
expect(columns3).toEqual(["id", "name"]);
|
|
202
203
|
expect(sqlCallCount).toBe(1);
|
|
203
204
|
});
|
|
204
205
|
|
|
205
|
-
test(
|
|
206
|
-
const mockColumns = [{ Field:
|
|
206
|
+
test("getTableColumns - Redis 错误处理", async () => {
|
|
207
|
+
const mockColumns = [{ Field: "id" }];
|
|
207
208
|
const sqlMock = {
|
|
208
209
|
unsafe: mock(async () => mockColumns)
|
|
209
210
|
};
|
|
210
211
|
|
|
211
212
|
const redisMock = {
|
|
212
213
|
getObject: mock(async () => {
|
|
213
|
-
throw new Error(
|
|
214
|
+
throw new Error("Redis 连接失败");
|
|
214
215
|
}),
|
|
215
216
|
setObject: mock(async () => true),
|
|
216
217
|
del: mock(async () => 1)
|
|
@@ -220,19 +221,19 @@ test('getTableColumns - Redis 错误处理', async () => {
|
|
|
220
221
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
221
222
|
|
|
222
223
|
try {
|
|
223
|
-
await (dbHelper as any).getTableColumns(
|
|
224
|
+
await (dbHelper as any).getTableColumns("users");
|
|
224
225
|
expect(true).toBe(false); // 不应该执行到这里
|
|
225
226
|
} catch (error: any) {
|
|
226
|
-
expect(error.message).toContain(
|
|
227
|
+
expect(error.message).toContain("Redis 连接失败");
|
|
227
228
|
}
|
|
228
229
|
});
|
|
229
230
|
|
|
230
|
-
test(
|
|
231
|
+
test("getTableColumns - 字段映射正确性", async () => {
|
|
231
232
|
const mockColumns = [
|
|
232
|
-
{ Field:
|
|
233
|
-
{ Field:
|
|
234
|
-
{ Field:
|
|
235
|
-
{ Field:
|
|
233
|
+
{ Field: "id", Type: "int" },
|
|
234
|
+
{ Field: "user_name", Type: "varchar" },
|
|
235
|
+
{ Field: "created_at", Type: "timestamp" },
|
|
236
|
+
{ Field: "is_active", Type: "tinyint" }
|
|
236
237
|
];
|
|
237
238
|
|
|
238
239
|
const sqlMock = {
|
|
@@ -242,10 +243,10 @@ test('getTableColumns - 字段映射正确性', async () => {
|
|
|
242
243
|
const befly = createMockBefly(sqlMock);
|
|
243
244
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
244
245
|
|
|
245
|
-
const columns = await (dbHelper as any).getTableColumns(
|
|
246
|
+
const columns = await (dbHelper as any).getTableColumns("users");
|
|
246
247
|
|
|
247
248
|
// 验证只提取 Field 字段
|
|
248
|
-
expect(columns).toEqual([
|
|
249
|
+
expect(columns).toEqual(["id", "user_name", "created_at", "is_active"]);
|
|
249
250
|
expect(columns.length).toBe(4);
|
|
250
|
-
expect(columns.every((col: string) => typeof col ===
|
|
251
|
+
expect(columns.every((col: string) => typeof col === "string")).toBe(true);
|
|
251
252
|
});
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* 测试 SQL 执行、错误处理等功能
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { test, expect, mock } from
|
|
7
|
-
|
|
6
|
+
import { test, expect, mock } from "bun:test";
|
|
7
|
+
|
|
8
|
+
import { DbHelper } from "../lib/dbHelper.js";
|
|
8
9
|
|
|
9
10
|
// 创建 Mock Befly 上下文
|
|
10
|
-
function createMockBefly(
|
|
11
|
+
function createMockBefly() {
|
|
11
12
|
return {
|
|
12
13
|
redis: {
|
|
13
14
|
get: mock(async () => null),
|
|
@@ -18,73 +19,73 @@ function createMockBefly(sqlMock: any) {
|
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
test(
|
|
22
|
-
const mockResult = [{ id: 1, name:
|
|
22
|
+
test("executeWithConn - 正常执行(无参数)", async () => {
|
|
23
|
+
const mockResult = [{ id: 1, name: "test" }];
|
|
23
24
|
const sqlMock = {
|
|
24
25
|
unsafe: mock(async () => mockResult)
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
const befly = createMockBefly(
|
|
28
|
+
const befly = createMockBefly();
|
|
28
29
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
29
30
|
|
|
30
31
|
// 使用反射访问私有方法
|
|
31
|
-
const result = await (dbHelper as any).executeWithConn(
|
|
32
|
+
const result = await (dbHelper as any).executeWithConn("SELECT * FROM users");
|
|
32
33
|
|
|
33
34
|
expect(result).toEqual(mockResult);
|
|
34
|
-
expect(sqlMock.unsafe).toHaveBeenCalledWith(
|
|
35
|
+
expect(sqlMock.unsafe).toHaveBeenCalledWith("SELECT * FROM users");
|
|
35
36
|
});
|
|
36
37
|
|
|
37
|
-
test(
|
|
38
|
-
const mockResult = [{ id: 1, email:
|
|
38
|
+
test("executeWithConn - 正常执行(带参数)", async () => {
|
|
39
|
+
const mockResult = [{ id: 1, email: "test@example.com" }];
|
|
39
40
|
const sqlMock = {
|
|
40
41
|
unsafe: mock(async () => mockResult)
|
|
41
42
|
};
|
|
42
43
|
|
|
43
|
-
const befly = createMockBefly(
|
|
44
|
+
const befly = createMockBefly();
|
|
44
45
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
45
46
|
|
|
46
|
-
const result = await (dbHelper as any).executeWithConn(
|
|
47
|
+
const result = await (dbHelper as any).executeWithConn("SELECT * FROM users WHERE id = ?", [1]);
|
|
47
48
|
|
|
48
49
|
expect(result).toEqual(mockResult);
|
|
49
|
-
expect(sqlMock.unsafe).toHaveBeenCalledWith(
|
|
50
|
+
expect(sqlMock.unsafe).toHaveBeenCalledWith("SELECT * FROM users WHERE id = ?", [1]);
|
|
50
51
|
});
|
|
51
52
|
|
|
52
|
-
test(
|
|
53
|
-
const sqlError = new Error(
|
|
53
|
+
test("executeWithConn - SQL 错误捕获", async () => {
|
|
54
|
+
const sqlError = new Error("You have an error in your SQL syntax");
|
|
54
55
|
const sqlMock = {
|
|
55
56
|
unsafe: mock(async () => {
|
|
56
57
|
throw sqlError;
|
|
57
58
|
})
|
|
58
59
|
};
|
|
59
60
|
|
|
60
|
-
const befly = createMockBefly(
|
|
61
|
+
const befly = createMockBefly();
|
|
61
62
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
62
63
|
|
|
63
64
|
try {
|
|
64
|
-
await (dbHelper as any).executeWithConn(
|
|
65
|
+
await (dbHelper as any).executeWithConn("SELECT * FROM invalid_table");
|
|
65
66
|
expect(true).toBe(false); // 不应该执行到这里
|
|
66
67
|
} catch (error: any) {
|
|
67
68
|
// 验证错误信息
|
|
68
|
-
expect(error.message).toContain(
|
|
69
|
+
expect(error.message).toContain("SQL执行失败");
|
|
69
70
|
expect(error.originalError).toBe(sqlError);
|
|
70
|
-
expect(error.sql).toBe(
|
|
71
|
+
expect(error.sql).toBe("SELECT * FROM invalid_table");
|
|
71
72
|
expect(error.params).toEqual([]);
|
|
72
73
|
expect(error.duration).toBeGreaterThanOrEqual(0);
|
|
73
74
|
}
|
|
74
75
|
});
|
|
75
76
|
|
|
76
|
-
test(
|
|
77
|
+
test("executeWithConn - 错误信息包含完整信息", async () => {
|
|
77
78
|
const sqlMock = {
|
|
78
79
|
unsafe: mock(async () => {
|
|
79
80
|
throw new Error('Syntax error near "??"');
|
|
80
81
|
})
|
|
81
82
|
};
|
|
82
83
|
|
|
83
|
-
const befly = createMockBefly(
|
|
84
|
+
const befly = createMockBefly();
|
|
84
85
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
85
86
|
|
|
86
|
-
const testSql =
|
|
87
|
-
const testParams = [
|
|
87
|
+
const testSql = "SHOW COLUMNS FROM ??";
|
|
88
|
+
const testParams = ["users"];
|
|
88
89
|
|
|
89
90
|
try {
|
|
90
91
|
await (dbHelper as any).executeWithConn(testSql, testParams);
|
|
@@ -92,20 +93,20 @@ test('executeWithConn - 错误信息包含完整信息', async () => {
|
|
|
92
93
|
// 验证增强的错误对象
|
|
93
94
|
expect(error.sql).toBe(testSql);
|
|
94
95
|
expect(error.params).toEqual(testParams);
|
|
95
|
-
expect(typeof error.duration).toBe(
|
|
96
|
+
expect(typeof error.duration).toBe("number");
|
|
96
97
|
expect(error.originalError.message).toBe('Syntax error near "??"');
|
|
97
98
|
}
|
|
98
99
|
});
|
|
99
100
|
|
|
100
|
-
test(
|
|
101
|
-
const longSql =
|
|
101
|
+
test("executeWithConn - 超长 SQL 保留在错误对象中", async () => {
|
|
102
|
+
const longSql = "SELECT * FROM users WHERE " + "id = ? AND ".repeat(50) + "name = ?";
|
|
102
103
|
const sqlMock = {
|
|
103
104
|
unsafe: mock(async () => {
|
|
104
|
-
throw new Error(
|
|
105
|
+
throw new Error("Test error");
|
|
105
106
|
})
|
|
106
107
|
};
|
|
107
108
|
|
|
108
|
-
const befly = createMockBefly(
|
|
109
|
+
const befly = createMockBefly();
|
|
109
110
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
110
111
|
|
|
111
112
|
try {
|
|
@@ -117,7 +118,7 @@ test('executeWithConn - 超长 SQL 保留在错误对象中', async () => {
|
|
|
117
118
|
}
|
|
118
119
|
});
|
|
119
120
|
|
|
120
|
-
test(
|
|
121
|
+
test("executeWithConn - 慢查询检测(>1000ms)", async () => {
|
|
121
122
|
const mockResult = [{ id: 1 }];
|
|
122
123
|
const sqlMock = {
|
|
123
124
|
unsafe: mock(async () => {
|
|
@@ -127,60 +128,60 @@ test('executeWithConn - 慢查询检测(>1000ms)', async () => {
|
|
|
127
128
|
})
|
|
128
129
|
};
|
|
129
130
|
|
|
130
|
-
const befly = createMockBefly(
|
|
131
|
+
const befly = createMockBefly();
|
|
131
132
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
132
133
|
|
|
133
|
-
const result = await (dbHelper as any).executeWithConn(
|
|
134
|
+
const result = await (dbHelper as any).executeWithConn("SELECT SLEEP(1)");
|
|
134
135
|
|
|
135
136
|
// 功能仍正常返回结果
|
|
136
137
|
expect(result).toEqual(mockResult);
|
|
137
138
|
});
|
|
138
139
|
|
|
139
|
-
test(
|
|
140
|
+
test("executeWithConn - 数据库未连接错误", async () => {
|
|
140
141
|
const befly = createMockBefly(null);
|
|
141
142
|
const dbHelper = new DbHelper(befly as any, null); // 没有 sql 实例
|
|
142
143
|
|
|
143
144
|
try {
|
|
144
|
-
await (dbHelper as any).executeWithConn(
|
|
145
|
+
await (dbHelper as any).executeWithConn("SELECT * FROM users");
|
|
145
146
|
expect(true).toBe(false); // 不应该执行到这里
|
|
146
147
|
} catch (error: any) {
|
|
147
|
-
expect(error.message).toBe(
|
|
148
|
+
expect(error.message).toBe("数据库连接未初始化");
|
|
148
149
|
}
|
|
149
150
|
});
|
|
150
151
|
|
|
151
|
-
test(
|
|
152
|
+
test("executeWithConn - 空参数数组", async () => {
|
|
152
153
|
const mockResult = [{ count: 10 }];
|
|
153
154
|
const sqlMock = {
|
|
154
155
|
unsafe: mock(async () => mockResult)
|
|
155
156
|
};
|
|
156
157
|
|
|
157
|
-
const befly = createMockBefly(
|
|
158
|
+
const befly = createMockBefly();
|
|
158
159
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
159
160
|
|
|
160
|
-
const result = await (dbHelper as any).executeWithConn(
|
|
161
|
+
const result = await (dbHelper as any).executeWithConn("SELECT COUNT(*) as count FROM users", []);
|
|
161
162
|
|
|
162
163
|
expect(result).toEqual(mockResult);
|
|
163
164
|
// 空数组应该走 else 分支(不传参数)
|
|
164
|
-
expect(sqlMock.unsafe).toHaveBeenCalledWith(
|
|
165
|
+
expect(sqlMock.unsafe).toHaveBeenCalledWith("SELECT COUNT(*) as count FROM users");
|
|
165
166
|
});
|
|
166
167
|
|
|
167
|
-
test(
|
|
168
|
+
test("executeWithConn - 复杂参数处理", async () => {
|
|
168
169
|
const sqlMock = {
|
|
169
170
|
unsafe: mock(async () => {
|
|
170
|
-
throw new Error(
|
|
171
|
+
throw new Error("Test error");
|
|
171
172
|
})
|
|
172
173
|
};
|
|
173
174
|
|
|
174
|
-
const befly = createMockBefly(
|
|
175
|
+
const befly = createMockBefly();
|
|
175
176
|
const dbHelper = new DbHelper(befly as any, sqlMock);
|
|
176
177
|
|
|
177
|
-
const complexParams = [1,
|
|
178
|
+
const complexParams = [1, "test", { nested: "object" }, [1, 2, 3], null, undefined];
|
|
178
179
|
|
|
179
180
|
try {
|
|
180
|
-
await (dbHelper as any).executeWithConn(
|
|
181
|
+
await (dbHelper as any).executeWithConn("SELECT ?", complexParams);
|
|
181
182
|
} catch (error: any) {
|
|
182
183
|
// 验证参数被正确保存
|
|
183
184
|
expect(error.params).toEqual(complexParams);
|
|
184
|
-
expect(error.sql).toBe(
|
|
185
|
+
expect(error.sql).toBe("SELECT ?");
|
|
185
186
|
}
|
|
186
187
|
});
|