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
package/tests/validator.test.ts
CHANGED
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
* 测试静态类方法、返回结构、各种验证场景
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { describe, test, expect } from
|
|
7
|
-
|
|
6
|
+
import { describe, test, expect } from "bun:test";
|
|
7
|
+
|
|
8
|
+
import { Validator } from "../lib/validator";
|
|
8
9
|
|
|
9
10
|
// ========== 返回结构测试 ==========
|
|
10
11
|
|
|
11
|
-
describe(
|
|
12
|
-
test(
|
|
13
|
-
const data = { name:
|
|
14
|
-
const rules = { name: { name:
|
|
12
|
+
describe("Validator.validate - 返回结构", () => {
|
|
13
|
+
test("验证通过时返回正确结构", () => {
|
|
14
|
+
const data = { name: "test" };
|
|
15
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 10 } };
|
|
15
16
|
|
|
16
17
|
const result = Validator.validate(data, rules);
|
|
17
18
|
|
|
@@ -23,9 +24,9 @@ describe('Validator.validate - 返回结构', () => {
|
|
|
23
24
|
expect(result.fieldErrors).toEqual({});
|
|
24
25
|
});
|
|
25
26
|
|
|
26
|
-
test(
|
|
27
|
-
const data = { name:
|
|
28
|
-
const rules = { name: { name:
|
|
27
|
+
test("验证失败时返回正确结构", () => {
|
|
28
|
+
const data = { name: "a" };
|
|
29
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 10 } };
|
|
29
30
|
|
|
30
31
|
const result = Validator.validate(data, rules);
|
|
31
32
|
|
|
@@ -33,15 +34,15 @@ describe('Validator.validate - 返回结构', () => {
|
|
|
33
34
|
expect(result.failed).toBe(true);
|
|
34
35
|
expect(result.firstError).toBeDefined();
|
|
35
36
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
36
|
-
expect(result.errorFields).toContain(
|
|
37
|
+
expect(result.errorFields).toContain("name");
|
|
37
38
|
expect(result.fieldErrors.name).toBeDefined();
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
test(
|
|
41
|
-
const data = { name:
|
|
41
|
+
test("多字段错误时返回所有错误", () => {
|
|
42
|
+
const data = { name: "a", age: 200 };
|
|
42
43
|
const rules = {
|
|
43
|
-
name: { name:
|
|
44
|
-
age: { name:
|
|
44
|
+
name: { name: "名称", type: "string", min: 2, max: 10 },
|
|
45
|
+
age: { name: "年龄", type: "number", min: 0, max: 150 }
|
|
45
46
|
};
|
|
46
47
|
|
|
47
48
|
const result = Validator.validate(data, rules);
|
|
@@ -49,183 +50,183 @@ describe('Validator.validate - 返回结构', () => {
|
|
|
49
50
|
expect(result.code).toBe(1);
|
|
50
51
|
expect(result.errors.length).toBe(2);
|
|
51
52
|
expect(result.errorFields.length).toBe(2);
|
|
52
|
-
expect(result.errorFields).toContain(
|
|
53
|
-
expect(result.errorFields).toContain(
|
|
53
|
+
expect(result.errorFields).toContain("name");
|
|
54
|
+
expect(result.errorFields).toContain("age");
|
|
54
55
|
});
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
// ========== 参数检查测试 ==========
|
|
58
59
|
|
|
59
|
-
describe(
|
|
60
|
-
test(
|
|
60
|
+
describe("Validator.validate - 参数检查", () => {
|
|
61
|
+
test("data 为 null", () => {
|
|
61
62
|
const result = Validator.validate(null as any, {});
|
|
62
63
|
|
|
63
64
|
expect(result.code).toBe(1);
|
|
64
|
-
expect(result.firstError).toContain(
|
|
65
|
+
expect(result.firstError).toContain("对象格式");
|
|
65
66
|
});
|
|
66
67
|
|
|
67
|
-
test(
|
|
68
|
+
test("data 为 undefined", () => {
|
|
68
69
|
const result = Validator.validate(undefined as any, {});
|
|
69
70
|
|
|
70
71
|
expect(result.code).toBe(1);
|
|
71
|
-
expect(result.firstError).toContain(
|
|
72
|
+
expect(result.firstError).toContain("对象格式");
|
|
72
73
|
});
|
|
73
74
|
|
|
74
|
-
test(
|
|
75
|
+
test("data 为数组", () => {
|
|
75
76
|
const result = Validator.validate([] as any, {});
|
|
76
77
|
|
|
77
78
|
expect(result.code).toBe(1);
|
|
78
|
-
expect(result.firstError).toContain(
|
|
79
|
+
expect(result.firstError).toContain("对象格式");
|
|
79
80
|
});
|
|
80
81
|
|
|
81
|
-
test(
|
|
82
|
-
const result = Validator.validate(
|
|
82
|
+
test("data 为字符串", () => {
|
|
83
|
+
const result = Validator.validate("string" as any, {});
|
|
83
84
|
|
|
84
85
|
expect(result.code).toBe(1);
|
|
85
|
-
expect(result.firstError).toContain(
|
|
86
|
+
expect(result.firstError).toContain("对象格式");
|
|
86
87
|
});
|
|
87
88
|
|
|
88
|
-
test(
|
|
89
|
+
test("rules 为 null", () => {
|
|
89
90
|
const result = Validator.validate({}, null as any);
|
|
90
91
|
|
|
91
92
|
expect(result.code).toBe(1);
|
|
92
|
-
expect(result.firstError).toContain(
|
|
93
|
+
expect(result.firstError).toContain("对象格式");
|
|
93
94
|
});
|
|
94
95
|
|
|
95
|
-
test(
|
|
96
|
+
test("rules 为 undefined", () => {
|
|
96
97
|
const result = Validator.validate({}, undefined as any);
|
|
97
98
|
|
|
98
99
|
expect(result.code).toBe(1);
|
|
99
|
-
expect(result.firstError).toContain(
|
|
100
|
+
expect(result.firstError).toContain("对象格式");
|
|
100
101
|
});
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
// ========== 必填字段测试 ==========
|
|
104
105
|
|
|
105
|
-
describe(
|
|
106
|
-
const rules = { name: { name:
|
|
106
|
+
describe("Validator.validate - 必填字段", () => {
|
|
107
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 20 } };
|
|
107
108
|
|
|
108
|
-
test(
|
|
109
|
-
const result = Validator.validate({ name:
|
|
109
|
+
test("字段存在且有值 - 通过", () => {
|
|
110
|
+
const result = Validator.validate({ name: "test" }, rules, ["name"]);
|
|
110
111
|
expect(result.code).toBe(0);
|
|
111
112
|
});
|
|
112
113
|
|
|
113
|
-
test(
|
|
114
|
-
const result = Validator.validate({}, rules, [
|
|
114
|
+
test("字段不存在 - 失败", () => {
|
|
115
|
+
const result = Validator.validate({}, rules, ["name"]);
|
|
115
116
|
expect(result.code).toBe(1);
|
|
116
|
-
expect(result.fieldErrors.name).toContain(
|
|
117
|
+
expect(result.fieldErrors.name).toContain("必填项");
|
|
117
118
|
});
|
|
118
119
|
|
|
119
|
-
test(
|
|
120
|
-
const result = Validator.validate({ name:
|
|
120
|
+
test("字段值为空字符串 - 失败", () => {
|
|
121
|
+
const result = Validator.validate({ name: "" }, rules, ["name"]);
|
|
121
122
|
expect(result.code).toBe(1);
|
|
122
|
-
expect(result.fieldErrors.name).toContain(
|
|
123
|
+
expect(result.fieldErrors.name).toContain("必填项");
|
|
123
124
|
});
|
|
124
125
|
|
|
125
|
-
test(
|
|
126
|
-
const result = Validator.validate({ name: null }, rules, [
|
|
126
|
+
test("字段值为 null - 失败", () => {
|
|
127
|
+
const result = Validator.validate({ name: null }, rules, ["name"]);
|
|
127
128
|
expect(result.code).toBe(1);
|
|
128
|
-
expect(result.fieldErrors.name).toContain(
|
|
129
|
+
expect(result.fieldErrors.name).toContain("必填项");
|
|
129
130
|
});
|
|
130
131
|
|
|
131
|
-
test(
|
|
132
|
-
const result = Validator.validate({ name: undefined }, rules, [
|
|
132
|
+
test("字段值为 undefined - 失败", () => {
|
|
133
|
+
const result = Validator.validate({ name: undefined }, rules, ["name"]);
|
|
133
134
|
expect(result.code).toBe(1);
|
|
134
|
-
expect(result.fieldErrors.name).toContain(
|
|
135
|
+
expect(result.fieldErrors.name).toContain("必填项");
|
|
135
136
|
});
|
|
136
137
|
|
|
137
|
-
test(
|
|
138
|
+
test("多个必填字段全部缺失", () => {
|
|
138
139
|
const multiRules = {
|
|
139
|
-
name: { name:
|
|
140
|
-
email: { name:
|
|
140
|
+
name: { name: "名称", type: "string", min: 2, max: 20 },
|
|
141
|
+
email: { name: "邮箱", type: "string", regexp: "@email" }
|
|
141
142
|
};
|
|
142
143
|
|
|
143
|
-
const result = Validator.validate({}, multiRules, [
|
|
144
|
+
const result = Validator.validate({}, multiRules, ["name", "email"]);
|
|
144
145
|
|
|
145
146
|
expect(result.code).toBe(1);
|
|
146
147
|
expect(result.errors.length).toBe(2);
|
|
147
|
-
expect(result.fieldErrors.name).toContain(
|
|
148
|
-
expect(result.fieldErrors.email).toContain(
|
|
148
|
+
expect(result.fieldErrors.name).toContain("必填项");
|
|
149
|
+
expect(result.fieldErrors.email).toContain("必填项");
|
|
149
150
|
});
|
|
150
151
|
|
|
151
|
-
test(
|
|
152
|
-
const result = Validator.validate({}, rules, [
|
|
153
|
-
expect(result.firstError).toContain(
|
|
152
|
+
test("必填字段使用字段标签", () => {
|
|
153
|
+
const result = Validator.validate({}, rules, ["name"]);
|
|
154
|
+
expect(result.firstError).toContain("名称");
|
|
154
155
|
});
|
|
155
156
|
|
|
156
|
-
test(
|
|
157
|
-
const noLabelRules = { name: { type:
|
|
158
|
-
const result = Validator.validate({}, noLabelRules, [
|
|
159
|
-
expect(result.firstError).toContain(
|
|
157
|
+
test("必填字段无标签时使用字段名", () => {
|
|
158
|
+
const noLabelRules = { name: { type: "string", min: 2, max: 20 } };
|
|
159
|
+
const result = Validator.validate({}, noLabelRules, ["name"]);
|
|
160
|
+
expect(result.firstError).toContain("name");
|
|
160
161
|
});
|
|
161
162
|
});
|
|
162
163
|
|
|
163
164
|
// ========== string 类型测试 ==========
|
|
164
165
|
|
|
165
|
-
describe(
|
|
166
|
-
test(
|
|
167
|
-
const rules = { name: { name:
|
|
168
|
-
const result = Validator.validate({ name:
|
|
166
|
+
describe("Validator.validate - string 类型", () => {
|
|
167
|
+
test("有效字符串 - 通过", () => {
|
|
168
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 10 } };
|
|
169
|
+
const result = Validator.validate({ name: "test" }, rules);
|
|
169
170
|
expect(result.code).toBe(0);
|
|
170
171
|
});
|
|
171
172
|
|
|
172
|
-
test(
|
|
173
|
-
const rules = { name: { name:
|
|
173
|
+
test("非字符串类型 - 失败", () => {
|
|
174
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 10 } };
|
|
174
175
|
const result = Validator.validate({ name: 123 }, rules);
|
|
175
176
|
expect(result.code).toBe(1);
|
|
176
|
-
expect(result.firstError).toContain(
|
|
177
|
+
expect(result.firstError).toContain("字符串");
|
|
177
178
|
});
|
|
178
179
|
|
|
179
|
-
test(
|
|
180
|
-
const rules = { name: { name:
|
|
181
|
-
const result = Validator.validate({ name:
|
|
180
|
+
test("长度小于 min - 失败", () => {
|
|
181
|
+
const rules = { name: { name: "名称", type: "string", min: 5, max: 20 } };
|
|
182
|
+
const result = Validator.validate({ name: "abc" }, rules);
|
|
182
183
|
expect(result.code).toBe(1);
|
|
183
|
-
expect(result.firstError).toContain(
|
|
184
|
+
expect(result.firstError).toContain("5");
|
|
184
185
|
});
|
|
185
186
|
|
|
186
|
-
test(
|
|
187
|
-
const rules = { name: { name:
|
|
188
|
-
const result = Validator.validate({ name:
|
|
187
|
+
test("长度等于 min - 通过", () => {
|
|
188
|
+
const rules = { name: { name: "名称", type: "string", min: 3, max: 20 } };
|
|
189
|
+
const result = Validator.validate({ name: "abc" }, rules);
|
|
189
190
|
expect(result.code).toBe(0);
|
|
190
191
|
});
|
|
191
192
|
|
|
192
|
-
test(
|
|
193
|
-
const rules = { name: { name:
|
|
194
|
-
const result = Validator.validate({ name:
|
|
193
|
+
test("长度大于 max - 失败", () => {
|
|
194
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 5 } };
|
|
195
|
+
const result = Validator.validate({ name: "abcdefg" }, rules);
|
|
195
196
|
expect(result.code).toBe(1);
|
|
196
|
-
expect(result.firstError).toContain(
|
|
197
|
+
expect(result.firstError).toContain("5");
|
|
197
198
|
});
|
|
198
199
|
|
|
199
|
-
test(
|
|
200
|
-
const rules = { name: { name:
|
|
201
|
-
const result = Validator.validate({ name:
|
|
200
|
+
test("长度等于 max - 通过", () => {
|
|
201
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 5 } };
|
|
202
|
+
const result = Validator.validate({ name: "abcde" }, rules);
|
|
202
203
|
expect(result.code).toBe(0);
|
|
203
204
|
});
|
|
204
205
|
|
|
205
|
-
test(
|
|
206
|
-
const rules = { name: { name:
|
|
207
|
-
const result = Validator.validate({ name:
|
|
206
|
+
test("中文字符按字符数计算", () => {
|
|
207
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 5 } };
|
|
208
|
+
const result = Validator.validate({ name: "你好世界" }, rules); // 4 个字符
|
|
208
209
|
expect(result.code).toBe(0);
|
|
209
210
|
});
|
|
210
211
|
|
|
211
|
-
test(
|
|
212
|
-
const rules = { name: { name:
|
|
213
|
-
const result = Validator.validate({ name:
|
|
212
|
+
test("max 为 0 时不检查最大长度", () => {
|
|
213
|
+
const rules = { name: { name: "名称", type: "string", min: 0, max: 0 } };
|
|
214
|
+
const result = Validator.validate({ name: "a".repeat(1000) }, rules);
|
|
214
215
|
expect(result.code).toBe(0);
|
|
215
216
|
});
|
|
216
217
|
});
|
|
217
218
|
|
|
218
219
|
// ========== text 类型测试 ==========
|
|
219
220
|
|
|
220
|
-
describe(
|
|
221
|
-
test(
|
|
222
|
-
const rules = { content: { name:
|
|
223
|
-
const result = Validator.validate({ content:
|
|
221
|
+
describe("Validator.validate - text 类型", () => {
|
|
222
|
+
test("text 类型与 string 类型行为一致", () => {
|
|
223
|
+
const rules = { content: { name: "内容", type: "text", min: 2, max: 100 } };
|
|
224
|
+
const result = Validator.validate({ content: "hello world" }, rules);
|
|
224
225
|
expect(result.code).toBe(0);
|
|
225
226
|
});
|
|
226
227
|
|
|
227
|
-
test(
|
|
228
|
-
const rules = { content: { name:
|
|
228
|
+
test("text 类型非字符串 - 失败", () => {
|
|
229
|
+
const rules = { content: { name: "内容", type: "text", min: 2, max: 100 } };
|
|
229
230
|
const result = Validator.validate({ content: 123 }, rules);
|
|
230
231
|
expect(result.code).toBe(1);
|
|
231
232
|
});
|
|
@@ -233,90 +234,90 @@ describe('Validator.validate - text 类型', () => {
|
|
|
233
234
|
|
|
234
235
|
// ========== number 类型测试 ==========
|
|
235
236
|
|
|
236
|
-
describe(
|
|
237
|
-
test(
|
|
238
|
-
const rules = { age: { name:
|
|
237
|
+
describe("Validator.validate - number 类型", () => {
|
|
238
|
+
test("有效数字 - 通过", () => {
|
|
239
|
+
const rules = { age: { name: "年龄", type: "number", min: 0, max: 150 } };
|
|
239
240
|
const result = Validator.validate({ age: 25 }, rules);
|
|
240
241
|
expect(result.code).toBe(0);
|
|
241
242
|
});
|
|
242
243
|
|
|
243
|
-
test(
|
|
244
|
-
const rules = { age: { name:
|
|
245
|
-
const result = Validator.validate({ age:
|
|
244
|
+
test("字符串数字自动转换 - 通过", () => {
|
|
245
|
+
const rules = { age: { name: "年龄", type: "number", min: 0, max: 150 } };
|
|
246
|
+
const result = Validator.validate({ age: "25" }, rules);
|
|
246
247
|
expect(result.code).toBe(0);
|
|
247
248
|
});
|
|
248
249
|
|
|
249
|
-
test(
|
|
250
|
-
const rules = { age: { name:
|
|
251
|
-
const result = Validator.validate({ age:
|
|
250
|
+
test("非数字字符串 - 失败", () => {
|
|
251
|
+
const rules = { age: { name: "年龄", type: "number", min: 0, max: 150 } };
|
|
252
|
+
const result = Validator.validate({ age: "abc" }, rules);
|
|
252
253
|
expect(result.code).toBe(1);
|
|
253
|
-
expect(result.firstError).toContain(
|
|
254
|
+
expect(result.firstError).toContain("数字");
|
|
254
255
|
});
|
|
255
256
|
|
|
256
|
-
test(
|
|
257
|
-
const rules = { age: { name:
|
|
257
|
+
test("NaN - 失败", () => {
|
|
258
|
+
const rules = { age: { name: "年龄", type: "number", min: 0, max: 150 } };
|
|
258
259
|
const result = Validator.validate({ age: NaN }, rules);
|
|
259
260
|
expect(result.code).toBe(1);
|
|
260
261
|
});
|
|
261
262
|
|
|
262
|
-
test(
|
|
263
|
-
const rules = { value: { name:
|
|
263
|
+
test("Infinity - 失败", () => {
|
|
264
|
+
const rules = { value: { name: "值", type: "number", min: 0, max: 100 } };
|
|
264
265
|
const result = Validator.validate({ value: Infinity }, rules);
|
|
265
266
|
expect(result.code).toBe(1);
|
|
266
267
|
});
|
|
267
268
|
|
|
268
|
-
test(
|
|
269
|
-
const rules = { value: { name:
|
|
269
|
+
test("-Infinity - 失败", () => {
|
|
270
|
+
const rules = { value: { name: "值", type: "number", min: 0, max: 100 } };
|
|
270
271
|
const result = Validator.validate({ value: -Infinity }, rules);
|
|
271
272
|
expect(result.code).toBe(1);
|
|
272
273
|
});
|
|
273
274
|
|
|
274
|
-
test(
|
|
275
|
-
const rules = { count: { name:
|
|
275
|
+
test("0 是有效值", () => {
|
|
276
|
+
const rules = { count: { name: "计数", type: "number", min: 0, max: 100 } };
|
|
276
277
|
const result = Validator.validate({ count: 0 }, rules);
|
|
277
278
|
expect(result.code).toBe(0);
|
|
278
279
|
});
|
|
279
280
|
|
|
280
|
-
test(
|
|
281
|
-
const rules = { temp: { name:
|
|
281
|
+
test("负数范围验证", () => {
|
|
282
|
+
const rules = { temp: { name: "温度", type: "number", min: -50, max: 50 } };
|
|
282
283
|
const result = Validator.validate({ temp: -30 }, rules);
|
|
283
284
|
expect(result.code).toBe(0);
|
|
284
285
|
});
|
|
285
286
|
|
|
286
|
-
test(
|
|
287
|
-
const rules = { age: { name:
|
|
287
|
+
test("小于 min - 失败", () => {
|
|
288
|
+
const rules = { age: { name: "年龄", type: "number", min: 18, max: 100 } };
|
|
288
289
|
const result = Validator.validate({ age: 10 }, rules);
|
|
289
290
|
expect(result.code).toBe(1);
|
|
290
|
-
expect(result.firstError).toContain(
|
|
291
|
+
expect(result.firstError).toContain("18");
|
|
291
292
|
});
|
|
292
293
|
|
|
293
|
-
test(
|
|
294
|
-
const rules = { age: { name:
|
|
294
|
+
test("等于 min - 通过", () => {
|
|
295
|
+
const rules = { age: { name: "年龄", type: "number", min: 18, max: 100 } };
|
|
295
296
|
const result = Validator.validate({ age: 18 }, rules);
|
|
296
297
|
expect(result.code).toBe(0);
|
|
297
298
|
});
|
|
298
299
|
|
|
299
|
-
test(
|
|
300
|
-
const rules = { age: { name:
|
|
300
|
+
test("大于 max - 失败", () => {
|
|
301
|
+
const rules = { age: { name: "年龄", type: "number", min: 0, max: 100 } };
|
|
301
302
|
const result = Validator.validate({ age: 150 }, rules);
|
|
302
303
|
expect(result.code).toBe(1);
|
|
303
|
-
expect(result.firstError).toContain(
|
|
304
|
+
expect(result.firstError).toContain("100");
|
|
304
305
|
});
|
|
305
306
|
|
|
306
|
-
test(
|
|
307
|
-
const rules = { age: { name:
|
|
307
|
+
test("等于 max - 通过", () => {
|
|
308
|
+
const rules = { age: { name: "年龄", type: "number", min: 0, max: 100 } };
|
|
308
309
|
const result = Validator.validate({ age: 100 }, rules);
|
|
309
310
|
expect(result.code).toBe(0);
|
|
310
311
|
});
|
|
311
312
|
|
|
312
|
-
test(
|
|
313
|
-
const rules = { price: { name:
|
|
313
|
+
test("浮点数", () => {
|
|
314
|
+
const rules = { price: { name: "价格", type: "number", min: 0, max: 1000 } };
|
|
314
315
|
const result = Validator.validate({ price: 99.99 }, rules);
|
|
315
316
|
expect(result.code).toBe(0);
|
|
316
317
|
});
|
|
317
318
|
|
|
318
|
-
test(
|
|
319
|
-
const rules = { value: { name:
|
|
319
|
+
test("max 为 0 时不检查最大值", () => {
|
|
320
|
+
const rules = { value: { name: "值", type: "number", min: 0, max: 0 } };
|
|
320
321
|
const result = Validator.validate({ value: 999999 }, rules);
|
|
321
322
|
expect(result.code).toBe(0);
|
|
322
323
|
});
|
|
@@ -324,143 +325,147 @@ describe('Validator.validate - number 类型', () => {
|
|
|
324
325
|
|
|
325
326
|
// ========== array_string 类型测试 ==========
|
|
326
327
|
|
|
327
|
-
describe(
|
|
328
|
-
test(
|
|
329
|
-
const rules = { tags: { name:
|
|
330
|
-
const result = Validator.validate({ tags: [
|
|
328
|
+
describe("Validator.validate - array_string 类型", () => {
|
|
329
|
+
test("有效数组 - 通过", () => {
|
|
330
|
+
const rules = { tags: { name: "标签", type: "array_string", min: 1, max: 5 } };
|
|
331
|
+
const result = Validator.validate({ tags: ["a", "b", "c"] }, rules);
|
|
331
332
|
expect(result.code).toBe(0);
|
|
332
333
|
});
|
|
333
334
|
|
|
334
|
-
test(
|
|
335
|
-
const rules = { tags: { name:
|
|
336
|
-
const result = Validator.validate({ tags:
|
|
335
|
+
test("非数组类型 - 失败", () => {
|
|
336
|
+
const rules = { tags: { name: "标签", type: "array_string", min: 1, max: 5 } };
|
|
337
|
+
const result = Validator.validate({ tags: "not array" }, rules);
|
|
337
338
|
expect(result.code).toBe(1);
|
|
338
|
-
expect(result.firstError).toContain(
|
|
339
|
+
expect(result.firstError).toContain("数组");
|
|
339
340
|
});
|
|
340
341
|
|
|
341
|
-
test(
|
|
342
|
-
const rules = { tags: { name:
|
|
343
|
-
const result = Validator.validate({ tags: [
|
|
342
|
+
test("元素数量小于 min - 失败", () => {
|
|
343
|
+
const rules = { tags: { name: "标签", type: "array_string", min: 3, max: 10 } };
|
|
344
|
+
const result = Validator.validate({ tags: ["a", "b"] }, rules);
|
|
344
345
|
expect(result.code).toBe(1);
|
|
345
|
-
expect(result.firstError).toContain(
|
|
346
|
+
expect(result.firstError).toContain("3");
|
|
346
347
|
});
|
|
347
348
|
|
|
348
|
-
test(
|
|
349
|
-
const rules = { tags: { name:
|
|
350
|
-
const result = Validator.validate({ tags: [
|
|
349
|
+
test("元素数量等于 min - 通过", () => {
|
|
350
|
+
const rules = { tags: { name: "标签", type: "array_string", min: 2, max: 10 } };
|
|
351
|
+
const result = Validator.validate({ tags: ["a", "b"] }, rules);
|
|
351
352
|
expect(result.code).toBe(0);
|
|
352
353
|
});
|
|
353
354
|
|
|
354
|
-
test(
|
|
355
|
-
const rules = { tags: { name:
|
|
356
|
-
const result = Validator.validate({ tags: [
|
|
355
|
+
test("元素数量大于 max - 失败", () => {
|
|
356
|
+
const rules = { tags: { name: "标签", type: "array_string", min: 1, max: 3 } };
|
|
357
|
+
const result = Validator.validate({ tags: ["a", "b", "c", "d", "e"] }, rules);
|
|
357
358
|
expect(result.code).toBe(1);
|
|
358
|
-
expect(result.firstError).toContain(
|
|
359
|
+
expect(result.firstError).toContain("3");
|
|
359
360
|
});
|
|
360
361
|
|
|
361
|
-
test(
|
|
362
|
-
const rules = { tags: { name:
|
|
363
|
-
const result = Validator.validate({ tags: [
|
|
362
|
+
test("元素数量等于 max - 通过", () => {
|
|
363
|
+
const rules = { tags: { name: "标签", type: "array_string", min: 1, max: 3 } };
|
|
364
|
+
const result = Validator.validate({ tags: ["a", "b", "c"] }, rules);
|
|
364
365
|
expect(result.code).toBe(0);
|
|
365
366
|
});
|
|
366
367
|
|
|
367
|
-
test(
|
|
368
|
-
const rules = { tags: { name:
|
|
368
|
+
test("空数组", () => {
|
|
369
|
+
const rules = { tags: { name: "标签", type: "array_string", min: 0, max: 10 } };
|
|
369
370
|
const result = Validator.validate({ tags: [] }, rules);
|
|
370
371
|
expect(result.code).toBe(0);
|
|
371
372
|
});
|
|
372
373
|
|
|
373
|
-
test(
|
|
374
|
-
const rules = {
|
|
375
|
-
|
|
374
|
+
test("数组元素正则验证 - 全部通过", () => {
|
|
375
|
+
const rules = {
|
|
376
|
+
emails: { name: "邮箱列表", type: "array_string", min: 1, max: 10, regexp: "@email" }
|
|
377
|
+
};
|
|
378
|
+
const result = Validator.validate({ emails: ["a@test.com", "b@test.com"] }, rules);
|
|
376
379
|
expect(result.code).toBe(0);
|
|
377
380
|
});
|
|
378
381
|
|
|
379
|
-
test(
|
|
380
|
-
const rules = {
|
|
381
|
-
|
|
382
|
+
test("数组元素正则验证 - 部分失败", () => {
|
|
383
|
+
const rules = {
|
|
384
|
+
emails: { name: "邮箱列表", type: "array_string", min: 1, max: 10, regexp: "@email" }
|
|
385
|
+
};
|
|
386
|
+
const result = Validator.validate({ emails: ["a@test.com", "invalid"] }, rules);
|
|
382
387
|
expect(result.code).toBe(1);
|
|
383
|
-
expect(result.firstError).toContain(
|
|
388
|
+
expect(result.firstError).toContain("格式");
|
|
384
389
|
});
|
|
385
390
|
});
|
|
386
391
|
|
|
387
392
|
// ========== array_text 类型测试 ==========
|
|
388
393
|
|
|
389
|
-
describe(
|
|
390
|
-
test(
|
|
391
|
-
const rules = { items: { name:
|
|
392
|
-
const result = Validator.validate({ items: [
|
|
394
|
+
describe("Validator.validate - array_text 类型", () => {
|
|
395
|
+
test("array_text 与 array_string 行为一致", () => {
|
|
396
|
+
const rules = { items: { name: "条目", type: "array_text", min: 1, max: 5 } };
|
|
397
|
+
const result = Validator.validate({ items: ["item1", "item2"] }, rules);
|
|
393
398
|
expect(result.code).toBe(0);
|
|
394
399
|
});
|
|
395
400
|
});
|
|
396
401
|
|
|
397
402
|
// ========== 正则验证测试 ==========
|
|
398
403
|
|
|
399
|
-
describe(
|
|
400
|
-
test(
|
|
401
|
-
const validEmails = [
|
|
404
|
+
describe("Validator.validate - 正则别名", () => {
|
|
405
|
+
test("@email - 有效邮箱", () => {
|
|
406
|
+
const validEmails = ["test@example.com", "user.name@domain.co.uk", "admin+tag@site.org", "test123@test.cn"];
|
|
402
407
|
|
|
403
408
|
validEmails.forEach((email) => {
|
|
404
|
-
const rules = { email: { name:
|
|
409
|
+
const rules = { email: { name: "邮箱", type: "string", regexp: "@email" } };
|
|
405
410
|
const result = Validator.validate({ email: email }, rules);
|
|
406
411
|
expect(result.code).toBe(0);
|
|
407
412
|
});
|
|
408
413
|
});
|
|
409
414
|
|
|
410
|
-
test(
|
|
411
|
-
const invalidEmails = [
|
|
415
|
+
test("@email - 无效邮箱", () => {
|
|
416
|
+
const invalidEmails = ["plaintext", "@example.com", "user@", "user @domain.com"];
|
|
412
417
|
|
|
413
418
|
invalidEmails.forEach((email) => {
|
|
414
|
-
const rules = { email: { name:
|
|
419
|
+
const rules = { email: { name: "邮箱", type: "string", regexp: "@email" } };
|
|
415
420
|
const result = Validator.validate({ email: email }, rules);
|
|
416
421
|
expect(result.code).toBe(1);
|
|
417
422
|
});
|
|
418
423
|
});
|
|
419
424
|
|
|
420
|
-
test(
|
|
421
|
-
const validPhones = [
|
|
425
|
+
test("@phone - 有效手机号", () => {
|
|
426
|
+
const validPhones = ["13800138000", "15012345678", "18888888888", "19912345678"];
|
|
422
427
|
|
|
423
428
|
validPhones.forEach((phone) => {
|
|
424
|
-
const rules = { phone: { name:
|
|
429
|
+
const rules = { phone: { name: "手机号", type: "string", regexp: "@phone" } };
|
|
425
430
|
const result = Validator.validate({ phone: phone }, rules);
|
|
426
431
|
expect(result.code).toBe(0);
|
|
427
432
|
});
|
|
428
433
|
});
|
|
429
434
|
|
|
430
|
-
test(
|
|
435
|
+
test("@phone - 无效手机号", () => {
|
|
431
436
|
const invalidPhones = [
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
437
|
+
"12345678901", // 首位不是 1 开头的有效段
|
|
438
|
+
"1380013800", // 10 位
|
|
439
|
+
"138001380001", // 12 位
|
|
440
|
+
"abc"
|
|
436
441
|
];
|
|
437
442
|
|
|
438
443
|
invalidPhones.forEach((phone) => {
|
|
439
|
-
const rules = { phone: { name:
|
|
444
|
+
const rules = { phone: { name: "手机号", type: "string", regexp: "@phone" } };
|
|
440
445
|
const result = Validator.validate({ phone: phone }, rules);
|
|
441
446
|
expect(result.code).toBe(1);
|
|
442
447
|
});
|
|
443
448
|
});
|
|
444
449
|
});
|
|
445
450
|
|
|
446
|
-
describe(
|
|
447
|
-
test(
|
|
448
|
-
const rules = { code: { name:
|
|
451
|
+
describe("Validator.validate - 自定义正则", () => {
|
|
452
|
+
test("纯数字", () => {
|
|
453
|
+
const rules = { code: { name: "验证码", type: "string", regexp: "^\\d+$" } };
|
|
449
454
|
|
|
450
|
-
expect(Validator.validate({ code:
|
|
451
|
-
expect(Validator.validate({ code:
|
|
455
|
+
expect(Validator.validate({ code: "123456" }, rules).code).toBe(0);
|
|
456
|
+
expect(Validator.validate({ code: "12a34" }, rules).code).toBe(1);
|
|
452
457
|
});
|
|
453
458
|
|
|
454
|
-
test(
|
|
455
|
-
const rules = { username: { name:
|
|
459
|
+
test("字母数字组合", () => {
|
|
460
|
+
const rules = { username: { name: "用户名", type: "string", regexp: "^[a-zA-Z0-9]+$" } };
|
|
456
461
|
|
|
457
|
-
expect(Validator.validate({ username:
|
|
458
|
-
expect(Validator.validate({ username:
|
|
462
|
+
expect(Validator.validate({ username: "user123" }, rules).code).toBe(0);
|
|
463
|
+
expect(Validator.validate({ username: "user@123" }, rules).code).toBe(1);
|
|
459
464
|
});
|
|
460
465
|
|
|
461
|
-
test(
|
|
462
|
-
const rules = { value: { name:
|
|
463
|
-
const result = Validator.validate({ value:
|
|
466
|
+
test("无效正则不抛出异常", () => {
|
|
467
|
+
const rules = { value: { name: "值", type: "string", regexp: "[invalid(" } };
|
|
468
|
+
const result = Validator.validate({ value: "test" }, rules);
|
|
464
469
|
// 无效正则应该导致验证失败,但不应抛出异常
|
|
465
470
|
expect(result.code).toBe(1);
|
|
466
471
|
});
|
|
@@ -468,27 +473,27 @@ describe('Validator.validate - 自定义正则', () => {
|
|
|
468
473
|
|
|
469
474
|
// ========== 非必填字段测试 ==========
|
|
470
475
|
|
|
471
|
-
describe(
|
|
472
|
-
test(
|
|
476
|
+
describe("Validator.validate - 非必填字段", () => {
|
|
477
|
+
test("非必填字段不存在时不验证", () => {
|
|
473
478
|
const rules = {
|
|
474
|
-
name: { name:
|
|
475
|
-
age: { name:
|
|
479
|
+
name: { name: "名称", type: "string", min: 2, max: 20 },
|
|
480
|
+
age: { name: "年龄", type: "number", min: 0, max: 150 }
|
|
476
481
|
};
|
|
477
482
|
|
|
478
483
|
// 只提供 name,不提供 age(age 不在 required 中)
|
|
479
|
-
const result = Validator.validate({ name:
|
|
484
|
+
const result = Validator.validate({ name: "test" }, rules, ["name"]);
|
|
480
485
|
|
|
481
486
|
expect(result.code).toBe(0);
|
|
482
487
|
});
|
|
483
488
|
|
|
484
|
-
test(
|
|
489
|
+
test("非必填字段存在时会验证", () => {
|
|
485
490
|
const rules = {
|
|
486
|
-
name: { name:
|
|
487
|
-
age: { name:
|
|
491
|
+
name: { name: "名称", type: "string", min: 2, max: 20 },
|
|
492
|
+
age: { name: "年龄", type: "number", min: 0, max: 150 }
|
|
488
493
|
};
|
|
489
494
|
|
|
490
495
|
// 提供了 age 但值无效
|
|
491
|
-
const result = Validator.validate({ name:
|
|
496
|
+
const result = Validator.validate({ name: "test", age: 200 }, rules, ["name"]);
|
|
492
497
|
|
|
493
498
|
expect(result.code).toBe(1);
|
|
494
499
|
expect(result.fieldErrors.age).toBeDefined();
|
|
@@ -497,178 +502,178 @@ describe('Validator.validate - 非必填字段', () => {
|
|
|
497
502
|
|
|
498
503
|
// ========== Validator.single 测试 ==========
|
|
499
504
|
|
|
500
|
-
describe(
|
|
501
|
-
test(
|
|
502
|
-
const fieldDef = { name:
|
|
505
|
+
describe("Validator.single - 单值验证", () => {
|
|
506
|
+
test("有效值返回转换后的值", () => {
|
|
507
|
+
const fieldDef = { name: "年龄", type: "number", min: 0, max: 150 };
|
|
503
508
|
const result = Validator.single(25, fieldDef);
|
|
504
509
|
|
|
505
510
|
expect(result.error).toBeNull();
|
|
506
511
|
expect(result.value).toBe(25);
|
|
507
512
|
});
|
|
508
513
|
|
|
509
|
-
test(
|
|
510
|
-
const fieldDef = { name:
|
|
511
|
-
const result = Validator.single(
|
|
514
|
+
test("字符串数字自动转换", () => {
|
|
515
|
+
const fieldDef = { name: "年龄", type: "number", min: 0, max: 150 };
|
|
516
|
+
const result = Validator.single("30", fieldDef);
|
|
512
517
|
|
|
513
518
|
expect(result.error).toBeNull();
|
|
514
519
|
expect(result.value).toBe(30);
|
|
515
520
|
});
|
|
516
521
|
|
|
517
|
-
test(
|
|
518
|
-
const fieldDef = { name:
|
|
519
|
-
const result = Validator.single(
|
|
522
|
+
test("无效值返回错误", () => {
|
|
523
|
+
const fieldDef = { name: "年龄", type: "number", min: 0, max: 150 };
|
|
524
|
+
const result = Validator.single("abc", fieldDef);
|
|
520
525
|
|
|
521
526
|
expect(result.error).toBeDefined();
|
|
522
527
|
expect(result.value).toBeNull();
|
|
523
528
|
});
|
|
524
529
|
|
|
525
|
-
test(
|
|
526
|
-
const fieldDef = { name:
|
|
530
|
+
test("空值返回默认值 - number", () => {
|
|
531
|
+
const fieldDef = { name: "计数", type: "number", default: null };
|
|
527
532
|
const result = Validator.single(null, fieldDef);
|
|
528
533
|
|
|
529
534
|
expect(result.error).toBeNull();
|
|
530
535
|
expect(result.value).toBe(0); // number 类型默认值
|
|
531
536
|
});
|
|
532
537
|
|
|
533
|
-
test(
|
|
534
|
-
const fieldDef = { name:
|
|
538
|
+
test("空值返回默认值 - string", () => {
|
|
539
|
+
const fieldDef = { name: "名称", type: "string", default: null };
|
|
535
540
|
const result = Validator.single(undefined, fieldDef);
|
|
536
541
|
|
|
537
542
|
expect(result.error).toBeNull();
|
|
538
|
-
expect(result.value).toBe(
|
|
543
|
+
expect(result.value).toBe(""); // string 类型默认值
|
|
539
544
|
});
|
|
540
545
|
|
|
541
|
-
test(
|
|
542
|
-
const fieldDef = { name:
|
|
543
|
-
const result = Validator.single(
|
|
546
|
+
test("空值返回默认值 - array_string", () => {
|
|
547
|
+
const fieldDef = { name: "标签", type: "array_string", default: null };
|
|
548
|
+
const result = Validator.single("", fieldDef);
|
|
544
549
|
|
|
545
550
|
expect(result.error).toBeNull();
|
|
546
551
|
expect(result.value).toEqual([]); // array 类型默认值
|
|
547
552
|
});
|
|
548
553
|
|
|
549
|
-
test(
|
|
550
|
-
const fieldDef = { name:
|
|
554
|
+
test("使用自定义默认值", () => {
|
|
555
|
+
const fieldDef = { name: "状态", type: "number", default: 1 };
|
|
551
556
|
const result = Validator.single(null, fieldDef);
|
|
552
557
|
|
|
553
558
|
expect(result.error).toBeNull();
|
|
554
559
|
expect(result.value).toBe(1);
|
|
555
560
|
});
|
|
556
561
|
|
|
557
|
-
test(
|
|
558
|
-
const fieldDef = { name:
|
|
562
|
+
test("字符串数字默认值转换", () => {
|
|
563
|
+
const fieldDef = { name: "计数", type: "number", default: "100" };
|
|
559
564
|
const result = Validator.single(null, fieldDef);
|
|
560
565
|
|
|
561
566
|
expect(result.error).toBeNull();
|
|
562
567
|
expect(result.value).toBe(100);
|
|
563
568
|
});
|
|
564
569
|
|
|
565
|
-
test(
|
|
566
|
-
const fieldDef = { name:
|
|
570
|
+
test("数组默认值解析", () => {
|
|
571
|
+
const fieldDef = { name: "标签", type: "array_string", default: '["a","b"]' };
|
|
567
572
|
const result = Validator.single(null, fieldDef);
|
|
568
573
|
|
|
569
574
|
expect(result.error).toBeNull();
|
|
570
|
-
expect(result.value).toEqual([
|
|
575
|
+
expect(result.value).toEqual(["a", "b"]);
|
|
571
576
|
});
|
|
572
577
|
|
|
573
|
-
test(
|
|
574
|
-
const fieldDef = { name:
|
|
578
|
+
test("空数组默认值", () => {
|
|
579
|
+
const fieldDef = { name: "标签", type: "array_string", default: "[]" };
|
|
575
580
|
const result = Validator.single(null, fieldDef);
|
|
576
581
|
|
|
577
582
|
expect(result.error).toBeNull();
|
|
578
583
|
expect(result.value).toEqual([]);
|
|
579
584
|
});
|
|
580
585
|
|
|
581
|
-
test(
|
|
582
|
-
const fieldDef = { name:
|
|
586
|
+
test("规则验证失败", () => {
|
|
587
|
+
const fieldDef = { name: "年龄", type: "number", min: 18, max: 100 };
|
|
583
588
|
const result = Validator.single(10, fieldDef);
|
|
584
589
|
|
|
585
590
|
expect(result.error).toBeDefined();
|
|
586
|
-
expect(result.error).toContain(
|
|
591
|
+
expect(result.error).toContain("18");
|
|
587
592
|
});
|
|
588
593
|
|
|
589
|
-
test(
|
|
590
|
-
const fieldDef = { name:
|
|
591
|
-
const result = Validator.single(
|
|
594
|
+
test("正则验证失败", () => {
|
|
595
|
+
const fieldDef = { name: "邮箱", type: "string", regexp: "@email" };
|
|
596
|
+
const result = Validator.single("invalid", fieldDef);
|
|
592
597
|
|
|
593
598
|
expect(result.error).toBeDefined();
|
|
594
|
-
expect(result.error).toContain(
|
|
599
|
+
expect(result.error).toContain("格式");
|
|
595
600
|
});
|
|
596
601
|
});
|
|
597
602
|
|
|
598
603
|
// ========== 错误消息测试 ==========
|
|
599
604
|
|
|
600
|
-
describe(
|
|
601
|
-
test(
|
|
602
|
-
const rules = { age: { name:
|
|
605
|
+
describe("Validator - 错误消息格式", () => {
|
|
606
|
+
test("错误消息包含字段标签", () => {
|
|
607
|
+
const rules = { age: { name: "年龄", type: "number", min: 18, max: 100 } };
|
|
603
608
|
const result = Validator.validate({ age: 10 }, rules);
|
|
604
609
|
|
|
605
|
-
expect(result.firstError).toContain(
|
|
610
|
+
expect(result.firstError).toContain("年龄");
|
|
606
611
|
});
|
|
607
612
|
|
|
608
|
-
test(
|
|
609
|
-
const rules = { name: { name:
|
|
610
|
-
const result = Validator.validate({}, rules, [
|
|
613
|
+
test("必填错误消息格式", () => {
|
|
614
|
+
const rules = { name: { name: "姓名", type: "string", min: 2, max: 20 } };
|
|
615
|
+
const result = Validator.validate({}, rules, ["name"]);
|
|
611
616
|
|
|
612
|
-
expect(result.firstError).toBe(
|
|
617
|
+
expect(result.firstError).toBe("姓名为必填项");
|
|
613
618
|
});
|
|
614
619
|
|
|
615
|
-
test(
|
|
616
|
-
const rules = { name: { name:
|
|
617
|
-
const result = Validator.validate({ name:
|
|
620
|
+
test("长度错误消息格式", () => {
|
|
621
|
+
const rules = { name: { name: "姓名", type: "string", min: 5, max: 20 } };
|
|
622
|
+
const result = Validator.validate({ name: "abc" }, rules);
|
|
618
623
|
|
|
619
|
-
expect(result.firstError).toContain(
|
|
620
|
-
expect(result.firstError).toContain(
|
|
624
|
+
expect(result.firstError).toContain("姓名");
|
|
625
|
+
expect(result.firstError).toContain("5");
|
|
621
626
|
});
|
|
622
627
|
|
|
623
|
-
test(
|
|
624
|
-
const rules = { age: { name:
|
|
628
|
+
test("数值范围错误消息格式", () => {
|
|
629
|
+
const rules = { age: { name: "年龄", type: "number", min: 18, max: 100 } };
|
|
625
630
|
const result = Validator.validate({ age: 10 }, rules);
|
|
626
631
|
|
|
627
|
-
expect(result.firstError).toContain(
|
|
628
|
-
expect(result.firstError).toContain(
|
|
632
|
+
expect(result.firstError).toContain("年龄");
|
|
633
|
+
expect(result.firstError).toContain("18");
|
|
629
634
|
});
|
|
630
635
|
});
|
|
631
636
|
|
|
632
637
|
// ========== 边界条件测试 ==========
|
|
633
638
|
|
|
634
|
-
describe(
|
|
635
|
-
test(
|
|
636
|
-
const result = Validator.validate({ name:
|
|
639
|
+
describe("Validator - 边界条件", () => {
|
|
640
|
+
test("空 rules 对象", () => {
|
|
641
|
+
const result = Validator.validate({ name: "test" }, {});
|
|
637
642
|
expect(result.code).toBe(0);
|
|
638
643
|
});
|
|
639
644
|
|
|
640
|
-
test(
|
|
641
|
-
const rules = { name: { name:
|
|
642
|
-
const result = Validator.validate({ name:
|
|
645
|
+
test("空 required 数组", () => {
|
|
646
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 20 } };
|
|
647
|
+
const result = Validator.validate({ name: "test" }, rules, []);
|
|
643
648
|
expect(result.code).toBe(0);
|
|
644
649
|
});
|
|
645
650
|
|
|
646
|
-
test(
|
|
647
|
-
const rules = { name: { name:
|
|
648
|
-
const result = Validator.validate({}, rules, [
|
|
651
|
+
test("required 中的字段不在 rules 中", () => {
|
|
652
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 20 } };
|
|
653
|
+
const result = Validator.validate({}, rules, ["unknown"]);
|
|
649
654
|
|
|
650
655
|
// 应该报错,显示 unknown 为必填项
|
|
651
656
|
expect(result.code).toBe(1);
|
|
652
657
|
expect(result.fieldErrors.unknown).toBeDefined();
|
|
653
658
|
});
|
|
654
659
|
|
|
655
|
-
test(
|
|
656
|
-
const rules = { name: { name:
|
|
657
|
-
const result = Validator.validate({ name:
|
|
660
|
+
test("data 中的字段不在 rules 中", () => {
|
|
661
|
+
const rules = { name: { name: "名称", type: "string", min: 2, max: 20 } };
|
|
662
|
+
const result = Validator.validate({ name: "test", extra: "ignored" }, rules);
|
|
658
663
|
|
|
659
664
|
// extra 字段应该被忽略
|
|
660
665
|
expect(result.code).toBe(0);
|
|
661
666
|
});
|
|
662
667
|
|
|
663
|
-
test(
|
|
664
|
-
const rules = { value: { name:
|
|
665
|
-
const result = Validator.validate({ value:
|
|
668
|
+
test("min/max 为 null 时不检查", () => {
|
|
669
|
+
const rules = { value: { name: "值", type: "string", min: null, max: null } };
|
|
670
|
+
const result = Validator.validate({ value: "any length string here" }, rules);
|
|
666
671
|
expect(result.code).toBe(0);
|
|
667
672
|
});
|
|
668
673
|
|
|
669
|
-
test(
|
|
670
|
-
const rules = { value: { name:
|
|
671
|
-
const result = Validator.validate({ value:
|
|
674
|
+
test("regexp 为 null 时不检查正则", () => {
|
|
675
|
+
const rules = { value: { name: "值", type: "string", min: 0, max: 100, regexp: null } };
|
|
676
|
+
const result = Validator.validate({ value: "anything" }, rules);
|
|
672
677
|
expect(result.code).toBe(0);
|
|
673
678
|
});
|
|
674
679
|
});
|