befly 3.10.1 → 3.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitignore +0 -0
- package/configs/presetFields.ts +10 -0
- package/configs/presetRegexp.ts +225 -0
- package/package.json +15 -16
- package/tests/_mocks/mockSqliteDb.ts +0 -204
- package/tests/addonHelper-cache.test.ts +0 -32
- package/tests/api-integration-array-number.test.ts +0 -282
- package/tests/apiHandler-routePath-only.test.ts +0 -32
- package/tests/befly-config-env.test.ts +0 -78
- package/tests/cacheHelper.test.ts +0 -323
- package/tests/cacheKeys.test.ts +0 -41
- package/tests/checkApi-routePath-strict.test.ts +0 -166
- package/tests/checkMenu.test.ts +0 -346
- package/tests/checkTable-smoke.test.ts +0 -157
- package/tests/cipher.test.ts +0 -249
- package/tests/dbDialect-cache.test.ts +0 -23
- package/tests/dbDialect.test.ts +0 -46
- package/tests/dbHelper-advanced.test.ts +0 -723
- package/tests/dbHelper-all-array-types.test.ts +0 -316
- package/tests/dbHelper-array-serialization.test.ts +0 -258
- package/tests/dbHelper-batch-write.test.ts +0 -90
- package/tests/dbHelper-columns.test.ts +0 -234
- package/tests/dbHelper-execute.test.ts +0 -187
- package/tests/dbHelper-joins.test.ts +0 -221
- package/tests/fields-redis-cache.test.ts +0 -127
- package/tests/fields-validate.test.ts +0 -99
- package/tests/fixtures/scanFilesAddon/node_modules/@befly-addon/demo/apis/sub/b.ts +0 -3
- package/tests/fixtures/scanFilesApis/a.ts +0 -3
- package/tests/fixtures/scanFilesApis/sub/b.ts +0 -3
- package/tests/getClientIp.test.ts +0 -54
- package/tests/integration.test.ts +0 -189
- package/tests/jwt.test.ts +0 -65
- package/tests/loadPlugins-order-smoke.test.ts +0 -75
- package/tests/logger.test.ts +0 -325
- package/tests/redisHelper.test.ts +0 -495
- package/tests/redisKeys.test.ts +0 -9
- package/tests/scanConfig.test.ts +0 -144
- package/tests/scanFiles-routePath.test.ts +0 -46
- package/tests/smoke-sql.test.ts +0 -24
- package/tests/sqlBuilder-advanced.test.ts +0 -608
- package/tests/sqlBuilder.test.ts +0 -209
- package/tests/sync-connection.test.ts +0 -183
- package/tests/sync-init-guard.test.ts +0 -105
- package/tests/syncApi-insBatch-fields-consistent.test.ts +0 -61
- package/tests/syncApi-obsolete-records.test.ts +0 -69
- package/tests/syncApi-type-compat.test.ts +0 -72
- package/tests/syncDev-permissions.test.ts +0 -81
- package/tests/syncMenu-disableMenus-hard-delete.test.ts +0 -88
- package/tests/syncMenu-duplicate-path.test.ts +0 -122
- package/tests/syncMenu-obsolete-records.test.ts +0 -161
- package/tests/syncMenu-parentPath-from-tree.test.ts +0 -75
- package/tests/syncMenu-paths.test.ts +0 -59
- package/tests/syncTable-apply.test.ts +0 -279
- package/tests/syncTable-array-number.test.ts +0 -160
- package/tests/syncTable-constants.test.ts +0 -101
- package/tests/syncTable-db-integration.test.ts +0 -237
- package/tests/syncTable-ddl.test.ts +0 -245
- package/tests/syncTable-helpers.test.ts +0 -99
- package/tests/syncTable-schema.test.ts +0 -99
- package/tests/syncTable-testkit.test.ts +0 -25
- package/tests/syncTable-types.test.ts +0 -122
- package/tests/tableRef-and-deserialize.test.ts +0 -67
- package/tests/util.test.ts +0 -100
- package/tests/validator-array-number.test.ts +0 -310
- package/tests/validator-default.test.ts +0 -373
- package/tests/validator.test.ts +0 -679
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { syncTable } from "../sync/syncTable.js";
|
|
4
|
-
|
|
5
|
-
describe("syncTable - TestKit 挂载", () => {
|
|
6
|
-
it("should expose TestKit as a stable object", () => {
|
|
7
|
-
expect(typeof syncTable).toBe("function");
|
|
8
|
-
expect(syncTable.TestKit).toBeDefined();
|
|
9
|
-
|
|
10
|
-
const descriptor = Object.getOwnPropertyDescriptor(syncTable, "TestKit");
|
|
11
|
-
expect(descriptor).toBeDefined();
|
|
12
|
-
expect(descriptor?.writable).toBe(false);
|
|
13
|
-
expect(descriptor?.configurable).toBe(false);
|
|
14
|
-
expect(descriptor?.enumerable).toBe(true);
|
|
15
|
-
|
|
16
|
-
expect(typeof syncTable.TestKit.quoteIdentifier).toBe("function");
|
|
17
|
-
expect(typeof syncTable.TestKit.getTypeMapping).toBe("function");
|
|
18
|
-
|
|
19
|
-
expect(syncTable.TestKit.DB_VERSION_REQUIREMENTS.MYSQL_MIN_MAJOR).toBe(8);
|
|
20
|
-
|
|
21
|
-
const firstRef = syncTable.TestKit;
|
|
22
|
-
const secondRef = syncTable.TestKit;
|
|
23
|
-
expect(firstRef).toBe(secondRef);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* syncTable 类型处理模块测试
|
|
3
|
-
*
|
|
4
|
-
* 测试 types.ts 中的函数:
|
|
5
|
-
* - isStringOrArrayType
|
|
6
|
-
* - getSqlType
|
|
7
|
-
* - resolveDefaultValue
|
|
8
|
-
* - generateDefaultSql
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, test, expect } from "bun:test";
|
|
12
|
-
|
|
13
|
-
import { syncTable } from "../sync/syncTable.js";
|
|
14
|
-
|
|
15
|
-
describe("isStringOrArrayType", () => {
|
|
16
|
-
test("string 类型返回 true", () => {
|
|
17
|
-
expect(syncTable.TestKit.isStringOrArrayType("string")).toBe(true);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test("array_string 类型返回 true", () => {
|
|
21
|
-
expect(syncTable.TestKit.isStringOrArrayType("array_string")).toBe(true);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test("number 类型返回 false", () => {
|
|
25
|
-
expect(syncTable.TestKit.isStringOrArrayType("number")).toBe(false);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test("text 类型返回 false", () => {
|
|
29
|
-
expect(syncTable.TestKit.isStringOrArrayType("text")).toBe(false);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("array_text 类型返回 false", () => {
|
|
33
|
-
expect(syncTable.TestKit.isStringOrArrayType("array_text")).toBe(false);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe("resolveDefaultValue", () => {
|
|
38
|
-
test("null 值 + string 类型 => 空字符串", () => {
|
|
39
|
-
expect(syncTable.TestKit.resolveDefaultValue(null, "string")).toBe("");
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test("null 值 + number 类型 => 0", () => {
|
|
43
|
-
expect(syncTable.TestKit.resolveDefaultValue(null, "number")).toBe(0);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('"null" 字符串 + number 类型 => 0', () => {
|
|
47
|
-
expect(syncTable.TestKit.resolveDefaultValue("null", "number")).toBe(0);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('null 值 + array_string 类型 => "[]"', () => {
|
|
51
|
-
expect(syncTable.TestKit.resolveDefaultValue(null, "array_string")).toBe("[]");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('null 值 + text 类型 => "null"', () => {
|
|
55
|
-
expect(syncTable.TestKit.resolveDefaultValue(null, "text")).toBe("null");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('null 值 + array_text 类型 => "null"(TEXT 不支持默认值)', () => {
|
|
59
|
-
expect(syncTable.TestKit.resolveDefaultValue(null, "array_text")).toBe("null");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("有实际值时直接返回", () => {
|
|
63
|
-
expect(syncTable.TestKit.resolveDefaultValue("admin", "string")).toBe("admin");
|
|
64
|
-
expect(syncTable.TestKit.resolveDefaultValue(100, "number")).toBe(100);
|
|
65
|
-
expect(syncTable.TestKit.resolveDefaultValue(0, "number")).toBe(0);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe("generateDefaultSql", () => {
|
|
70
|
-
test("number 类型生成数字默认值", () => {
|
|
71
|
-
expect(syncTable.TestKit.generateDefaultSql(0, "number")).toBe(" DEFAULT 0");
|
|
72
|
-
expect(syncTable.TestKit.generateDefaultSql(100, "number")).toBe(" DEFAULT 100");
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("string 类型生成带引号默认值", () => {
|
|
76
|
-
expect(syncTable.TestKit.generateDefaultSql("admin", "string")).toBe(" DEFAULT 'admin'");
|
|
77
|
-
expect(syncTable.TestKit.generateDefaultSql("", "string")).toBe(" DEFAULT ''");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("text 类型不生成默认值", () => {
|
|
81
|
-
expect(syncTable.TestKit.generateDefaultSql("null", "text")).toBe("");
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("array_string 类型生成 JSON 数组默认值", () => {
|
|
85
|
-
expect(syncTable.TestKit.generateDefaultSql("[]", "array_string")).toBe(" DEFAULT '[]'");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("array_text 类型不生成默认值(MySQL TEXT 不支持)", () => {
|
|
89
|
-
expect(syncTable.TestKit.generateDefaultSql("[]", "array_text")).toBe("");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("单引号被正确转义", () => {
|
|
93
|
-
expect(syncTable.TestKit.generateDefaultSql("it's", "string")).toBe(" DEFAULT 'it''s'");
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe("getSqlType", () => {
|
|
98
|
-
test("string 类型带长度", () => {
|
|
99
|
-
const result = syncTable.TestKit.getSqlType("mysql", "string", 100);
|
|
100
|
-
expect(result).toBe("VARCHAR(100)");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test("array_string 类型带长度", () => {
|
|
104
|
-
const result = syncTable.TestKit.getSqlType("mysql", "array_string", 500);
|
|
105
|
-
expect(result).toBe("VARCHAR(500)");
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("number 类型无符号", () => {
|
|
109
|
-
const result = syncTable.TestKit.getSqlType("mysql", "number", null, true);
|
|
110
|
-
expect(result).toBe("BIGINT UNSIGNED");
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test("number 类型有符号", () => {
|
|
114
|
-
const result = syncTable.TestKit.getSqlType("mysql", "number", null, false);
|
|
115
|
-
expect(result).toBe("BIGINT");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test("text 类型", () => {
|
|
119
|
-
const result = syncTable.TestKit.getSqlType("mysql", "text", null);
|
|
120
|
-
expect(result).toBe("MEDIUMTEXT");
|
|
121
|
-
});
|
|
122
|
-
});
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, mock } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { MySqlDialect } from "../lib/dbDialect.js";
|
|
4
|
-
import { DbHelper } from "../lib/dbHelper.js";
|
|
5
|
-
import { DbUtils } from "../lib/dbUtils.js";
|
|
6
|
-
import { SqlBuilder } from "../lib/sqlBuilder.js";
|
|
7
|
-
|
|
8
|
-
describe("tableRef normalize + escape", () => {
|
|
9
|
-
it("DbUtils.normalizeTableRef: 保留 alias,仅 snakeCase 表名", () => {
|
|
10
|
-
expect(DbUtils.normalizeTableRef("UserProfile up")).toBe("user_profile up");
|
|
11
|
-
expect(DbUtils.normalizeTableRef("order o")).toBe("order o");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("SqlBuilder.from: 支持 table alias", () => {
|
|
15
|
-
const sql = new SqlBuilder().select(["*"]).from("order o").toSelectSql().sql;
|
|
16
|
-
expect(sql).toContain("FROM `order` o");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("SqlBuilder.from: 支持 schema.table alias", () => {
|
|
20
|
-
const sql = new SqlBuilder().select(["*"]).from("my_db.users u").toSelectSql().sql;
|
|
21
|
-
expect(sql).toContain("FROM `my_db`.`users` u");
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("SqlBuilder.from: 复杂表引用要求显式 fromRaw", () => {
|
|
25
|
-
expect(() => new SqlBuilder().select(["*"]).from("users u FORCE INDEX (idx_user)")).toThrow();
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
describe("DbHelper.getList deserialize", () => {
|
|
30
|
-
it("getList: 数组字段应被 DbUtils.deserializeArrayFields 反序列化", async () => {
|
|
31
|
-
const sqlMock = {
|
|
32
|
-
unsafe: mock(async (sql: string, _params?: any[]) => {
|
|
33
|
-
if (sql.includes("COUNT(*) as total")) {
|
|
34
|
-
return [{ total: 1 }];
|
|
35
|
-
}
|
|
36
|
-
return [
|
|
37
|
-
{
|
|
38
|
-
id: "1",
|
|
39
|
-
tags: '["a","b"]',
|
|
40
|
-
state: 1,
|
|
41
|
-
created_at: 0,
|
|
42
|
-
updated_at: 0
|
|
43
|
-
}
|
|
44
|
-
];
|
|
45
|
-
})
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const redisMock = {
|
|
49
|
-
getObject: mock(async () => null),
|
|
50
|
-
setObject: mock(async () => "OK"),
|
|
51
|
-
genTimeID: mock(async () => 1)
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const dbHelper = new DbHelper({ redis: redisMock as any, sql: sqlMock as any, dialect: new MySqlDialect() });
|
|
55
|
-
|
|
56
|
-
const result = await dbHelper.getList<{ id: number; tags: string[] }>({
|
|
57
|
-
table: "users",
|
|
58
|
-
fields: ["id", "tags"],
|
|
59
|
-
where: {}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
expect(result.total).toBe(1);
|
|
63
|
-
expect(result.lists.length).toBe(1);
|
|
64
|
-
expect(Array.isArray(result.lists[0].tags)).toBe(true);
|
|
65
|
-
expect(result.lists[0].tags).toEqual(["a", "b"]);
|
|
66
|
-
});
|
|
67
|
-
});
|
package/tests/util.test.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { arrayKeysToCamel } from "../utils/arrayKeysToCamel.js";
|
|
4
|
-
import { calcPerfTime } from "../utils/calcPerfTime.js";
|
|
5
|
-
import { fieldClear } from "../utils/fieldClear.js";
|
|
6
|
-
import { keysToCamel } from "../utils/keysToCamel.js";
|
|
7
|
-
import { keysToSnake } from "../utils/keysToSnake.js";
|
|
8
|
-
|
|
9
|
-
describe("Util - keysToCamel", () => {
|
|
10
|
-
test("转换对象键名为驼峰", () => {
|
|
11
|
-
const result = keysToCamel({ user_name: "John", user_id: 123 });
|
|
12
|
-
expect(result.userName).toBe("John");
|
|
13
|
-
expect(result.userId).toBe(123);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test("保持已有驼峰格式", () => {
|
|
17
|
-
const result = keysToCamel({ userName: "John", userId: 123 });
|
|
18
|
-
expect(result.userName).toBe("John");
|
|
19
|
-
expect(result.userId).toBe(123);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("处理空对象", () => {
|
|
23
|
-
const result = keysToCamel({});
|
|
24
|
-
expect(Object.keys(result).length).toBe(0);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("处理嵌套对象", () => {
|
|
28
|
-
const result = keysToCamel({ user_info: { first_name: "John" } });
|
|
29
|
-
expect(result.userInfo).toBeDefined();
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe("Util - keysToSnake", () => {
|
|
34
|
-
test("转换对象键名为下划线", () => {
|
|
35
|
-
const result = keysToSnake({ userName: "John", userId: 123 });
|
|
36
|
-
expect(result.user_name).toBe("John");
|
|
37
|
-
expect(result.user_id).toBe(123);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("保持已有下划线格式", () => {
|
|
41
|
-
const result = keysToSnake({ user_name: "John", user_id: 123 });
|
|
42
|
-
expect(result.user_name).toBe("John");
|
|
43
|
-
expect(result.user_id).toBe(123);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("处理空对象", () => {
|
|
47
|
-
const result = keysToSnake({});
|
|
48
|
-
expect(Object.keys(result).length).toBe(0);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
describe("Util - arrayKeysToCamel", () => {
|
|
53
|
-
test("转换数组中对象键名为驼峰", () => {
|
|
54
|
-
const result = arrayKeysToCamel([
|
|
55
|
-
{ user_name: "John", user_id: 1 },
|
|
56
|
-
{ user_name: "Jane", user_id: 2 }
|
|
57
|
-
]);
|
|
58
|
-
expect(result[0].userName).toBe("John");
|
|
59
|
-
expect(result[0].userId).toBe(1);
|
|
60
|
-
expect(result[1].userName).toBe("Jane");
|
|
61
|
-
expect(result[1].userId).toBe(2);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("处理空数组", () => {
|
|
65
|
-
const result = arrayKeysToCamel([]);
|
|
66
|
-
expect(result.length).toBe(0);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe("Util - fieldClear", () => {
|
|
71
|
-
test("移除 null 和 undefined", () => {
|
|
72
|
-
const result = fieldClear({ a: 1, b: null, c: undefined, d: "test" }, { excludeValues: [null, undefined] });
|
|
73
|
-
expect(result.a).toBe(1);
|
|
74
|
-
expect(result.b).toBeUndefined();
|
|
75
|
-
expect(result.c).toBeUndefined();
|
|
76
|
-
expect(result.d).toBe("test");
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test("保留指定值", () => {
|
|
80
|
-
const result = fieldClear({ a: 1, b: null, c: 0 }, { excludeValues: [null, undefined], keepMap: { c: 0 } });
|
|
81
|
-
expect(result.a).toBe(1);
|
|
82
|
-
expect(result.b).toBeUndefined();
|
|
83
|
-
expect(result.c).toBe(0);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("处理空对象", () => {
|
|
87
|
-
const result = fieldClear({});
|
|
88
|
-
expect(Object.keys(result).length).toBe(0);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe("Util - calcPerfTime", () => {
|
|
93
|
-
test("计算性能时间", () => {
|
|
94
|
-
const start = Bun.nanoseconds();
|
|
95
|
-
const result = calcPerfTime(start);
|
|
96
|
-
expect(result).toContain("毫秒");
|
|
97
|
-
expect(typeof result).toBe("string");
|
|
98
|
-
expect(result).toMatch(/\d+(\.\d+)?\s*毫秒/);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 测试 Validator 对 array_number_string 和 array_number_text 类型的支持
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { FieldDefinition } from "befly/types/validate";
|
|
6
|
-
|
|
7
|
-
import { describe, expect, test } from "bun:test";
|
|
8
|
-
|
|
9
|
-
import { Validator } from "../lib/validator.js";
|
|
10
|
-
|
|
11
|
-
describe("Validator - array_number 类型验证", () => {
|
|
12
|
-
// ==================== 类型转换测试 ====================
|
|
13
|
-
|
|
14
|
-
test("array_number_string: 接受数字数组", () => {
|
|
15
|
-
const field: FieldDefinition = {
|
|
16
|
-
name: "数字数组",
|
|
17
|
-
type: "array_number_string",
|
|
18
|
-
min: null,
|
|
19
|
-
max: null,
|
|
20
|
-
default: null,
|
|
21
|
-
regexp: null
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const result = Validator.single([1, 2, 3], field);
|
|
25
|
-
expect(result.error).toBeNull();
|
|
26
|
-
expect(result.value).toEqual([1, 2, 3]);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("array_number_string: 拒绝字符串数组", () => {
|
|
30
|
-
const field: FieldDefinition = {
|
|
31
|
-
name: "数字数组",
|
|
32
|
-
type: "array_number_string",
|
|
33
|
-
min: null,
|
|
34
|
-
max: null,
|
|
35
|
-
default: null,
|
|
36
|
-
regexp: null
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const result = Validator.single(["1", "2", "3"], field);
|
|
40
|
-
expect(result.error).toBe("数组元素必须是数字");
|
|
41
|
-
expect(result.value).toBeNull();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("array_number_string: 拒绝混合类型数组", () => {
|
|
45
|
-
const field: FieldDefinition = {
|
|
46
|
-
name: "数字数组",
|
|
47
|
-
type: "array_number_string",
|
|
48
|
-
min: null,
|
|
49
|
-
max: null,
|
|
50
|
-
default: null,
|
|
51
|
-
regexp: null
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const result = Validator.single([1, "2", 3], field);
|
|
55
|
-
expect(result.error).toBe("数组元素必须是数字");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("array_number_string: 拒绝非数组值", () => {
|
|
59
|
-
const field: FieldDefinition = {
|
|
60
|
-
name: "数字数组",
|
|
61
|
-
type: "array_number_string",
|
|
62
|
-
min: null,
|
|
63
|
-
max: null,
|
|
64
|
-
default: null,
|
|
65
|
-
regexp: null
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const result = Validator.single("123", field);
|
|
69
|
-
expect(result.error).toBe("必须是数组");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("array_number_text: 接受数字数组", () => {
|
|
73
|
-
const field: FieldDefinition = {
|
|
74
|
-
name: "长数字数组",
|
|
75
|
-
type: "array_number_text",
|
|
76
|
-
min: null,
|
|
77
|
-
max: null,
|
|
78
|
-
default: null,
|
|
79
|
-
regexp: null
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const result = Validator.single([100, 200, 300], field);
|
|
83
|
-
expect(result.error).toBeNull();
|
|
84
|
-
expect(result.value).toEqual([100, 200, 300]);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("array_number_text: 拒绝 NaN 和 Infinity", () => {
|
|
88
|
-
const field: FieldDefinition = {
|
|
89
|
-
name: "数字数组",
|
|
90
|
-
type: "array_number_text",
|
|
91
|
-
min: null,
|
|
92
|
-
max: null,
|
|
93
|
-
default: null,
|
|
94
|
-
regexp: null
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const result1 = Validator.single([1, NaN, 3], field);
|
|
98
|
-
expect(result1.error).toBe("数组元素必须是数字");
|
|
99
|
-
|
|
100
|
-
const result2 = Validator.single([1, Infinity, 3], field);
|
|
101
|
-
expect(result2.error).toBe("数组元素必须是数字");
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// ==================== 规则验证测试 ====================
|
|
105
|
-
|
|
106
|
-
test("array_number_string: min 规则验证", () => {
|
|
107
|
-
const field: FieldDefinition = {
|
|
108
|
-
name: "数字数组",
|
|
109
|
-
type: "array_number_string",
|
|
110
|
-
min: 2,
|
|
111
|
-
max: null,
|
|
112
|
-
default: null,
|
|
113
|
-
regexp: null
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const result1 = Validator.single([1], field);
|
|
117
|
-
expect(result1.error).toBe("至少需要2个元素");
|
|
118
|
-
|
|
119
|
-
const result2 = Validator.single([1, 2], field);
|
|
120
|
-
expect(result2.error).toBeNull();
|
|
121
|
-
|
|
122
|
-
const result3 = Validator.single([1, 2, 3], field);
|
|
123
|
-
expect(result3.error).toBeNull();
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("array_number_string: max 规则验证", () => {
|
|
127
|
-
const field: FieldDefinition = {
|
|
128
|
-
name: "数字数组",
|
|
129
|
-
type: "array_number_string",
|
|
130
|
-
min: null,
|
|
131
|
-
max: 3,
|
|
132
|
-
default: null,
|
|
133
|
-
regexp: null
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const result1 = Validator.single([1, 2, 3], field);
|
|
137
|
-
expect(result1.error).toBeNull();
|
|
138
|
-
|
|
139
|
-
const result2 = Validator.single([1, 2, 3, 4], field);
|
|
140
|
-
expect(result2.error).toBe("最多只能有3个元素");
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test("array_number_text: min + max 规则验证", () => {
|
|
144
|
-
const field: FieldDefinition = {
|
|
145
|
-
name: "长数字数组",
|
|
146
|
-
type: "array_number_text",
|
|
147
|
-
min: 1,
|
|
148
|
-
max: 5,
|
|
149
|
-
default: null,
|
|
150
|
-
regexp: null
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const result1 = Validator.single([], field);
|
|
154
|
-
expect(result1.error).toBe("至少需要1个元素");
|
|
155
|
-
|
|
156
|
-
const result2 = Validator.single([1, 2, 3], field);
|
|
157
|
-
expect(result2.error).toBeNull();
|
|
158
|
-
|
|
159
|
-
const result3 = Validator.single([1, 2, 3, 4, 5, 6], field);
|
|
160
|
-
expect(result3.error).toBe("最多只能有5个元素");
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// ==================== 默认值测试 ====================
|
|
164
|
-
|
|
165
|
-
test("array_number_string: 无 default 时返回空数组", () => {
|
|
166
|
-
const field: FieldDefinition = {
|
|
167
|
-
name: "数字数组",
|
|
168
|
-
type: "array_number_string",
|
|
169
|
-
min: null,
|
|
170
|
-
max: null,
|
|
171
|
-
default: null,
|
|
172
|
-
regexp: null
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const result1 = Validator.single(undefined, field);
|
|
176
|
-
expect(result1.error).toBeNull();
|
|
177
|
-
expect(result1.value).toEqual([]);
|
|
178
|
-
|
|
179
|
-
const result2 = Validator.single(null, field);
|
|
180
|
-
expect(result2.error).toBeNull();
|
|
181
|
-
expect(result2.value).toEqual([]);
|
|
182
|
-
|
|
183
|
-
const result3 = Validator.single("", field);
|
|
184
|
-
expect(result3.error).toBeNull();
|
|
185
|
-
expect(result3.value).toEqual([]);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test("array_number_string: 有 default 时返回自定义默认值", () => {
|
|
189
|
-
const field: FieldDefinition = {
|
|
190
|
-
name: "数字数组",
|
|
191
|
-
type: "array_number_string",
|
|
192
|
-
min: null,
|
|
193
|
-
max: null,
|
|
194
|
-
default: [10, 20, 30],
|
|
195
|
-
regexp: null
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
const result = Validator.single(undefined, field);
|
|
199
|
-
expect(result.error).toBeNull();
|
|
200
|
-
expect(result.value).toEqual([10, 20, 30]);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test('array_number_string: default 为字符串 "[]" 时返回空数组', () => {
|
|
204
|
-
const field: FieldDefinition = {
|
|
205
|
-
name: "数字数组",
|
|
206
|
-
type: "array_number_string",
|
|
207
|
-
min: null,
|
|
208
|
-
max: null,
|
|
209
|
-
default: "[]",
|
|
210
|
-
regexp: null
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const result = Validator.single(null, field);
|
|
214
|
-
expect(result.error).toBeNull();
|
|
215
|
-
expect(result.value).toEqual([]);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
test("array_number_text: 无 default 时返回空数组", () => {
|
|
219
|
-
const field: FieldDefinition = {
|
|
220
|
-
name: "长数字数组",
|
|
221
|
-
type: "array_number_text",
|
|
222
|
-
min: null,
|
|
223
|
-
max: null,
|
|
224
|
-
default: null,
|
|
225
|
-
regexp: null
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const result = Validator.single(undefined, field);
|
|
229
|
-
expect(result.error).toBeNull();
|
|
230
|
-
expect(result.value).toEqual([]);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
test("array_number_text: 有 default 时返回自定义默认值", () => {
|
|
234
|
-
const field: FieldDefinition = {
|
|
235
|
-
name: "长数字数组",
|
|
236
|
-
type: "array_number_text",
|
|
237
|
-
min: null,
|
|
238
|
-
max: null,
|
|
239
|
-
default: [100, 200],
|
|
240
|
-
regexp: null
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
const result = Validator.single(undefined, field);
|
|
244
|
-
expect(result.error).toBeNull();
|
|
245
|
-
expect(result.value).toEqual([100, 200]);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// ==================== validate 方法测试 ====================
|
|
249
|
-
|
|
250
|
-
test("validate: array_number_string 字段验证", () => {
|
|
251
|
-
const rules = {
|
|
252
|
-
tags: {
|
|
253
|
-
name: "标签ID",
|
|
254
|
-
type: "array_number_string",
|
|
255
|
-
min: 1,
|
|
256
|
-
max: 10,
|
|
257
|
-
default: null,
|
|
258
|
-
regexp: null
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const result1 = Validator.validate({ tags: [1, 2, 3] }, rules as any, ["tags"]);
|
|
263
|
-
expect(result1.failed).toBe(false);
|
|
264
|
-
|
|
265
|
-
const result2 = Validator.validate({ tags: [] }, rules as any, ["tags"]);
|
|
266
|
-
expect(result2.failed).toBe(true);
|
|
267
|
-
expect(result2.firstError).toBe("标签ID至少需要1个元素");
|
|
268
|
-
|
|
269
|
-
const result3 = Validator.validate({ tags: ["1", "2"] }, rules as any, ["tags"]);
|
|
270
|
-
expect(result3.failed).toBe(true);
|
|
271
|
-
expect(result3.firstError).toBe("标签ID数组元素必须是数字");
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
test("validate: array_number_text 字段验证", () => {
|
|
275
|
-
const rules = {
|
|
276
|
-
ids: {
|
|
277
|
-
name: "关联ID",
|
|
278
|
-
type: "array_number_text",
|
|
279
|
-
min: null,
|
|
280
|
-
max: null,
|
|
281
|
-
default: null,
|
|
282
|
-
regexp: null
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
const result1 = Validator.validate({ ids: [100, 200, 300] }, rules as any, []);
|
|
287
|
-
expect(result1.failed).toBe(false);
|
|
288
|
-
|
|
289
|
-
const result2 = Validator.validate({ ids: "not-array" }, rules as any, []);
|
|
290
|
-
expect(result2.failed).toBe(true);
|
|
291
|
-
expect(result2.firstError).toBe("关联ID必须是数组");
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
test("validate: undefined 参数跳过验证", () => {
|
|
295
|
-
const rules = {
|
|
296
|
-
tags: {
|
|
297
|
-
name: "标签",
|
|
298
|
-
type: "array_number_string",
|
|
299
|
-
min: 1,
|
|
300
|
-
max: null,
|
|
301
|
-
default: null,
|
|
302
|
-
regexp: null
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
// undefined 且不是必填字段,应跳过验证
|
|
307
|
-
const result = Validator.validate({ other: "value" }, rules as any, []);
|
|
308
|
-
expect(result.failed).toBe(false);
|
|
309
|
-
});
|
|
310
|
-
});
|