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.
- package/.gitignore +0 -0
- package/configs/presetFields.ts +10 -0
- package/configs/presetRegexp.ts +225 -0
- package/package.json +15 -16
- package/tests/_mocks/mockSqliteDb.ts +0 -204
- package/tests/addonHelper-cache.test.ts +0 -32
- package/tests/api-integration-array-number.test.ts +0 -282
- package/tests/apiHandler-routePath-only.test.ts +0 -32
- package/tests/befly-config-env.test.ts +0 -78
- package/tests/cacheHelper.test.ts +0 -323
- package/tests/cacheKeys.test.ts +0 -41
- package/tests/checkApi-routePath-strict.test.ts +0 -166
- package/tests/checkMenu.test.ts +0 -346
- package/tests/checkTable-smoke.test.ts +0 -157
- package/tests/cipher.test.ts +0 -249
- package/tests/dbDialect-cache.test.ts +0 -23
- package/tests/dbDialect.test.ts +0 -46
- package/tests/dbHelper-advanced.test.ts +0 -723
- package/tests/dbHelper-all-array-types.test.ts +0 -316
- package/tests/dbHelper-array-serialization.test.ts +0 -258
- package/tests/dbHelper-batch-write.test.ts +0 -90
- package/tests/dbHelper-columns.test.ts +0 -234
- package/tests/dbHelper-execute.test.ts +0 -187
- package/tests/dbHelper-joins.test.ts +0 -221
- package/tests/fields-redis-cache.test.ts +0 -127
- package/tests/fields-validate.test.ts +0 -99
- package/tests/fixtures/scanFilesAddon/node_modules/@befly-addon/demo/apis/sub/b.ts +0 -3
- package/tests/fixtures/scanFilesApis/a.ts +0 -3
- package/tests/fixtures/scanFilesApis/sub/b.ts +0 -3
- package/tests/getClientIp.test.ts +0 -54
- package/tests/integration.test.ts +0 -189
- package/tests/jwt.test.ts +0 -65
- package/tests/loadPlugins-order-smoke.test.ts +0 -75
- package/tests/logger.test.ts +0 -325
- package/tests/redisHelper.test.ts +0 -495
- package/tests/redisKeys.test.ts +0 -9
- package/tests/scanConfig.test.ts +0 -144
- package/tests/scanFiles-routePath.test.ts +0 -46
- package/tests/smoke-sql.test.ts +0 -24
- package/tests/sqlBuilder-advanced.test.ts +0 -608
- package/tests/sqlBuilder.test.ts +0 -209
- package/tests/sync-connection.test.ts +0 -183
- package/tests/sync-init-guard.test.ts +0 -105
- package/tests/syncApi-insBatch-fields-consistent.test.ts +0 -61
- package/tests/syncApi-obsolete-records.test.ts +0 -69
- package/tests/syncApi-type-compat.test.ts +0 -72
- package/tests/syncDev-permissions.test.ts +0 -81
- package/tests/syncMenu-disableMenus-hard-delete.test.ts +0 -88
- package/tests/syncMenu-duplicate-path.test.ts +0 -122
- package/tests/syncMenu-obsolete-records.test.ts +0 -161
- package/tests/syncMenu-parentPath-from-tree.test.ts +0 -75
- package/tests/syncMenu-paths.test.ts +0 -59
- package/tests/syncTable-apply.test.ts +0 -279
- package/tests/syncTable-array-number.test.ts +0 -160
- package/tests/syncTable-constants.test.ts +0 -101
- package/tests/syncTable-db-integration.test.ts +0 -237
- package/tests/syncTable-ddl.test.ts +0 -245
- package/tests/syncTable-helpers.test.ts +0 -99
- package/tests/syncTable-schema.test.ts +0 -99
- package/tests/syncTable-testkit.test.ts +0 -25
- package/tests/syncTable-types.test.ts +0 -122
- package/tests/tableRef-and-deserialize.test.ts +0 -67
- package/tests/util.test.ts +0 -100
- package/tests/validator-array-number.test.ts +0 -310
- package/tests/validator-default.test.ts +0 -373
- package/tests/validator.test.ts +0 -679
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* API 集成测试 - 验证 array_number_string 类型在完整数据流中的表现
|
|
3
|
-
*
|
|
4
|
-
* 测试流程:请求验证 → 数据库存储 → 查询返回
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { TableDefinition } from "befly/types/validate";
|
|
8
|
-
|
|
9
|
-
import { describe, expect, test } from "bun:test";
|
|
10
|
-
|
|
11
|
-
import { Validator } from "../lib/validator.js";
|
|
12
|
-
|
|
13
|
-
describe("API 集成测试 - array_number_string 数据流", () => {
|
|
14
|
-
// 模拟角色表定义
|
|
15
|
-
const roleTable: TableDefinition = {
|
|
16
|
-
name: {
|
|
17
|
-
name: "角色名称",
|
|
18
|
-
type: "string",
|
|
19
|
-
min: 2,
|
|
20
|
-
max: 50,
|
|
21
|
-
default: null,
|
|
22
|
-
regexp: null
|
|
23
|
-
},
|
|
24
|
-
code: {
|
|
25
|
-
name: "角色编码",
|
|
26
|
-
type: "string",
|
|
27
|
-
min: 2,
|
|
28
|
-
max: 50,
|
|
29
|
-
default: null,
|
|
30
|
-
regexp: null
|
|
31
|
-
},
|
|
32
|
-
menus: {
|
|
33
|
-
name: "菜单权限",
|
|
34
|
-
type: "array_number_string",
|
|
35
|
-
min: null,
|
|
36
|
-
max: 2000,
|
|
37
|
-
default: null,
|
|
38
|
-
regexp: null
|
|
39
|
-
},
|
|
40
|
-
apis: {
|
|
41
|
-
name: "接口权限",
|
|
42
|
-
type: "array_number_string",
|
|
43
|
-
min: null,
|
|
44
|
-
max: 3000,
|
|
45
|
-
default: null,
|
|
46
|
-
regexp: null
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// ==================== 请求验证阶段 ====================
|
|
51
|
-
|
|
52
|
-
test("请求验证: 接受合法的数字数组", () => {
|
|
53
|
-
const requestData = {
|
|
54
|
-
name: "测试角色",
|
|
55
|
-
code: "test_role",
|
|
56
|
-
menus: [1, 2, 3, 4, 5],
|
|
57
|
-
apis: [10, 20, 30]
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const result = Validator.validate(requestData, roleTable, ["name", "code"]);
|
|
61
|
-
expect(result.failed).toBe(false);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("请求验证: 拒绝字符串数组", () => {
|
|
65
|
-
const requestData = {
|
|
66
|
-
name: "测试角色",
|
|
67
|
-
code: "test_role",
|
|
68
|
-
menus: ["1", "2", "3"], // 错误:字符串数组
|
|
69
|
-
apis: [10, 20, 30]
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const result = Validator.validate(requestData, roleTable, ["name", "code"]);
|
|
73
|
-
expect(result.failed).toBe(true);
|
|
74
|
-
expect(result.firstError).toContain("数组元素必须是数字");
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test("请求验证: 拒绝混合类型数组", () => {
|
|
78
|
-
const requestData = {
|
|
79
|
-
name: "测试角色",
|
|
80
|
-
code: "test_role",
|
|
81
|
-
menus: [1, 2, 3],
|
|
82
|
-
apis: [10, "20", 30] // 错误:混合类型
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const result = Validator.validate(requestData, roleTable, ["name", "code"]);
|
|
86
|
-
expect(result.failed).toBe(true);
|
|
87
|
-
expect(result.firstError).toContain("数组元素必须是数字");
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("请求验证: 空数组应通过验证", () => {
|
|
91
|
-
const requestData = {
|
|
92
|
-
name: "测试角色",
|
|
93
|
-
code: "test_role",
|
|
94
|
-
menus: [],
|
|
95
|
-
apis: []
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const result = Validator.validate(requestData, roleTable, ["name", "code"]);
|
|
99
|
-
expect(result.failed).toBe(false);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("请求验证: 未传递数组字段时使用默认值", () => {
|
|
103
|
-
const requestData = {
|
|
104
|
-
name: "测试角色",
|
|
105
|
-
code: "test_role"
|
|
106
|
-
// menus 和 apis 未传递
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const result = Validator.validate(requestData, roleTable, ["name", "code"]);
|
|
110
|
-
expect(result.failed).toBe(false);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test("请求验证: max 元素数量限制生效", () => {
|
|
114
|
-
// 注意:max 参数对 array_number_string 是限制数组长度,不是字符串长度
|
|
115
|
-
const roleTableWithMax: TableDefinition = {
|
|
116
|
-
...roleTable,
|
|
117
|
-
menus: {
|
|
118
|
-
name: "菜单权限",
|
|
119
|
-
type: "array_number_string",
|
|
120
|
-
min: null,
|
|
121
|
-
max: 10, // 最多 10 个元素
|
|
122
|
-
default: null,
|
|
123
|
-
regexp: null
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const requestData = {
|
|
128
|
-
name: "测试角色",
|
|
129
|
-
code: "test_role",
|
|
130
|
-
menus: Array.from({ length: 15 }, (_, i) => i), // 15 个元素,超过限制
|
|
131
|
-
apis: [10, 20, 30]
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const result = Validator.validate(requestData, roleTableWithMax, ["name", "code"]);
|
|
135
|
-
expect(result.failed).toBe(true);
|
|
136
|
-
expect(result.firstError).toContain("最多只能有10个元素");
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// ==================== 数据库存储模拟 ====================
|
|
140
|
-
|
|
141
|
-
test("数据库存储: 数字数组应被正确序列化", () => {
|
|
142
|
-
const menuIds = [1, 2, 3, 4, 5];
|
|
143
|
-
const apiIds = [10, 20, 30, 40];
|
|
144
|
-
|
|
145
|
-
// 模拟数据库存储时的序列化(实际由 dbHelper 处理)
|
|
146
|
-
const serializedMenus = JSON.stringify(menuIds);
|
|
147
|
-
const serializedApis = JSON.stringify(apiIds);
|
|
148
|
-
|
|
149
|
-
expect(serializedMenus).toBe("[1,2,3,4,5]");
|
|
150
|
-
expect(serializedApis).toBe("[10,20,30,40]");
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test('数据库存储: 空数组应被序列化为 "[]"', () => {
|
|
154
|
-
const menuIds: number[] = [];
|
|
155
|
-
|
|
156
|
-
const serialized = JSON.stringify(menuIds);
|
|
157
|
-
expect(serialized).toBe("[]");
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// ==================== 查询返回模拟 ====================
|
|
161
|
-
|
|
162
|
-
test("查询返回: 数据库字符串应被正确解析为数组", () => {
|
|
163
|
-
// 模拟从数据库查询返回的数据(实际由 dbHelper 处理)
|
|
164
|
-
const dbRow = {
|
|
165
|
-
id: 1,
|
|
166
|
-
name: "测试角色",
|
|
167
|
-
code: "test_role",
|
|
168
|
-
menus: "[1,2,3,4,5]",
|
|
169
|
-
apis: "[10,20,30,40]"
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
// 解析
|
|
173
|
-
const parsedMenus = JSON.parse(dbRow.menus);
|
|
174
|
-
const parsedApis = JSON.parse(dbRow.apis);
|
|
175
|
-
|
|
176
|
-
expect(Array.isArray(parsedMenus)).toBe(true);
|
|
177
|
-
expect(Array.isArray(parsedApis)).toBe(true);
|
|
178
|
-
expect(parsedMenus).toEqual([1, 2, 3, 4, 5]);
|
|
179
|
-
expect(parsedApis).toEqual([10, 20, 30, 40]);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test('查询返回: 空数组字符串 "[]" 应解析为空数组', () => {
|
|
183
|
-
const dbRow = {
|
|
184
|
-
id: 1,
|
|
185
|
-
name: "测试角色",
|
|
186
|
-
code: "test_role",
|
|
187
|
-
menus: "[]",
|
|
188
|
-
apis: "[]"
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const parsedMenus = JSON.parse(dbRow.menus);
|
|
192
|
-
const parsedApis = JSON.parse(dbRow.apis);
|
|
193
|
-
|
|
194
|
-
expect(parsedMenus).toEqual([]);
|
|
195
|
-
expect(parsedApis).toEqual([]);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// ==================== 完整数据流测试 ====================
|
|
199
|
-
|
|
200
|
-
test("完整流程: 请求 → 验证 → 存储 → 查询 → 返回", () => {
|
|
201
|
-
// 1. 模拟 API 请求
|
|
202
|
-
const requestData = {
|
|
203
|
-
name: "管理员角色",
|
|
204
|
-
code: "admin",
|
|
205
|
-
menus: [1, 2, 3, 10, 11, 12],
|
|
206
|
-
apis: [100, 101, 102, 200, 201]
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
// 2. 验证请求数据
|
|
210
|
-
const validationResult = Validator.validate(requestData, roleTable, ["name", "code"]);
|
|
211
|
-
expect(validationResult.failed).toBe(false);
|
|
212
|
-
|
|
213
|
-
// 3. 模拟数据库存储(序列化)
|
|
214
|
-
const dbData = {
|
|
215
|
-
name: requestData.name,
|
|
216
|
-
code: requestData.code,
|
|
217
|
-
menus: JSON.stringify(requestData.menus),
|
|
218
|
-
apis: JSON.stringify(requestData.apis)
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
expect(dbData.menus).toBe("[1,2,3,10,11,12]");
|
|
222
|
-
expect(dbData.apis).toBe("[100,101,102,200,201]");
|
|
223
|
-
|
|
224
|
-
// 4. 模拟查询返回(反序列化)
|
|
225
|
-
const responseData = {
|
|
226
|
-
id: 1,
|
|
227
|
-
name: dbData.name,
|
|
228
|
-
code: dbData.code,
|
|
229
|
-
menus: JSON.parse(dbData.menus),
|
|
230
|
-
apis: JSON.parse(dbData.apis)
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
// 5. 验证返回数据
|
|
234
|
-
expect(responseData.menus).toEqual(requestData.menus);
|
|
235
|
-
expect(responseData.apis).toEqual(requestData.apis);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
test("完整流程: 更新权限(部分字段)", () => {
|
|
239
|
-
// 模拟更新操作,只更新 menus
|
|
240
|
-
const existingData = {
|
|
241
|
-
id: 1,
|
|
242
|
-
name: "管理员角色",
|
|
243
|
-
code: "admin",
|
|
244
|
-
menus: "[1,2,3]",
|
|
245
|
-
apis: "[100,101,102]"
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
const updateRequest = {
|
|
249
|
-
id: 1,
|
|
250
|
-
menus: [1, 2, 3, 4, 5, 6] // 新增菜单
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
// 验证更新数据
|
|
254
|
-
const fields: TableDefinition = {
|
|
255
|
-
menus: roleTable.menus
|
|
256
|
-
};
|
|
257
|
-
const validationResult = Validator.validate(updateRequest, fields, []);
|
|
258
|
-
expect(validationResult.failed).toBe(false);
|
|
259
|
-
|
|
260
|
-
// 更新后的数据
|
|
261
|
-
const updatedData = {
|
|
262
|
-
...existingData,
|
|
263
|
-
menus: JSON.stringify(updateRequest.menus)
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
expect(updatedData.menus).toBe("[1,2,3,4,5,6]");
|
|
267
|
-
expect(updatedData.apis).toBe("[100,101,102]"); // 未变
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
test("错误场景: 尝试存储非法数据", () => {
|
|
271
|
-
const invalidRequest = {
|
|
272
|
-
name: "测试角色",
|
|
273
|
-
code: "test",
|
|
274
|
-
menus: "invalid", // 错误:不是数组
|
|
275
|
-
apis: [10, 20]
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
const result = Validator.validate(invalidRequest, roleTable, ["name", "code"]);
|
|
279
|
-
expect(result.failed).toBe(true);
|
|
280
|
-
expect(result.errorFields).toContain("menus");
|
|
281
|
-
});
|
|
282
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { apiHandler } from "../router/api.js";
|
|
4
|
-
|
|
5
|
-
describe("apiHandler - route matching", () => {
|
|
6
|
-
test("接口存在性判断应仅使用 url.pathname(不附加 method)", async () => {
|
|
7
|
-
const apis = new Map<string, any>();
|
|
8
|
-
|
|
9
|
-
apis.set("/api/hello", {
|
|
10
|
-
name: "hello",
|
|
11
|
-
method: "POST",
|
|
12
|
-
handler: async () => {
|
|
13
|
-
return {
|
|
14
|
-
msg: "ok"
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const hooks: any[] = [];
|
|
20
|
-
const context: any = {};
|
|
21
|
-
|
|
22
|
-
const handler = apiHandler(apis as any, hooks as any, context as any);
|
|
23
|
-
|
|
24
|
-
// 如果 key 里拼了 method,这里会变成 "POST /api/hello" 之类,从而导致接口不存在
|
|
25
|
-
const res = await handler(new Request("http://localhost/api/hello", { method: "POST" }));
|
|
26
|
-
expect(res.status).toBe(200);
|
|
27
|
-
|
|
28
|
-
const body = await res.json();
|
|
29
|
-
expect(body.code).toBe(0);
|
|
30
|
-
expect(body.msg).toBe("ok");
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from "bun:test";
|
|
2
|
-
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
|
-
import { loadBeflyConfig } from "../befly.config.js";
|
|
6
|
-
|
|
7
|
-
describe("Config - NODE_ENV 选择 development/production", () => {
|
|
8
|
-
test("production -> befly.production.json;其他 -> befly.development.json(common 总是生效)", async () => {
|
|
9
|
-
const tempRoot = join(process.cwd(), "temp", `befly-config-env-test-${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
|
10
|
-
const tempConfigsDir = join(tempRoot, "configs");
|
|
11
|
-
|
|
12
|
-
mkdirSync(tempConfigsDir, { recursive: true });
|
|
13
|
-
|
|
14
|
-
writeFileSync(
|
|
15
|
-
join(tempConfigsDir, "befly.common.json"),
|
|
16
|
-
JSON.stringify(
|
|
17
|
-
{
|
|
18
|
-
appPort: 31111,
|
|
19
|
-
logger: {
|
|
20
|
-
debug: 2
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
null,
|
|
24
|
-
4
|
|
25
|
-
),
|
|
26
|
-
{ encoding: "utf8" }
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
writeFileSync(
|
|
30
|
-
join(tempConfigsDir, "befly.development.json"),
|
|
31
|
-
JSON.stringify(
|
|
32
|
-
{
|
|
33
|
-
appName: "development",
|
|
34
|
-
logger: {
|
|
35
|
-
debug: 3
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
null,
|
|
39
|
-
4
|
|
40
|
-
),
|
|
41
|
-
{ encoding: "utf8" }
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
writeFileSync(
|
|
45
|
-
join(tempConfigsDir, "befly.production.json"),
|
|
46
|
-
JSON.stringify(
|
|
47
|
-
{
|
|
48
|
-
appName: "production",
|
|
49
|
-
logger: {
|
|
50
|
-
debug: 9
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
null,
|
|
54
|
-
4
|
|
55
|
-
),
|
|
56
|
-
{ encoding: "utf8" }
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const productionConfig = await loadBeflyConfig({ cwd: tempRoot, nodeEnv: "production" });
|
|
61
|
-
expect(productionConfig.appName).toBe("production");
|
|
62
|
-
expect(productionConfig.logger.debug).toBe(9);
|
|
63
|
-
expect(productionConfig.appPort).toBe(31111);
|
|
64
|
-
|
|
65
|
-
const developmentConfig = await loadBeflyConfig({ cwd: tempRoot, nodeEnv: "development" });
|
|
66
|
-
expect(developmentConfig.appName).toBe("development");
|
|
67
|
-
expect(developmentConfig.logger.debug).toBe(3);
|
|
68
|
-
expect(developmentConfig.appPort).toBe(31111);
|
|
69
|
-
|
|
70
|
-
const fallbackConfig = await loadBeflyConfig({ cwd: tempRoot, nodeEnv: "test" });
|
|
71
|
-
expect(fallbackConfig.appName).toBe("development");
|
|
72
|
-
expect(fallbackConfig.logger.debug).toBe(3);
|
|
73
|
-
expect(fallbackConfig.appPort).toBe(31111);
|
|
74
|
-
} finally {
|
|
75
|
-
rmSync(tempRoot, { recursive: true, force: true });
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
});
|