befly 3.10.0 → 3.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.gitignore +0 -0
  2. package/README.md +10 -13
  3. package/configs/presetFields.ts +10 -0
  4. package/configs/presetRegexp.ts +225 -0
  5. package/docs/README.md +17 -11
  6. package/docs/api/api.md +15 -1
  7. package/docs/guide/quickstart.md +19 -5
  8. package/docs/infra/redis.md +23 -11
  9. package/docs/quickstart.md +5 -335
  10. package/docs/reference/addon.md +0 -15
  11. package/docs/reference/config.md +1 -1
  12. package/docs/reference/logger.md +3 -3
  13. package/docs/reference/sync.md +99 -73
  14. package/docs/reference/table.md +1 -1
  15. package/package.json +15 -16
  16. package/docs/cipher.md +0 -582
  17. package/docs/database.md +0 -1176
  18. package/tests/_mocks/mockSqliteDb.ts +0 -204
  19. package/tests/addonHelper-cache.test.ts +0 -32
  20. package/tests/api-integration-array-number.test.ts +0 -282
  21. package/tests/apiHandler-routePath-only.test.ts +0 -32
  22. package/tests/befly-config-env.test.ts +0 -78
  23. package/tests/cacheHelper.test.ts +0 -323
  24. package/tests/cacheKeys.test.ts +0 -41
  25. package/tests/checkApi-routePath-strict.test.ts +0 -166
  26. package/tests/checkMenu.test.ts +0 -346
  27. package/tests/checkTable-smoke.test.ts +0 -157
  28. package/tests/cipher.test.ts +0 -249
  29. package/tests/dbDialect-cache.test.ts +0 -23
  30. package/tests/dbDialect.test.ts +0 -46
  31. package/tests/dbHelper-advanced.test.ts +0 -723
  32. package/tests/dbHelper-all-array-types.test.ts +0 -316
  33. package/tests/dbHelper-array-serialization.test.ts +0 -258
  34. package/tests/dbHelper-batch-write.test.ts +0 -90
  35. package/tests/dbHelper-columns.test.ts +0 -234
  36. package/tests/dbHelper-execute.test.ts +0 -187
  37. package/tests/dbHelper-joins.test.ts +0 -221
  38. package/tests/fields-redis-cache.test.ts +0 -127
  39. package/tests/fields-validate.test.ts +0 -99
  40. package/tests/fixtures/scanFilesAddon/node_modules/@befly-addon/demo/apis/sub/b.ts +0 -3
  41. package/tests/fixtures/scanFilesApis/a.ts +0 -3
  42. package/tests/fixtures/scanFilesApis/sub/b.ts +0 -3
  43. package/tests/getClientIp.test.ts +0 -54
  44. package/tests/integration.test.ts +0 -189
  45. package/tests/jwt.test.ts +0 -65
  46. package/tests/loadPlugins-order-smoke.test.ts +0 -75
  47. package/tests/logger.test.ts +0 -325
  48. package/tests/redisHelper.test.ts +0 -495
  49. package/tests/redisKeys.test.ts +0 -9
  50. package/tests/scanConfig.test.ts +0 -144
  51. package/tests/scanFiles-routePath.test.ts +0 -46
  52. package/tests/smoke-sql.test.ts +0 -24
  53. package/tests/sqlBuilder-advanced.test.ts +0 -608
  54. package/tests/sqlBuilder.test.ts +0 -209
  55. package/tests/sync-connection.test.ts +0 -183
  56. package/tests/sync-init-guard.test.ts +0 -105
  57. package/tests/syncApi-insBatch-fields-consistent.test.ts +0 -61
  58. package/tests/syncApi-obsolete-records.test.ts +0 -69
  59. package/tests/syncApi-type-compat.test.ts +0 -72
  60. package/tests/syncDev-permissions.test.ts +0 -81
  61. package/tests/syncMenu-disableMenus-hard-delete.test.ts +0 -88
  62. package/tests/syncMenu-duplicate-path.test.ts +0 -122
  63. package/tests/syncMenu-obsolete-records.test.ts +0 -161
  64. package/tests/syncMenu-parentPath-from-tree.test.ts +0 -75
  65. package/tests/syncMenu-paths.test.ts +0 -59
  66. package/tests/syncTable-apply.test.ts +0 -279
  67. package/tests/syncTable-array-number.test.ts +0 -160
  68. package/tests/syncTable-constants.test.ts +0 -101
  69. package/tests/syncTable-db-integration.test.ts +0 -237
  70. package/tests/syncTable-ddl.test.ts +0 -245
  71. package/tests/syncTable-helpers.test.ts +0 -99
  72. package/tests/syncTable-schema.test.ts +0 -99
  73. package/tests/syncTable-testkit.test.ts +0 -25
  74. package/tests/syncTable-types.test.ts +0 -122
  75. package/tests/tableRef-and-deserialize.test.ts +0 -67
  76. package/tests/util.test.ts +0 -100
  77. package/tests/validator-array-number.test.ts +0 -310
  78. package/tests/validator-default.test.ts +0 -373
  79. 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
- });
@@ -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
- });