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
package/tests/sqlBuilder.test.ts
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { SqlBuilder } from "../lib/sqlBuilder";
|
|
4
|
-
|
|
5
|
-
describe("SqlBuilder - SELECT 查询", () => {
|
|
6
|
-
test("简单查询", () => {
|
|
7
|
-
const builder = new SqlBuilder();
|
|
8
|
-
const result = builder.select(["id", "name"]).from("users").toSelectSql();
|
|
9
|
-
expect(result.sql).toContain("SELECT `id`, `name` FROM `users`");
|
|
10
|
-
expect(result.params).toEqual([]);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("查询所有字段", () => {
|
|
14
|
-
const builder = new SqlBuilder();
|
|
15
|
-
const result = builder.select(["*"]).from("users").toSelectSql();
|
|
16
|
-
expect(result.sql).toContain("SELECT * FROM `users`");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("带 WHERE 条件", () => {
|
|
20
|
-
const builder = new SqlBuilder();
|
|
21
|
-
const result = builder.select(["*"]).from("users").where({ id: 1 }).toSelectSql();
|
|
22
|
-
expect(result.sql).toContain("WHERE `id` = ?");
|
|
23
|
-
expect(result.params).toEqual([1]);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("多个 WHERE 条件", () => {
|
|
27
|
-
const builder = new SqlBuilder();
|
|
28
|
-
const result = builder.select(["*"]).from("users").where({ id: 1, status: "active" }).toSelectSql();
|
|
29
|
-
expect(result.sql).toContain("WHERE");
|
|
30
|
-
expect(result.sql).toContain("AND");
|
|
31
|
-
expect(result.params).toEqual([1, "active"]);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("ORDER BY", () => {
|
|
35
|
-
const builder = new SqlBuilder();
|
|
36
|
-
const result = builder.select(["*"]).from("users").orderBy(["created_at#DESC"]).toSelectSql();
|
|
37
|
-
expect(result.sql).toContain("ORDER BY `created_at` DESC");
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("LIMIT 和 OFFSET", () => {
|
|
41
|
-
const builder = new SqlBuilder();
|
|
42
|
-
const result = builder.select(["*"]).from("users").limit(10).offset(20).toSelectSql();
|
|
43
|
-
expect(result.sql).toContain("LIMIT 10 OFFSET 20");
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe("SqlBuilder - WHERE 操作符", () => {
|
|
48
|
-
test("$ne 不等于", () => {
|
|
49
|
-
const builder = new SqlBuilder();
|
|
50
|
-
const result = builder
|
|
51
|
-
.select(["*"])
|
|
52
|
-
.from("users")
|
|
53
|
-
.where({ status: { $ne: "deleted" } })
|
|
54
|
-
.toSelectSql();
|
|
55
|
-
expect(result.sql).toContain("`status` != ?");
|
|
56
|
-
expect(result.params).toEqual(["deleted"]);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("$in 包含", () => {
|
|
60
|
-
const builder = new SqlBuilder();
|
|
61
|
-
const result = builder
|
|
62
|
-
.select(["*"])
|
|
63
|
-
.from("users")
|
|
64
|
-
.where({ id: { $in: [1, 2, 3] } })
|
|
65
|
-
.toSelectSql();
|
|
66
|
-
expect(result.sql).toContain("`id` IN (?,?,?)");
|
|
67
|
-
expect(result.params).toEqual([1, 2, 3]);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("$gt 大于", () => {
|
|
71
|
-
const builder = new SqlBuilder();
|
|
72
|
-
const result = builder
|
|
73
|
-
.select(["*"])
|
|
74
|
-
.from("users")
|
|
75
|
-
.where({ age: { $gt: 18 } })
|
|
76
|
-
.toSelectSql();
|
|
77
|
-
expect(result.sql).toContain("`age` > ?");
|
|
78
|
-
expect(result.params).toEqual([18]);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("$gte 大于等于", () => {
|
|
82
|
-
const builder = new SqlBuilder();
|
|
83
|
-
const result = builder
|
|
84
|
-
.select(["*"])
|
|
85
|
-
.from("users")
|
|
86
|
-
.where({ age: { $gte: 18 } })
|
|
87
|
-
.toSelectSql();
|
|
88
|
-
expect(result.sql).toContain("`age` >= ?");
|
|
89
|
-
expect(result.params).toEqual([18]);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("$lt 小于", () => {
|
|
93
|
-
const builder = new SqlBuilder();
|
|
94
|
-
const result = builder
|
|
95
|
-
.select(["*"])
|
|
96
|
-
.from("users")
|
|
97
|
-
.where({ age: { $lt: 60 } })
|
|
98
|
-
.toSelectSql();
|
|
99
|
-
expect(result.sql).toContain("`age` < ?");
|
|
100
|
-
expect(result.params).toEqual([60]);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test("$lte 小于等于", () => {
|
|
104
|
-
const builder = new SqlBuilder();
|
|
105
|
-
const result = builder
|
|
106
|
-
.select(["*"])
|
|
107
|
-
.from("users")
|
|
108
|
-
.where({ age: { $lte: 60 } })
|
|
109
|
-
.toSelectSql();
|
|
110
|
-
expect(result.sql).toContain("`age` <= ?");
|
|
111
|
-
expect(result.params).toEqual([60]);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("$like 模糊匹配", () => {
|
|
115
|
-
const builder = new SqlBuilder();
|
|
116
|
-
const result = builder
|
|
117
|
-
.select(["*"])
|
|
118
|
-
.from("users")
|
|
119
|
-
.where({ name: { $like: "%john%" } })
|
|
120
|
-
.toSelectSql();
|
|
121
|
-
expect(result.sql).toContain("`name` LIKE ?");
|
|
122
|
-
expect(result.params).toEqual(["%john%"]);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
describe("SqlBuilder - INSERT", () => {
|
|
127
|
-
test("插入单条数据", () => {
|
|
128
|
-
const builder = new SqlBuilder();
|
|
129
|
-
const result = builder.toInsertSql("users", { name: "John", age: 25 });
|
|
130
|
-
expect(result.sql).toContain("INSERT INTO `users`");
|
|
131
|
-
expect(result.sql).toContain("(`name`, `age`)");
|
|
132
|
-
expect(result.sql).toContain("VALUES (?, ?)");
|
|
133
|
-
expect(result.params).toEqual(["John", 25]);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test("插入单条数据 - 参数为 undefined 应抛错", () => {
|
|
137
|
-
const builder = new SqlBuilder();
|
|
138
|
-
expect(() => {
|
|
139
|
-
builder.toInsertSql("users", { name: undefined as any, age: 25 });
|
|
140
|
-
}).toThrow("undefined");
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test("批量插入 - 行字段不一致应抛错", () => {
|
|
144
|
-
const builder = new SqlBuilder();
|
|
145
|
-
expect(() => {
|
|
146
|
-
builder.toInsertSql("users", [{ name: "John", age: 25 }, { name: "Jane" }] as any);
|
|
147
|
-
}).toThrow("字段必须一致");
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test("批量插入 - 缺字段或 undefined 应抛错", () => {
|
|
151
|
-
const builder = new SqlBuilder();
|
|
152
|
-
expect(() => {
|
|
153
|
-
builder.toInsertSql("users", [
|
|
154
|
-
{ name: "John", age: 25 },
|
|
155
|
-
{ name: "Jane", age: undefined as any }
|
|
156
|
-
] as any);
|
|
157
|
-
}).toThrow("undefined");
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe("SqlBuilder - UPDATE", () => {
|
|
162
|
-
test("更新数据", () => {
|
|
163
|
-
const builder = new SqlBuilder();
|
|
164
|
-
const result = builder.where({ id: 1 }).toUpdateSql("users", { name: "Jane" });
|
|
165
|
-
expect(result.sql).toContain("UPDATE `users`");
|
|
166
|
-
expect(result.sql).toContain("SET `name` = ?");
|
|
167
|
-
expect(result.sql).toContain("WHERE `id` = ?");
|
|
168
|
-
expect(result.params).toEqual(["Jane", 1]);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe("SqlBuilder - DELETE", () => {
|
|
173
|
-
test("删除数据", () => {
|
|
174
|
-
const builder = new SqlBuilder();
|
|
175
|
-
const result = builder.where({ id: 1 }).toDeleteSql("users");
|
|
176
|
-
expect(result.sql).toContain("DELETE FROM `users`");
|
|
177
|
-
expect(result.sql).toContain("WHERE `id` = ?");
|
|
178
|
-
expect(result.params).toEqual([1]);
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
describe("SqlBuilder - 链式调用", () => {
|
|
183
|
-
test("复杂查询", () => {
|
|
184
|
-
const builder = new SqlBuilder();
|
|
185
|
-
const result = builder
|
|
186
|
-
.select(["id", "name", "email"])
|
|
187
|
-
.from("users")
|
|
188
|
-
.where({ status: "active", age: { $gte: 18 } })
|
|
189
|
-
.orderBy(["created_at#DESC"])
|
|
190
|
-
.limit(10)
|
|
191
|
-
.toSelectSql();
|
|
192
|
-
|
|
193
|
-
expect(result.sql).toContain("SELECT");
|
|
194
|
-
expect(result.sql).toContain("FROM `users`");
|
|
195
|
-
expect(result.sql).toContain("WHERE");
|
|
196
|
-
expect(result.sql).toContain("ORDER BY");
|
|
197
|
-
expect(result.sql).toContain("LIMIT 10");
|
|
198
|
-
expect(result.params.length).toBeGreaterThan(0);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
test("reset 重置", () => {
|
|
202
|
-
const builder = new SqlBuilder();
|
|
203
|
-
builder.select(["*"]).from("users").where({ id: 1 });
|
|
204
|
-
builder.reset();
|
|
205
|
-
const result = builder.select(["*"]).from("posts").toSelectSql();
|
|
206
|
-
expect(result.sql).toContain("FROM `posts`");
|
|
207
|
-
expect(result.sql).not.toContain("users");
|
|
208
|
-
});
|
|
209
|
-
});
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* sync 模块连接管理集成测试
|
|
3
|
-
* 验证数据库连接的正确关闭
|
|
4
|
-
*/
|
|
5
|
-
import { describe, test, expect, afterEach } from "bun:test";
|
|
6
|
-
|
|
7
|
-
import { Connect } from "../lib/connect.js";
|
|
8
|
-
|
|
9
|
-
describe("sync 模块连接管理", () => {
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
// 每个测试后重置连接状态
|
|
12
|
-
Connect.__reset();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe("Connect.isConnected", () => {
|
|
16
|
-
test("初始状态应该都是未连接", () => {
|
|
17
|
-
const status = Connect.isConnected();
|
|
18
|
-
expect(status.sql).toBe(false);
|
|
19
|
-
expect(status.redis).toBe(false);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe("Connect.disconnect", () => {
|
|
24
|
-
test("disconnect 应该能安全地关闭未连接的状态", async () => {
|
|
25
|
-
// 即使没有连接,disconnect 也不应该抛出错误
|
|
26
|
-
await expect(Connect.disconnect()).resolves.toBeUndefined();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("disconnectSql 应该能安全地关闭未连接的状态", async () => {
|
|
30
|
-
await expect(Connect.disconnectSql()).resolves.toBeUndefined();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test("disconnectRedis 应该能安全地关闭未连接的状态", async () => {
|
|
34
|
-
await expect(Connect.disconnectRedis()).resolves.toBeUndefined();
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe("Connect.getSql", () => {
|
|
39
|
-
test("未连接时应该抛出错误", () => {
|
|
40
|
-
expect(() => Connect.getSql()).toThrow("SQL 客户端未连接");
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe("Connect.getRedis", () => {
|
|
45
|
-
test("未连接时应该抛出错误", () => {
|
|
46
|
-
expect(() => Connect.getRedis()).toThrow("Redis 客户端未连接");
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe("Mock 连接测试", () => {
|
|
51
|
-
test("__setMockSql 应该设置 mock SQL 客户端", () => {
|
|
52
|
-
const mockSql = { close: async () => {} } as any;
|
|
53
|
-
Connect.__setMockSql(mockSql);
|
|
54
|
-
|
|
55
|
-
const status = Connect.isConnected();
|
|
56
|
-
expect(status.sql).toBe(true);
|
|
57
|
-
expect(status.redis).toBe(false);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("__setMockRedis 应该设置 mock Redis 客户端", () => {
|
|
61
|
-
const mockRedis = { close: () => {} } as any;
|
|
62
|
-
Connect.__setMockRedis(mockRedis);
|
|
63
|
-
|
|
64
|
-
const status = Connect.isConnected();
|
|
65
|
-
expect(status.sql).toBe(false);
|
|
66
|
-
expect(status.redis).toBe(true);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("__reset 应该重置所有连接", () => {
|
|
70
|
-
const mockSql = { close: async () => {} } as any;
|
|
71
|
-
const mockRedis = { close: () => {} } as any;
|
|
72
|
-
Connect.__setMockSql(mockSql);
|
|
73
|
-
Connect.__setMockRedis(mockRedis);
|
|
74
|
-
|
|
75
|
-
expect(Connect.isConnected().sql).toBe(true);
|
|
76
|
-
expect(Connect.isConnected().redis).toBe(true);
|
|
77
|
-
|
|
78
|
-
Connect.__reset();
|
|
79
|
-
|
|
80
|
-
expect(Connect.isConnected().sql).toBe(false);
|
|
81
|
-
expect(Connect.isConnected().redis).toBe(false);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("disconnect 应该正确关闭 mock 连接", async () => {
|
|
85
|
-
let sqlClosed = false;
|
|
86
|
-
let redisClosed = false;
|
|
87
|
-
|
|
88
|
-
const mockSql = {
|
|
89
|
-
close: async () => {
|
|
90
|
-
sqlClosed = true;
|
|
91
|
-
}
|
|
92
|
-
} as any;
|
|
93
|
-
const mockRedis = {
|
|
94
|
-
close: () => {
|
|
95
|
-
redisClosed = true;
|
|
96
|
-
}
|
|
97
|
-
} as any;
|
|
98
|
-
|
|
99
|
-
Connect.__setMockSql(mockSql);
|
|
100
|
-
Connect.__setMockRedis(mockRedis);
|
|
101
|
-
|
|
102
|
-
await Connect.disconnect();
|
|
103
|
-
|
|
104
|
-
expect(sqlClosed).toBe(true);
|
|
105
|
-
expect(redisClosed).toBe(true);
|
|
106
|
-
expect(Connect.isConnected().sql).toBe(false);
|
|
107
|
-
expect(Connect.isConnected().redis).toBe(false);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe("连接异常处理", () => {
|
|
112
|
-
test("disconnectSql 应该处理关闭时的错误", async () => {
|
|
113
|
-
const mockSql = {
|
|
114
|
-
close: async () => {
|
|
115
|
-
throw new Error("Close error");
|
|
116
|
-
}
|
|
117
|
-
} as any;
|
|
118
|
-
|
|
119
|
-
Connect.__setMockSql(mockSql);
|
|
120
|
-
|
|
121
|
-
// 不应该抛出错误,只是记录日志
|
|
122
|
-
await expect(Connect.disconnectSql()).resolves.toBeUndefined();
|
|
123
|
-
expect(Connect.isConnected().sql).toBe(false);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("disconnectRedis 应该处理关闭时的错误", async () => {
|
|
127
|
-
const mockRedis = {
|
|
128
|
-
close: () => {
|
|
129
|
-
throw new Error("Close error");
|
|
130
|
-
}
|
|
131
|
-
} as any;
|
|
132
|
-
|
|
133
|
-
Connect.__setMockRedis(mockRedis);
|
|
134
|
-
|
|
135
|
-
// 不应该抛出错误,只是记录日志
|
|
136
|
-
await expect(Connect.disconnectRedis()).resolves.toBeUndefined();
|
|
137
|
-
expect(Connect.isConnected().redis).toBe(false);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe("Connect.getStatus", () => {
|
|
142
|
-
test("未连接时返回正确的状态", () => {
|
|
143
|
-
const status = Connect.getStatus();
|
|
144
|
-
|
|
145
|
-
expect(status.sql.connected).toBe(false);
|
|
146
|
-
expect(status.sql.connectedAt).toBeNull();
|
|
147
|
-
expect(status.sql.uptime).toBeNull();
|
|
148
|
-
expect(status.sql.poolMax).toBe(1);
|
|
149
|
-
|
|
150
|
-
expect(status.redis.connected).toBe(false);
|
|
151
|
-
expect(status.redis.connectedAt).toBeNull();
|
|
152
|
-
expect(status.redis.uptime).toBeNull();
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("Mock 连接后返回正确的状态", () => {
|
|
156
|
-
const mockSql = { close: async () => {} } as any;
|
|
157
|
-
const mockRedis = { close: () => {} } as any;
|
|
158
|
-
|
|
159
|
-
Connect.__setMockSql(mockSql);
|
|
160
|
-
Connect.__setMockRedis(mockRedis);
|
|
161
|
-
|
|
162
|
-
const status = Connect.getStatus();
|
|
163
|
-
|
|
164
|
-
// Mock 不会设置连接时间,但连接状态应该为 true
|
|
165
|
-
expect(status.sql.connected).toBe(true);
|
|
166
|
-
expect(status.redis.connected).toBe(true);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test("__reset 应该重置所有状态包括连接时间", () => {
|
|
170
|
-
const mockSql = { close: async () => {} } as any;
|
|
171
|
-
Connect.__setMockSql(mockSql);
|
|
172
|
-
|
|
173
|
-
expect(Connect.getStatus().sql.connected).toBe(true);
|
|
174
|
-
|
|
175
|
-
Connect.__reset();
|
|
176
|
-
|
|
177
|
-
const status = Connect.getStatus();
|
|
178
|
-
expect(status.sql.connected).toBe(false);
|
|
179
|
-
expect(status.sql.connectedAt).toBeNull();
|
|
180
|
-
expect(status.sql.poolMax).toBe(1);
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
});
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { syncApi } from "../sync/syncApi.js";
|
|
4
|
-
import { syncDev } from "../sync/syncDev.js";
|
|
5
|
-
import { syncMenu } from "../sync/syncMenu.js";
|
|
6
|
-
|
|
7
|
-
describe("sync - init guard", () => {
|
|
8
|
-
test("syncApi: ctx.db 缺失时应给出明确错误", async () => {
|
|
9
|
-
const ctx = {} as any;
|
|
10
|
-
|
|
11
|
-
let error: any = null;
|
|
12
|
-
try {
|
|
13
|
-
await syncApi(ctx, [] as any);
|
|
14
|
-
} catch (err: any) {
|
|
15
|
-
error = err;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
expect(typeof error?.message).toBe("string");
|
|
19
|
-
expect(error.message).toBe("syncApi: ctx.db 未初始化(Db 插件未加载或注入失败)");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("syncApi: ctx.cache 缺失时应给出明确错误", async () => {
|
|
23
|
-
const ctx = { db: {} } as any;
|
|
24
|
-
|
|
25
|
-
let error: any = null;
|
|
26
|
-
try {
|
|
27
|
-
await syncApi(ctx, [] as any);
|
|
28
|
-
} catch (err: any) {
|
|
29
|
-
error = err;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
expect(typeof error?.message).toBe("string");
|
|
33
|
-
expect(error.message).toBe("syncApi: ctx.cache 未初始化(cache 插件未加载或注入失败)");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("syncDev: ctx.db 缺失时应给出明确错误", async () => {
|
|
37
|
-
const ctx = {} as any;
|
|
38
|
-
|
|
39
|
-
let error: any = null;
|
|
40
|
-
try {
|
|
41
|
-
await syncDev(ctx, { devEmail: "dev@qq.com", devPassword: "dev-password" });
|
|
42
|
-
} catch (err: any) {
|
|
43
|
-
error = err;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
expect(typeof error?.message).toBe("string");
|
|
47
|
-
expect(error.message).toBe("syncDev: ctx.db 未初始化(Db 插件未加载或注入失败)");
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("syncDev: ctx.cache 缺失时应给出明确错误", async () => {
|
|
51
|
-
const ctx = { db: {} } as any;
|
|
52
|
-
|
|
53
|
-
let error: any = null;
|
|
54
|
-
try {
|
|
55
|
-
await syncDev(ctx, { devEmail: "dev@qq.com", devPassword: "dev-password" });
|
|
56
|
-
} catch (err: any) {
|
|
57
|
-
error = err;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
expect(typeof error?.message).toBe("string");
|
|
61
|
-
expect(error.message).toBe("syncDev: ctx.cache 未初始化(cache 插件未加载或注入失败)");
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("syncMenu: ctx.db 缺失时应给出明确错误", async () => {
|
|
65
|
-
const ctx = {} as any;
|
|
66
|
-
|
|
67
|
-
let error: any = null;
|
|
68
|
-
try {
|
|
69
|
-
await syncMenu(ctx, [] as any);
|
|
70
|
-
} catch (err: any) {
|
|
71
|
-
error = err;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
expect(typeof error?.message).toBe("string");
|
|
75
|
-
expect(error.message).toBe("syncMenu: ctx.db 未初始化(Db 插件未加载或注入失败)");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("syncMenu: ctx.cache 缺失时应给出明确错误", async () => {
|
|
79
|
-
const ctx = { db: {} } as any;
|
|
80
|
-
|
|
81
|
-
let error: any = null;
|
|
82
|
-
try {
|
|
83
|
-
await syncMenu(ctx, [] as any);
|
|
84
|
-
} catch (err: any) {
|
|
85
|
-
error = err;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
expect(typeof error?.message).toBe("string");
|
|
89
|
-
expect(error.message).toBe("syncMenu: ctx.cache 未初始化(cache 插件未加载或注入失败)");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("syncMenu: ctx.config 缺失时应给出明确错误", async () => {
|
|
93
|
-
const ctx = { db: {}, cache: {} } as any;
|
|
94
|
-
|
|
95
|
-
let error: any = null;
|
|
96
|
-
try {
|
|
97
|
-
await syncMenu(ctx, [] as any);
|
|
98
|
-
} catch (err: any) {
|
|
99
|
-
error = err;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
expect(typeof error?.message).toBe("string");
|
|
103
|
-
expect(error.message).toBe("syncMenu: ctx.config 未初始化(config 插件未加载或注入失败)");
|
|
104
|
-
});
|
|
105
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { syncApi } from "../sync/syncApi.js";
|
|
4
|
-
|
|
5
|
-
describe("syncApi - insBatch rows consistency", () => {
|
|
6
|
-
test("当部分 api 缺少 addonName 时,insBatch 仍应传入稳定字段(addonName 为空字符串)", async () => {
|
|
7
|
-
const calls: any = {
|
|
8
|
-
insBatch: [] as any[],
|
|
9
|
-
cacheApis: 0,
|
|
10
|
-
rebuildRoleApiPermissions: 0
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const ctx: any = {
|
|
14
|
-
db: {
|
|
15
|
-
tableExists: async () => true,
|
|
16
|
-
getAll: async () => {
|
|
17
|
-
return { lists: [], total: 0 };
|
|
18
|
-
},
|
|
19
|
-
insBatch: async (_table: string, dataList: any[]) => {
|
|
20
|
-
calls.insBatch.push({ table: _table, dataList: dataList });
|
|
21
|
-
return [1, 2];
|
|
22
|
-
},
|
|
23
|
-
updBatch: async () => 0,
|
|
24
|
-
delForceBatch: async () => 0
|
|
25
|
-
},
|
|
26
|
-
cache: {
|
|
27
|
-
cacheApis: async () => {
|
|
28
|
-
calls.cacheApis += 1;
|
|
29
|
-
},
|
|
30
|
-
rebuildRoleApiPermissions: async () => {
|
|
31
|
-
calls.rebuildRoleApiPermissions += 1;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// 根因修复后:scanFiles 会确保 API 记录总是携带 addonName(app/core 为 "")。
|
|
37
|
-
// 因此这里模拟真实扫描结果:第二条的 addonName 应该是空字符串而非 undefined。
|
|
38
|
-
const apis: any[] = [
|
|
39
|
-
{ type: "api", name: "A", routePath: "/api/addon/admin/a", addonName: "admin" },
|
|
40
|
-
{ name: "B", routePath: "/api/app/b", addonName: "" }
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
await syncApi(ctx, apis as any);
|
|
44
|
-
|
|
45
|
-
expect(calls.insBatch.length).toBe(1);
|
|
46
|
-
expect(calls.insBatch[0].table).toBe("addon_admin_api");
|
|
47
|
-
|
|
48
|
-
const rows = calls.insBatch[0].dataList;
|
|
49
|
-
expect(rows.length).toBe(2);
|
|
50
|
-
|
|
51
|
-
expect(Object.keys(rows[0]).sort()).toEqual(Object.keys(rows[1]).sort());
|
|
52
|
-
|
|
53
|
-
expect(typeof rows[0].addonName).toBe("string");
|
|
54
|
-
expect(typeof rows[1].addonName).toBe("string");
|
|
55
|
-
expect(rows[1].addonName).toBe("");
|
|
56
|
-
|
|
57
|
-
// 缓存同步职责已收敛到 syncCache(启动流程单点调用)
|
|
58
|
-
expect(calls.cacheApis).toBe(0);
|
|
59
|
-
expect(calls.rebuildRoleApiPermissions).toBe(0);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { syncApi } from "../sync/syncApi.js";
|
|
4
|
-
|
|
5
|
-
describe("syncApi - delete obsolete records", () => {
|
|
6
|
-
test("应删除不在配置中的接口记录", async () => {
|
|
7
|
-
const existingRecords = [
|
|
8
|
-
{ id: 1, routePath: "/api/app/testSyncKeep", name: "Keep", addonName: "", state: 0 },
|
|
9
|
-
{ id: 2, routePath: "/api/app/testSyncRemove", name: "Remove", addonName: "", state: 0 }
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
const existingByPath = new Map<string, any>();
|
|
13
|
-
for (const record of existingRecords) {
|
|
14
|
-
existingByPath.set(record.routePath, record);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const calls = {
|
|
18
|
-
delForceBatch: [] as any[],
|
|
19
|
-
getAllArgs: null as any
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const dbHelper = {
|
|
23
|
-
tableExists: async () => true,
|
|
24
|
-
updBatch: async () => 0,
|
|
25
|
-
insBatch: async () => [],
|
|
26
|
-
getAll: async (options: any) => {
|
|
27
|
-
calls.getAllArgs = options;
|
|
28
|
-
return { lists: existingRecords };
|
|
29
|
-
},
|
|
30
|
-
delForceBatch: async (_table: any, ids: any[]) => {
|
|
31
|
-
calls.delForceBatch.push(ids);
|
|
32
|
-
return ids.length;
|
|
33
|
-
}
|
|
34
|
-
} as any;
|
|
35
|
-
|
|
36
|
-
const ctx = {
|
|
37
|
-
db: dbHelper,
|
|
38
|
-
addons: [],
|
|
39
|
-
cache: {
|
|
40
|
-
cacheApis: async () => {},
|
|
41
|
-
rebuildRoleApiPermissions: async () => {}
|
|
42
|
-
}
|
|
43
|
-
} as any;
|
|
44
|
-
|
|
45
|
-
const apiItems = [
|
|
46
|
-
{
|
|
47
|
-
source: "app",
|
|
48
|
-
sourceName: "项目",
|
|
49
|
-
filePath: "DUMMY",
|
|
50
|
-
relativePath: "testSyncKeep",
|
|
51
|
-
fileName: "testSyncKeep",
|
|
52
|
-
moduleName: "app_testSyncKeep",
|
|
53
|
-
name: "Keep",
|
|
54
|
-
routePath: "/api/app/testSyncKeep",
|
|
55
|
-
addonName: "",
|
|
56
|
-
fileBaseName: "testSyncKeep.ts",
|
|
57
|
-
fileDir: "DUMMY",
|
|
58
|
-
content: { name: "Keep", handler: async () => {} }
|
|
59
|
-
}
|
|
60
|
-
] as any;
|
|
61
|
-
|
|
62
|
-
await syncApi(ctx, apiItems);
|
|
63
|
-
|
|
64
|
-
expect(calls.getAllArgs?.fields).toEqual(["id", "routePath", "name", "addonName", "state"]);
|
|
65
|
-
|
|
66
|
-
expect(calls.delForceBatch).toHaveLength(1);
|
|
67
|
-
expect(calls.delForceBatch[0]).toEqual([2]);
|
|
68
|
-
});
|
|
69
|
-
});
|