befly 3.9.37 → 3.9.39
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 +38 -39
- 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} +225 -235
- package/docs/cipher.md +71 -69
- package/docs/database.md +155 -153
- 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} +7 -7
- 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 +15 -7
- package/lib/asyncContext.ts +43 -0
- package/lib/cacheHelper.ts +212 -81
- package/lib/cacheKeys.ts +38 -0
- package/lib/cipher.ts +30 -30
- package/lib/connect.ts +28 -28
- package/lib/dbHelper.ts +211 -109
- 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 +53 -47
- 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 -54
- 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 -66
- package/sync/syncMenu.ts +190 -57
- 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
|
@@ -5,183 +5,184 @@
|
|
|
5
5
|
* - compareFieldDefinition
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { describe, test, expect, beforeAll } from
|
|
9
|
-
|
|
8
|
+
import { describe, test, expect, beforeAll } from "bun:test";
|
|
9
|
+
|
|
10
|
+
import { setDbType } from "../sync/syncDb/constants.js";
|
|
10
11
|
|
|
11
12
|
// 设置数据库类型为 MySQL
|
|
12
|
-
setDbType(
|
|
13
|
+
setDbType("mysql");
|
|
13
14
|
|
|
14
15
|
let compareFieldDefinition: any;
|
|
15
16
|
|
|
16
17
|
beforeAll(async () => {
|
|
17
|
-
const apply = await import(
|
|
18
|
+
const apply = await import("../sync/syncDb/apply.js");
|
|
18
19
|
compareFieldDefinition = apply.compareFieldDefinition;
|
|
19
20
|
});
|
|
20
21
|
|
|
21
|
-
describe(
|
|
22
|
-
describe(
|
|
23
|
-
test(
|
|
22
|
+
describe("compareFieldDefinition", () => {
|
|
23
|
+
describe("长度变化检测", () => {
|
|
24
|
+
test("string 类型长度变化被检测到", () => {
|
|
24
25
|
const existingColumn = {
|
|
25
|
-
type:
|
|
26
|
+
type: "varchar",
|
|
26
27
|
max: 50,
|
|
27
28
|
nullable: false,
|
|
28
|
-
defaultValue:
|
|
29
|
-
comment:
|
|
29
|
+
defaultValue: "",
|
|
30
|
+
comment: "用户名"
|
|
30
31
|
};
|
|
31
32
|
const fieldDef = {
|
|
32
|
-
name:
|
|
33
|
-
type:
|
|
33
|
+
name: "用户名",
|
|
34
|
+
type: "string",
|
|
34
35
|
max: 100,
|
|
35
36
|
nullable: false,
|
|
36
37
|
default: null
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
40
|
-
const lengthChange = changes.find((c: any) => c.type ===
|
|
41
|
+
const lengthChange = changes.find((c: any) => c.type === "length");
|
|
41
42
|
|
|
42
43
|
expect(lengthChange).toBeDefined();
|
|
43
44
|
expect(lengthChange.current).toBe(50);
|
|
44
45
|
expect(lengthChange.expected).toBe(100);
|
|
45
46
|
});
|
|
46
47
|
|
|
47
|
-
test(
|
|
48
|
+
test("长度相同无变化", () => {
|
|
48
49
|
const existingColumn = {
|
|
49
|
-
type:
|
|
50
|
+
type: "varchar",
|
|
50
51
|
max: 100,
|
|
51
52
|
nullable: false,
|
|
52
|
-
defaultValue:
|
|
53
|
-
comment:
|
|
53
|
+
defaultValue: "",
|
|
54
|
+
comment: "用户名"
|
|
54
55
|
};
|
|
55
56
|
const fieldDef = {
|
|
56
|
-
name:
|
|
57
|
-
type:
|
|
57
|
+
name: "用户名",
|
|
58
|
+
type: "string",
|
|
58
59
|
max: 100,
|
|
59
60
|
nullable: false,
|
|
60
61
|
default: null
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
64
|
-
const lengthChange = changes.find((c: any) => c.type ===
|
|
65
|
+
const lengthChange = changes.find((c: any) => c.type === "length");
|
|
65
66
|
|
|
66
67
|
expect(lengthChange).toBeUndefined();
|
|
67
68
|
});
|
|
68
69
|
});
|
|
69
70
|
|
|
70
|
-
describe(
|
|
71
|
-
test(
|
|
71
|
+
describe("注释变化检测", () => {
|
|
72
|
+
test("注释变化被检测到", () => {
|
|
72
73
|
const existingColumn = {
|
|
73
|
-
type:
|
|
74
|
+
type: "varchar",
|
|
74
75
|
max: 100,
|
|
75
76
|
nullable: false,
|
|
76
|
-
defaultValue:
|
|
77
|
-
comment:
|
|
77
|
+
defaultValue: "",
|
|
78
|
+
comment: "旧注释"
|
|
78
79
|
};
|
|
79
80
|
const fieldDef = {
|
|
80
|
-
name:
|
|
81
|
-
type:
|
|
81
|
+
name: "新注释",
|
|
82
|
+
type: "string",
|
|
82
83
|
max: 100,
|
|
83
84
|
nullable: false,
|
|
84
85
|
default: null
|
|
85
86
|
};
|
|
86
87
|
|
|
87
88
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
88
|
-
const commentChange = changes.find((c: any) => c.type ===
|
|
89
|
+
const commentChange = changes.find((c: any) => c.type === "comment");
|
|
89
90
|
|
|
90
91
|
expect(commentChange).toBeDefined();
|
|
91
|
-
expect(commentChange.current).toBe(
|
|
92
|
-
expect(commentChange.expected).toBe(
|
|
92
|
+
expect(commentChange.current).toBe("旧注释");
|
|
93
|
+
expect(commentChange.expected).toBe("新注释");
|
|
93
94
|
});
|
|
94
95
|
|
|
95
|
-
test(
|
|
96
|
+
test("注释相同无变化", () => {
|
|
96
97
|
const existingColumn = {
|
|
97
|
-
type:
|
|
98
|
+
type: "varchar",
|
|
98
99
|
max: 100,
|
|
99
100
|
nullable: false,
|
|
100
|
-
defaultValue:
|
|
101
|
-
comment:
|
|
101
|
+
defaultValue: "",
|
|
102
|
+
comment: "用户名"
|
|
102
103
|
};
|
|
103
104
|
const fieldDef = {
|
|
104
|
-
name:
|
|
105
|
-
type:
|
|
105
|
+
name: "用户名",
|
|
106
|
+
type: "string",
|
|
106
107
|
max: 100,
|
|
107
108
|
nullable: false,
|
|
108
109
|
default: null
|
|
109
110
|
};
|
|
110
111
|
|
|
111
112
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
112
|
-
const commentChange = changes.find((c: any) => c.type ===
|
|
113
|
+
const commentChange = changes.find((c: any) => c.type === "comment");
|
|
113
114
|
|
|
114
115
|
expect(commentChange).toBeUndefined();
|
|
115
116
|
});
|
|
116
117
|
});
|
|
117
118
|
|
|
118
|
-
describe(
|
|
119
|
-
test(
|
|
119
|
+
describe("数据类型变化检测", () => {
|
|
120
|
+
test("类型变化被检测到", () => {
|
|
120
121
|
const existingColumn = {
|
|
121
|
-
type:
|
|
122
|
+
type: "bigint",
|
|
122
123
|
max: null,
|
|
123
124
|
nullable: false,
|
|
124
125
|
defaultValue: 0,
|
|
125
|
-
comment:
|
|
126
|
+
comment: "数量"
|
|
126
127
|
};
|
|
127
128
|
const fieldDef = {
|
|
128
|
-
name:
|
|
129
|
-
type:
|
|
129
|
+
name: "数量",
|
|
130
|
+
type: "string",
|
|
130
131
|
max: 100,
|
|
131
132
|
nullable: false,
|
|
132
133
|
default: null
|
|
133
134
|
};
|
|
134
135
|
|
|
135
136
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
136
|
-
const typeChange = changes.find((c: any) => c.type ===
|
|
137
|
+
const typeChange = changes.find((c: any) => c.type === "datatype");
|
|
137
138
|
|
|
138
139
|
expect(typeChange).toBeDefined();
|
|
139
|
-
expect(typeChange.current).toBe(
|
|
140
|
-
expect(typeChange.expected).toBe(
|
|
140
|
+
expect(typeChange.current).toBe("bigint");
|
|
141
|
+
expect(typeChange.expected).toBe("varchar");
|
|
141
142
|
});
|
|
142
143
|
|
|
143
|
-
test(
|
|
144
|
+
test("类型相同无变化", () => {
|
|
144
145
|
const existingColumn = {
|
|
145
|
-
type:
|
|
146
|
+
type: "bigint",
|
|
146
147
|
max: null,
|
|
147
148
|
nullable: false,
|
|
148
149
|
defaultValue: 0,
|
|
149
|
-
comment:
|
|
150
|
+
comment: "数量"
|
|
150
151
|
};
|
|
151
152
|
const fieldDef = {
|
|
152
|
-
name:
|
|
153
|
-
type:
|
|
153
|
+
name: "数量",
|
|
154
|
+
type: "number",
|
|
154
155
|
max: null,
|
|
155
156
|
nullable: false,
|
|
156
157
|
default: 0
|
|
157
158
|
};
|
|
158
159
|
|
|
159
160
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
160
|
-
const typeChange = changes.find((c: any) => c.type ===
|
|
161
|
+
const typeChange = changes.find((c: any) => c.type === "datatype");
|
|
161
162
|
|
|
162
163
|
expect(typeChange).toBeUndefined();
|
|
163
164
|
});
|
|
164
165
|
});
|
|
165
166
|
|
|
166
|
-
describe(
|
|
167
|
-
test(
|
|
167
|
+
describe("可空性变化检测", () => {
|
|
168
|
+
test("nullable 变化被检测到", () => {
|
|
168
169
|
const existingColumn = {
|
|
169
|
-
type:
|
|
170
|
+
type: "varchar",
|
|
170
171
|
max: 100,
|
|
171
172
|
nullable: false,
|
|
172
|
-
defaultValue:
|
|
173
|
-
comment:
|
|
173
|
+
defaultValue: "",
|
|
174
|
+
comment: "用户名"
|
|
174
175
|
};
|
|
175
176
|
const fieldDef = {
|
|
176
|
-
name:
|
|
177
|
-
type:
|
|
177
|
+
name: "用户名",
|
|
178
|
+
type: "string",
|
|
178
179
|
max: 100,
|
|
179
180
|
nullable: true,
|
|
180
181
|
default: null
|
|
181
182
|
};
|
|
182
183
|
|
|
183
184
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
184
|
-
const nullableChange = changes.find((c: any) => c.type ===
|
|
185
|
+
const nullableChange = changes.find((c: any) => c.type === "nullable");
|
|
185
186
|
|
|
186
187
|
expect(nullableChange).toBeDefined();
|
|
187
188
|
expect(nullableChange.current).toBe(false);
|
|
@@ -189,92 +190,92 @@ describe('compareFieldDefinition', () => {
|
|
|
189
190
|
});
|
|
190
191
|
});
|
|
191
192
|
|
|
192
|
-
describe(
|
|
193
|
-
test(
|
|
193
|
+
describe("默认值变化检测", () => {
|
|
194
|
+
test("默认值变化被检测到", () => {
|
|
194
195
|
const existingColumn = {
|
|
195
|
-
type:
|
|
196
|
+
type: "varchar",
|
|
196
197
|
max: 100,
|
|
197
198
|
nullable: false,
|
|
198
|
-
defaultValue:
|
|
199
|
-
comment:
|
|
199
|
+
defaultValue: "old",
|
|
200
|
+
comment: "用户名"
|
|
200
201
|
};
|
|
201
202
|
const fieldDef = {
|
|
202
|
-
name:
|
|
203
|
-
type:
|
|
203
|
+
name: "用户名",
|
|
204
|
+
type: "string",
|
|
204
205
|
max: 100,
|
|
205
206
|
nullable: false,
|
|
206
|
-
default:
|
|
207
|
+
default: "new"
|
|
207
208
|
};
|
|
208
209
|
|
|
209
210
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
210
|
-
const defaultChange = changes.find((c: any) => c.type ===
|
|
211
|
+
const defaultChange = changes.find((c: any) => c.type === "default");
|
|
211
212
|
|
|
212
213
|
expect(defaultChange).toBeDefined();
|
|
213
|
-
expect(defaultChange.current).toBe(
|
|
214
|
-
expect(defaultChange.expected).toBe(
|
|
214
|
+
expect(defaultChange.current).toBe("old");
|
|
215
|
+
expect(defaultChange.expected).toBe("new");
|
|
215
216
|
});
|
|
216
217
|
|
|
217
|
-
test(
|
|
218
|
+
test("null 默认值被正确处理", () => {
|
|
218
219
|
const existingColumn = {
|
|
219
|
-
type:
|
|
220
|
+
type: "varchar",
|
|
220
221
|
max: 100,
|
|
221
222
|
nullable: false,
|
|
222
|
-
defaultValue:
|
|
223
|
-
comment:
|
|
223
|
+
defaultValue: "",
|
|
224
|
+
comment: "用户名"
|
|
224
225
|
};
|
|
225
226
|
const fieldDef = {
|
|
226
|
-
name:
|
|
227
|
-
type:
|
|
227
|
+
name: "用户名",
|
|
228
|
+
type: "string",
|
|
228
229
|
max: 100,
|
|
229
230
|
nullable: false,
|
|
230
231
|
default: null // null 会被解析为空字符串
|
|
231
232
|
};
|
|
232
233
|
|
|
233
234
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
234
|
-
const defaultChange = changes.find((c: any) => c.type ===
|
|
235
|
+
const defaultChange = changes.find((c: any) => c.type === "default");
|
|
235
236
|
|
|
236
237
|
// null -> '' (空字符串),与现有值相同,无变化
|
|
237
238
|
expect(defaultChange).toBeUndefined();
|
|
238
239
|
});
|
|
239
240
|
});
|
|
240
241
|
|
|
241
|
-
describe(
|
|
242
|
-
test(
|
|
242
|
+
describe("多变化组合", () => {
|
|
243
|
+
test("多个变化同时被检测", () => {
|
|
243
244
|
const existingColumn = {
|
|
244
|
-
type:
|
|
245
|
+
type: "varchar",
|
|
245
246
|
max: 50,
|
|
246
247
|
nullable: false,
|
|
247
|
-
defaultValue:
|
|
248
|
-
comment:
|
|
248
|
+
defaultValue: "old",
|
|
249
|
+
comment: "旧注释"
|
|
249
250
|
};
|
|
250
251
|
const fieldDef = {
|
|
251
|
-
name:
|
|
252
|
-
type:
|
|
252
|
+
name: "新注释",
|
|
253
|
+
type: "string",
|
|
253
254
|
max: 100,
|
|
254
255
|
nullable: true,
|
|
255
|
-
default:
|
|
256
|
+
default: "new"
|
|
256
257
|
};
|
|
257
258
|
|
|
258
259
|
const changes = compareFieldDefinition(existingColumn, fieldDef);
|
|
259
260
|
|
|
260
261
|
expect(changes.length).toBe(4); // length, comment, nullable, default
|
|
261
|
-
expect(changes.some((c: any) => c.type ===
|
|
262
|
-
expect(changes.some((c: any) => c.type ===
|
|
263
|
-
expect(changes.some((c: any) => c.type ===
|
|
264
|
-
expect(changes.some((c: any) => c.type ===
|
|
262
|
+
expect(changes.some((c: any) => c.type === "length")).toBe(true);
|
|
263
|
+
expect(changes.some((c: any) => c.type === "comment")).toBe(true);
|
|
264
|
+
expect(changes.some((c: any) => c.type === "nullable")).toBe(true);
|
|
265
|
+
expect(changes.some((c: any) => c.type === "default")).toBe(true);
|
|
265
266
|
});
|
|
266
267
|
|
|
267
|
-
test(
|
|
268
|
+
test("无变化返回空数组", () => {
|
|
268
269
|
const existingColumn = {
|
|
269
|
-
type:
|
|
270
|
+
type: "varchar",
|
|
270
271
|
max: 100,
|
|
271
272
|
nullable: false,
|
|
272
|
-
defaultValue:
|
|
273
|
-
comment:
|
|
273
|
+
defaultValue: "",
|
|
274
|
+
comment: "用户名"
|
|
274
275
|
};
|
|
275
276
|
const fieldDef = {
|
|
276
|
-
name:
|
|
277
|
-
type:
|
|
277
|
+
name: "用户名",
|
|
278
|
+
type: "string",
|
|
278
279
|
max: 100,
|
|
279
280
|
nullable: false,
|
|
280
281
|
default: null
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 测试 syncDb 对 array_number_string 和 array_number_text 类型的支持
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
|
|
7
|
+
import { getSqlType, resolveDefaultValue, generateDefaultSql, isStringOrArrayType } from "../sync/syncDb/types.js";
|
|
8
|
+
|
|
9
|
+
describe("syncDb - array_number 类型支持", () => {
|
|
10
|
+
// ==================== 类型判断测试 ====================
|
|
11
|
+
|
|
12
|
+
test("isStringOrArrayType: array_number_string 需要长度", () => {
|
|
13
|
+
expect(isStringOrArrayType("array_number_string")).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("isStringOrArrayType: array_number_text 不需要长度", () => {
|
|
17
|
+
expect(isStringOrArrayType("array_number_text")).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// ==================== SQL 类型映射测试 ====================
|
|
21
|
+
|
|
22
|
+
test("getSqlType: array_number_string 生成 VARCHAR(max)", () => {
|
|
23
|
+
const sqlType = getSqlType("array_number_string", 500);
|
|
24
|
+
expect(sqlType).toMatch(/VARCHAR\(500\)/i);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("getSqlType: array_number_text 生成 TEXT/MEDIUMTEXT", () => {
|
|
28
|
+
const sqlType = getSqlType("array_number_text", null);
|
|
29
|
+
expect(sqlType).toMatch(/TEXT/i);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("getSqlType: array_number_string 使用 max 参数", () => {
|
|
33
|
+
const sqlType1 = getSqlType("array_number_string", 200);
|
|
34
|
+
const sqlType2 = getSqlType("array_number_string", 1000);
|
|
35
|
+
|
|
36
|
+
expect(sqlType1).toMatch(/VARCHAR\(200\)/i);
|
|
37
|
+
expect(sqlType2).toMatch(/VARCHAR\(1000\)/i);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ==================== 默认值处理测试 ====================
|
|
41
|
+
|
|
42
|
+
test('resolveDefaultValue: array_number_string null 时返回 "[]"', () => {
|
|
43
|
+
const result = resolveDefaultValue(null, "array_number_string");
|
|
44
|
+
expect(result).toBe("[]");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("resolveDefaultValue: array_number_string 有默认值时保留", () => {
|
|
48
|
+
const result = resolveDefaultValue("[1,2,3]", "array_number_string");
|
|
49
|
+
expect(result).toBe("[1,2,3]");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('resolveDefaultValue: array_number_text null 时返回 "null"', () => {
|
|
53
|
+
const result = resolveDefaultValue(null, "array_number_text");
|
|
54
|
+
expect(result).toBe("null");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("resolveDefaultValue: array_number_text 有默认值时保留", () => {
|
|
58
|
+
const result = resolveDefaultValue("[100,200]", "array_number_text");
|
|
59
|
+
expect(result).toBe("[100,200]");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('resolveDefaultValue: 字符串 "null" 也视为 null', () => {
|
|
63
|
+
const result1 = resolveDefaultValue("null", "array_number_string");
|
|
64
|
+
const result2 = resolveDefaultValue("null", "array_number_text");
|
|
65
|
+
|
|
66
|
+
expect(result1).toBe("[]");
|
|
67
|
+
expect(result2).toBe("null");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// ==================== SQL DEFAULT 子句测试 ====================
|
|
71
|
+
|
|
72
|
+
test("generateDefaultSql: array_number_string 生成 DEFAULT 子句", () => {
|
|
73
|
+
const sql = generateDefaultSql("[]", "array_number_string");
|
|
74
|
+
expect(sql).toBe(" DEFAULT '[]'");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("generateDefaultSql: array_number_string 自定义默认值", () => {
|
|
78
|
+
const sql = generateDefaultSql("[10,20,30]", "array_number_string");
|
|
79
|
+
expect(sql).toBe(" DEFAULT '[10,20,30]'");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("generateDefaultSql: array_number_text 不生成 DEFAULT", () => {
|
|
83
|
+
const sql = generateDefaultSql("[]", "array_number_text");
|
|
84
|
+
expect(sql).toBe("");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("generateDefaultSql: array_number_text null 时不生成 DEFAULT", () => {
|
|
88
|
+
const sql = generateDefaultSql("null", "array_number_text");
|
|
89
|
+
expect(sql).toBe("");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// ==================== 单引号转义测试 ====================
|
|
93
|
+
|
|
94
|
+
test("generateDefaultSql: 默认值包含单引号时正确转义", () => {
|
|
95
|
+
const sql = generateDefaultSql("[1,'test',2]", "array_number_string");
|
|
96
|
+
expect(sql).toBe(" DEFAULT '[1,''test'',2]'");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ==================== 完整流程测试 ====================
|
|
100
|
+
|
|
101
|
+
test("完整流程: array_number_string 字段定义", () => {
|
|
102
|
+
// 模拟字段定义:"标签ID|array_number_string|0|500|[]|0"
|
|
103
|
+
const fieldType = "array_number_string";
|
|
104
|
+
const fieldMax = 500;
|
|
105
|
+
const fieldDefault = null;
|
|
106
|
+
|
|
107
|
+
// 1. 判断是否需要长度
|
|
108
|
+
expect(isStringOrArrayType(fieldType)).toBe(true);
|
|
109
|
+
|
|
110
|
+
// 2. 获取 SQL 类型
|
|
111
|
+
const sqlType = getSqlType(fieldType, fieldMax);
|
|
112
|
+
expect(sqlType).toMatch(/VARCHAR\(500\)/i);
|
|
113
|
+
|
|
114
|
+
// 3. 处理默认值
|
|
115
|
+
const actualDefault = resolveDefaultValue(fieldDefault, fieldType);
|
|
116
|
+
expect(actualDefault).toBe("[]");
|
|
117
|
+
|
|
118
|
+
// 4. 生成 DEFAULT 子句
|
|
119
|
+
const defaultSql = generateDefaultSql(actualDefault, fieldType);
|
|
120
|
+
expect(defaultSql).toBe(" DEFAULT '[]'");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("完整流程: array_number_text 字段定义", () => {
|
|
124
|
+
// 模拟字段定义:"关联ID|array_number_text|||null|0"
|
|
125
|
+
const fieldType = "array_number_text";
|
|
126
|
+
const fieldMax = null;
|
|
127
|
+
const fieldDefault = null;
|
|
128
|
+
|
|
129
|
+
// 1. 判断是否需要长度
|
|
130
|
+
expect(isStringOrArrayType(fieldType)).toBe(false);
|
|
131
|
+
|
|
132
|
+
// 2. 获取 SQL 类型
|
|
133
|
+
const sqlType = getSqlType(fieldType, fieldMax);
|
|
134
|
+
expect(sqlType).toMatch(/TEXT/i);
|
|
135
|
+
|
|
136
|
+
// 3. 处理默认值
|
|
137
|
+
const actualDefault = resolveDefaultValue(fieldDefault, fieldType);
|
|
138
|
+
expect(actualDefault).toBe("null");
|
|
139
|
+
|
|
140
|
+
// 4. 生成 DEFAULT 子句(TEXT 类型不支持)
|
|
141
|
+
const defaultSql = generateDefaultSql(actualDefault, fieldType);
|
|
142
|
+
expect(defaultSql).toBe("");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("完整流程: array_number_string 自定义默认值", () => {
|
|
146
|
+
// 模拟字段定义:"分数|array_number_string|2|10|[60,70,80]|0"
|
|
147
|
+
const fieldType = "array_number_string";
|
|
148
|
+
const fieldMax = 10;
|
|
149
|
+
const fieldDefault = "[60,70,80]";
|
|
150
|
+
|
|
151
|
+
const sqlType = getSqlType(fieldType, fieldMax);
|
|
152
|
+
expect(sqlType).toMatch(/VARCHAR\(10\)/i);
|
|
153
|
+
|
|
154
|
+
const actualDefault = resolveDefaultValue(fieldDefault, fieldType);
|
|
155
|
+
expect(actualDefault).toBe("[60,70,80]");
|
|
156
|
+
|
|
157
|
+
const defaultSql = generateDefaultSql(actualDefault, fieldType);
|
|
158
|
+
expect(defaultSql).toBe(" DEFAULT '[60,70,80]'");
|
|
159
|
+
});
|
|
160
|
+
});
|