hola-server 1.0.10 → 2.0.1
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 +196 -1
- package/core/array.js +79 -142
- package/core/bash.js +208 -259
- package/core/chart.js +26 -16
- package/core/cron.js +14 -3
- package/core/date.js +15 -44
- package/core/encrypt.js +19 -9
- package/core/file.js +42 -29
- package/core/lhs.js +32 -6
- package/core/meta.js +213 -289
- package/core/msg.js +20 -7
- package/core/number.js +105 -103
- package/core/obj.js +15 -12
- package/core/random.js +9 -6
- package/core/role.js +69 -77
- package/core/thread.js +12 -2
- package/core/type.js +300 -261
- package/core/url.js +20 -12
- package/core/validate.js +29 -26
- package/db/db.js +297 -227
- package/db/entity.js +631 -963
- package/db/gridfs.js +120 -166
- package/design/add_default_field_attr.md +56 -0
- package/http/context.js +22 -8
- package/http/cors.js +25 -8
- package/http/error.js +27 -9
- package/http/express.js +70 -41
- package/http/params.js +70 -42
- package/http/router.js +51 -40
- package/http/session.js +59 -36
- package/index.js +85 -9
- package/package.json +2 -2
- package/router/clone.js +28 -36
- package/router/create.js +21 -26
- package/router/delete.js +24 -28
- package/router/read.js +137 -123
- package/router/update.js +38 -56
- package/setting.js +22 -6
- package/skills/array.md +155 -0
- package/skills/bash.md +91 -0
- package/skills/chart.md +54 -0
- package/skills/code.md +422 -0
- package/skills/context.md +177 -0
- package/skills/date.md +58 -0
- package/skills/express.md +255 -0
- package/skills/file.md +60 -0
- package/skills/lhs.md +54 -0
- package/skills/meta.md +1023 -0
- package/skills/msg.md +30 -0
- package/skills/number.md +88 -0
- package/skills/obj.md +36 -0
- package/skills/params.md +206 -0
- package/skills/random.md +22 -0
- package/skills/role.md +59 -0
- package/skills/session.md +281 -0
- package/skills/storage.md +743 -0
- package/skills/thread.md +22 -0
- package/skills/type.md +547 -0
- package/skills/url.md +34 -0
- package/skills/validate.md +48 -0
- package/test/cleanup/close-db.js +5 -0
- package/test/core/array.js +226 -0
- package/test/core/chart.js +51 -0
- package/test/core/file.js +59 -0
- package/test/core/lhs.js +44 -0
- package/test/core/number.js +167 -12
- package/test/core/obj.js +47 -0
- package/test/core/random.js +24 -0
- package/test/core/thread.js +20 -0
- package/test/core/type.js +216 -0
- package/test/core/validate.js +67 -0
- package/test/db/db-ops.js +99 -0
- package/test/db/pipe_test.txt +0 -0
- package/test/db/test_case_design.md +528 -0
- package/test/db/test_db_class.js +613 -0
- package/test/db/test_entity_class.js +414 -0
- package/test/db/test_gridfs_class.js +234 -0
- package/test/entity/create.js +1 -1
- package/test/entity/delete-mixed.js +156 -0
- package/test/entity/ref-filter.js +63 -0
- package/tool/gen_i18n.js +55 -21
- package/test/crud/router.js +0 -99
- package/test/router/user.js +0 -17
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
const { strictEqual, deepStrictEqual } = require('assert');
|
|
2
|
+
const { shuffle, remove_element, pop_n, shift_n, sum, avg, combine, sort_desc, sort_asc, sort_by_key_seq, unique, map_array_to_obj } = require('../../core/array');
|
|
3
|
+
|
|
4
|
+
describe('array', function () {
|
|
5
|
+
describe('shuffle', function () {
|
|
6
|
+
it('should shuffle array in place', function () {
|
|
7
|
+
const arr = [1, 2, 3, 4, 5];
|
|
8
|
+
const original = [...arr];
|
|
9
|
+
shuffle(arr);
|
|
10
|
+
strictEqual(arr.length, original.length);
|
|
11
|
+
// All elements should still be present
|
|
12
|
+
original.forEach(el => strictEqual(arr.includes(el), true));
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should not modify single element array', function () {
|
|
16
|
+
const arr = [1];
|
|
17
|
+
shuffle(arr);
|
|
18
|
+
deepStrictEqual(arr, [1]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should handle empty array', function () {
|
|
22
|
+
const arr = [];
|
|
23
|
+
shuffle(arr);
|
|
24
|
+
deepStrictEqual(arr, []);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('remove_element', function () {
|
|
29
|
+
it('should remove elements by field value', function () {
|
|
30
|
+
const arr = [{ id: 1 }, { id: 2 }, { id: 1 }, { id: 3 }];
|
|
31
|
+
remove_element(arr, 'id', 1);
|
|
32
|
+
strictEqual(arr.length, 2);
|
|
33
|
+
strictEqual(arr[0].id, 2);
|
|
34
|
+
strictEqual(arr[1].id, 3);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should handle empty array', function () {
|
|
38
|
+
const arr = [];
|
|
39
|
+
remove_element(arr, 'id', 1);
|
|
40
|
+
strictEqual(arr.length, 0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should handle no matching elements', function () {
|
|
44
|
+
const arr = [{ id: 1 }, { id: 2 }];
|
|
45
|
+
remove_element(arr, 'id', 999);
|
|
46
|
+
strictEqual(arr.length, 2);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('pop_n', function () {
|
|
51
|
+
it('should pop n elements from end', function () {
|
|
52
|
+
const arr = [1, 2, 3, 4, 5];
|
|
53
|
+
const result = pop_n(arr, 2);
|
|
54
|
+
deepStrictEqual(result, [5, 4]);
|
|
55
|
+
strictEqual(arr.length, 3);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should return undefined for empty array', function () {
|
|
59
|
+
const arr = [];
|
|
60
|
+
const result = pop_n(arr, 2);
|
|
61
|
+
strictEqual(result, undefined);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should return partial results when n > array length', function () {
|
|
65
|
+
const arr = [1, 2, 3];
|
|
66
|
+
const result = pop_n(arr, 5);
|
|
67
|
+
deepStrictEqual(result, [3, 2, 1]);
|
|
68
|
+
strictEqual(arr.length, 0);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('shift_n', function () {
|
|
73
|
+
it('should shift n elements from start', function () {
|
|
74
|
+
const arr = [1, 2, 3, 4, 5];
|
|
75
|
+
const result = shift_n(arr, 2);
|
|
76
|
+
deepStrictEqual(result, [1, 2]);
|
|
77
|
+
strictEqual(arr.length, 3);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return undefined for empty array', function () {
|
|
81
|
+
const arr = [];
|
|
82
|
+
const result = shift_n(arr, 2);
|
|
83
|
+
strictEqual(result, undefined);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should return partial results when n > array length', function () {
|
|
87
|
+
const arr = [1, 2, 3];
|
|
88
|
+
const result = shift_n(arr, 5);
|
|
89
|
+
deepStrictEqual(result, [1, 2, 3]);
|
|
90
|
+
strictEqual(arr.length, 0);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('sum', function () {
|
|
95
|
+
it('should calculate sum of numbers', function () {
|
|
96
|
+
strictEqual(sum([1, 2, 3, 4, 5]), 15);
|
|
97
|
+
strictEqual(sum([1.1, 2.2, 3.3]), 6.6);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return 0 for empty array', function () {
|
|
101
|
+
strictEqual(sum([]), 0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should handle negative numbers', function () {
|
|
105
|
+
strictEqual(sum([10, -5, 3]), 8);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('avg', function () {
|
|
110
|
+
it('should calculate average of numbers', function () {
|
|
111
|
+
strictEqual(avg([1, 2, 3, 4, 5]), 3);
|
|
112
|
+
strictEqual(avg([10, 20]), 15);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should return 0 for empty array', function () {
|
|
116
|
+
strictEqual(avg([]), 0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should handle negative numbers', function () {
|
|
120
|
+
strictEqual(avg([10, -10, 5]), 1.67);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('combine', function () {
|
|
125
|
+
it('should create cartesian product', function () {
|
|
126
|
+
const arr1 = [{ a: 1 }, { a: 2 }];
|
|
127
|
+
const arr2 = [{ b: 3 }, { b: 4 }];
|
|
128
|
+
const result = combine(arr1, arr2);
|
|
129
|
+
strictEqual(result.length, 4);
|
|
130
|
+
deepStrictEqual(result[0], { a: 1, b: 3 });
|
|
131
|
+
deepStrictEqual(result[1], { a: 1, b: 4 });
|
|
132
|
+
deepStrictEqual(result[2], { a: 2, b: 3 });
|
|
133
|
+
deepStrictEqual(result[3], { a: 2, b: 4 });
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should handle empty first array', function () {
|
|
137
|
+
const result = combine([], [{ b: 1 }]);
|
|
138
|
+
deepStrictEqual(result, []);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should handle empty second array', function () {
|
|
142
|
+
const result = combine([{ a: 1 }], []);
|
|
143
|
+
deepStrictEqual(result, []);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('sort_desc', function () {
|
|
148
|
+
it('should sort array descending by attribute', function () {
|
|
149
|
+
const arr = [{ val: 3 }, { val: 1 }, { val: 2 }];
|
|
150
|
+
sort_desc(arr, 'val');
|
|
151
|
+
strictEqual(arr[0].val, 3);
|
|
152
|
+
strictEqual(arr[1].val, 2);
|
|
153
|
+
strictEqual(arr[2].val, 1);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('sort_asc', function () {
|
|
158
|
+
it('should sort array ascending by attribute', function () {
|
|
159
|
+
const arr = [{ val: 3 }, { val: 1 }, { val: 2 }];
|
|
160
|
+
sort_asc(arr, 'val');
|
|
161
|
+
strictEqual(arr[0].val, 1);
|
|
162
|
+
strictEqual(arr[1].val, 2);
|
|
163
|
+
strictEqual(arr[2].val, 3);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('sort_by_key_seq', function () {
|
|
168
|
+
it('should sort by predefined key sequence', function () {
|
|
169
|
+
const arr = [{ type: 'c' }, { type: 'a' }, { type: 'b' }];
|
|
170
|
+
const keys = ['a', 'b', 'c'];
|
|
171
|
+
sort_by_key_seq(arr, 'type', keys);
|
|
172
|
+
strictEqual(arr[0].type, 'a');
|
|
173
|
+
strictEqual(arr[1].type, 'b');
|
|
174
|
+
strictEqual(arr[2].type, 'c');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should handle elements not in key sequence', function () {
|
|
178
|
+
const arr = [{ type: 'x' }, { type: 'a' }, { type: 'b' }];
|
|
179
|
+
const keys = ['a', 'b', 'c'];
|
|
180
|
+
sort_by_key_seq(arr, 'type', keys);
|
|
181
|
+
// 'x' not in keys, indexOf returns -1, sorts to beginning
|
|
182
|
+
strictEqual(arr[0].type, 'x');
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('unique', function () {
|
|
187
|
+
it('should remove duplicate primitives', function () {
|
|
188
|
+
const result = unique([1, 2, 2, 3, 1]);
|
|
189
|
+
deepStrictEqual(result, [1, 2, 3]);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should remove duplicate objects', function () {
|
|
193
|
+
const result = unique([{ a: 1 }, { a: 2 }, { a: 1 }]);
|
|
194
|
+
strictEqual(result.length, 2);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should handle empty array', function () {
|
|
198
|
+
const result = unique([]);
|
|
199
|
+
deepStrictEqual(result, []);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should handle array with all duplicates', function () {
|
|
203
|
+
const result = unique([1, 1, 1]);
|
|
204
|
+
deepStrictEqual(result, [1]);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('map_array_to_obj', function () {
|
|
209
|
+
it('should convert array to key-value object', function () {
|
|
210
|
+
const arr = [{ key: 'a', val: 1 }, { key: 'b', val: 2 }];
|
|
211
|
+
const result = map_array_to_obj(arr, 'key', 'val');
|
|
212
|
+
deepStrictEqual(result, { a: 1, b: 2 });
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should handle duplicate keys (last wins)', function () {
|
|
216
|
+
const arr = [{ id: 'a', val: 1 }, { id: 'a', val: 2 }];
|
|
217
|
+
const result = map_array_to_obj(arr, 'id', 'val');
|
|
218
|
+
deepStrictEqual(result, { a: 2 });
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should handle empty array', function () {
|
|
222
|
+
const result = map_array_to_obj([], 'key', 'val');
|
|
223
|
+
deepStrictEqual(result, {});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const { strictEqual, deepStrictEqual } = require('assert');
|
|
2
|
+
const { set_chart_header, merge_chart_data } = require('../../core/chart');
|
|
3
|
+
|
|
4
|
+
describe('chart', function () {
|
|
5
|
+
describe('set_chart_header', function () {
|
|
6
|
+
it('should add prefix to headers except first', function () {
|
|
7
|
+
const arr = [['Date', 'Value1', 'Value2'], [1, 2, 3]];
|
|
8
|
+
set_chart_header(arr, 'Prefix_');
|
|
9
|
+
strictEqual(arr[0][0], 'Date');
|
|
10
|
+
strictEqual(arr[0][1], 'Prefix_Value1');
|
|
11
|
+
strictEqual(arr[0][2], 'Prefix_Value2');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should handle empty array', function () {
|
|
15
|
+
const arr = [];
|
|
16
|
+
set_chart_header(arr, 'Prefix_');
|
|
17
|
+
strictEqual(arr.length, 0);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should handle single column', function () {
|
|
21
|
+
const arr = [['Date']];
|
|
22
|
+
set_chart_header(arr, 'Prefix_');
|
|
23
|
+
strictEqual(arr[0][0], 'Date');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('merge_chart_data', function () {
|
|
28
|
+
it('should merge two chart data arrays', function () {
|
|
29
|
+
const arr1 = [['Date', 'A'], ['2024', 1]];
|
|
30
|
+
const arr2 = [['Date', 'B'], ['2024', 2]];
|
|
31
|
+
merge_chart_data(arr1, arr2);
|
|
32
|
+
deepStrictEqual(arr1[0], ['Date', 'A', 'B']);
|
|
33
|
+
deepStrictEqual(arr1[1], ['2024', 1, 2]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle arrays of different lengths', function () {
|
|
37
|
+
const arr1 = [['Date', 'A'], ['2024', 1], ['2025', 2]];
|
|
38
|
+
const arr2 = [['Date', 'B'], ['2024', 3]];
|
|
39
|
+
merge_chart_data(arr1, arr2);
|
|
40
|
+
strictEqual(arr1.length, 3);
|
|
41
|
+
strictEqual(arr1[0].length, 3);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle empty arrays', function () {
|
|
45
|
+
const arr1 = [];
|
|
46
|
+
const arr2 = [['Date', 'B']];
|
|
47
|
+
merge_chart_data(arr1, arr2);
|
|
48
|
+
strictEqual(arr1.length, 0);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { strictEqual } = require('assert');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const { file_extension, file_prefix, is_file_exist, get_file_size } = require('../../core/file');
|
|
5
|
+
|
|
6
|
+
describe('file', function () {
|
|
7
|
+
describe('file_extension', function () {
|
|
8
|
+
it('should extract file extension', function () {
|
|
9
|
+
strictEqual(file_extension('document.pdf'), 'pdf');
|
|
10
|
+
strictEqual(file_extension('image.png'), 'png');
|
|
11
|
+
strictEqual(file_extension('archive.tar.gz'), 'gz');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should return empty string for files without extension', function () {
|
|
15
|
+
strictEqual(file_extension('Makefile'), 'Makefile');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should handle null/undefined', function () {
|
|
19
|
+
strictEqual(file_extension(null), '');
|
|
20
|
+
strictEqual(file_extension(undefined), '');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('file_prefix', function () {
|
|
25
|
+
it('should extract file name prefix', function () {
|
|
26
|
+
strictEqual(file_prefix('document.pdf'), 'document');
|
|
27
|
+
strictEqual(file_prefix('image.png'), 'image');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should handle files without extension', function () {
|
|
31
|
+
strictEqual(file_prefix('Makefile'), 'Makefile');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should handle null/undefined', function () {
|
|
35
|
+
strictEqual(file_prefix(null), '');
|
|
36
|
+
strictEqual(file_prefix(undefined), '');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('is_file_exist', function () {
|
|
41
|
+
it('should return true for existing file', function () {
|
|
42
|
+
const testFile = path.join(__dirname, '../../package.json');
|
|
43
|
+
strictEqual(is_file_exist(testFile), true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return false for non-existing file', function () {
|
|
47
|
+
strictEqual(is_file_exist('/nonexistent/path/file.txt'), false);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('get_file_size', function () {
|
|
52
|
+
it('should return file size in bytes', async function () {
|
|
53
|
+
const testFile = path.join(__dirname, '../../package.json');
|
|
54
|
+
const size = await get_file_size(testFile);
|
|
55
|
+
strictEqual(typeof size, 'number');
|
|
56
|
+
strictEqual(size > 0, true);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
package/test/core/lhs.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const { strictEqual } = require('assert');
|
|
2
|
+
const { get_context, verify_template, execute_template } = require('../../core/lhs');
|
|
3
|
+
|
|
4
|
+
describe('lhs', function () {
|
|
5
|
+
describe('get_context', function () {
|
|
6
|
+
it('should return context with number utilities', function () {
|
|
7
|
+
const ctx = get_context();
|
|
8
|
+
strictEqual(typeof ctx.range, 'function');
|
|
9
|
+
strictEqual(typeof ctx.scale, 'function');
|
|
10
|
+
strictEqual(typeof ctx.space, 'function');
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('verify_template', function () {
|
|
15
|
+
it('should return null for valid template', function () {
|
|
16
|
+
const result = verify_template('Hello ${name}!', { name: 'World' });
|
|
17
|
+
strictEqual(result, null);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should return error message for invalid template', function () {
|
|
21
|
+
const result = verify_template('Hello ${name!', { name: 'World' });
|
|
22
|
+
strictEqual(typeof result, 'string');
|
|
23
|
+
strictEqual(result.length > 0, true);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('execute_template', function () {
|
|
28
|
+
it('should execute template with variables', function () {
|
|
29
|
+
const result = execute_template('Hello ${name}!', { name: 'World' });
|
|
30
|
+
strictEqual(result, 'Hello World!');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should support expressions in template', function () {
|
|
34
|
+
const result = execute_template('Sum is ${a + b}', { a: 2, b: 3 });
|
|
35
|
+
strictEqual(result, 'Sum is 5');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should work with number utilities from context', function () {
|
|
39
|
+
const ctx = { ...get_context() };
|
|
40
|
+
const result = execute_template('Range: ${range(3).join(",")}', ctx);
|
|
41
|
+
strictEqual(result, 'Range: 0,1,2');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
package/test/core/number.js
CHANGED
|
@@ -1,17 +1,172 @@
|
|
|
1
|
-
const { strictEqual } = require('assert');
|
|
2
|
-
const {
|
|
1
|
+
const { strictEqual, deepStrictEqual } = require('assert');
|
|
2
|
+
const {
|
|
3
|
+
parse_num, extract_number, to_fixed2, round_to_fixed2,
|
|
4
|
+
range, scale, space, is_space, contains_space, is_integer,
|
|
5
|
+
random_number, random_sample, lhs_samples
|
|
6
|
+
} = require('../../core/number');
|
|
3
7
|
|
|
4
8
|
describe('number', function () {
|
|
5
|
-
describe('
|
|
9
|
+
describe('parse_num', function () {
|
|
10
|
+
it('should parse valid numbers', function () {
|
|
11
|
+
strictEqual(parse_num('123'), 123);
|
|
12
|
+
strictEqual(parse_num('3.14'), 3.14);
|
|
13
|
+
strictEqual(parse_num(-5), -5);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should return 0 for invalid values', function () {
|
|
17
|
+
strictEqual(parse_num('abc'), 0);
|
|
18
|
+
strictEqual(parse_num(null), 0);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('to_fixed2', function () {
|
|
23
|
+
it('should parse and round to 2 decimals', function () {
|
|
24
|
+
strictEqual(to_fixed2('3.14159'), 3.14);
|
|
25
|
+
strictEqual(to_fixed2('2.999'), 3);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should return 0 for invalid values', function () {
|
|
29
|
+
strictEqual(to_fixed2('abc'), 0);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('round_to_fixed2', function () {
|
|
34
|
+
it('should round to 2 decimal places', function () {
|
|
35
|
+
strictEqual(round_to_fixed2(3.14159), 3.14);
|
|
36
|
+
strictEqual(round_to_fixed2(2.996), 3);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('range', function () {
|
|
41
|
+
it('should generate range with single argument', function () {
|
|
42
|
+
deepStrictEqual(range(3), [0, 1, 2]);
|
|
43
|
+
deepStrictEqual(range(5), [0, 1, 2, 3, 4]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should generate range with start and end', function () {
|
|
47
|
+
deepStrictEqual(range(1, 5), [1, 2, 3, 4, 5]);
|
|
48
|
+
deepStrictEqual(range(0, 4), [0, 1, 2, 3, 4]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should generate range with step', function () {
|
|
52
|
+
deepStrictEqual(range(0, 10, 2), [0, 2, 4, 6, 8, 10]);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('scale', function () {
|
|
57
|
+
it('should generate exponential scale', function () {
|
|
58
|
+
deepStrictEqual(scale(2, 8), [2, 4, 8]);
|
|
59
|
+
deepStrictEqual(scale(1, 8, 2), [1, 2, 4, 8]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should work with different ratios', function () {
|
|
63
|
+
deepStrictEqual(scale(1, 27, 3), [1, 3, 9, 27]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('space', function () {
|
|
68
|
+
it('should create space object', function () {
|
|
69
|
+
const s = space(0, 100);
|
|
70
|
+
strictEqual(s.min, 0);
|
|
71
|
+
strictEqual(s.max, 100);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should throw without min or max', function () {
|
|
75
|
+
let threw = false;
|
|
76
|
+
try { space(null, 100); } catch { threw = true; }
|
|
77
|
+
strictEqual(threw, true);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('is_space', function () {
|
|
82
|
+
it('should return true for space objects', function () {
|
|
83
|
+
strictEqual(is_space({ min: 0, max: 100 }), true);
|
|
84
|
+
strictEqual(is_space(space(0, 10)), true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should return false for non-space values', function () {
|
|
88
|
+
strictEqual(is_space({ min: 0 }), false);
|
|
89
|
+
strictEqual(is_space(123), false);
|
|
90
|
+
strictEqual(is_space([0, 100]), false);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('is_integer', function () {
|
|
95
|
+
it('should detect integers', function () {
|
|
96
|
+
strictEqual(is_integer('123'), true);
|
|
97
|
+
strictEqual(is_integer('-45'), true);
|
|
98
|
+
strictEqual(is_integer('0'), true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should return false for non-integers', function () {
|
|
102
|
+
strictEqual(is_integer('3.14'), false);
|
|
103
|
+
strictEqual(is_integer('abc'), false);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('contains_space', function () {
|
|
108
|
+
it('should detect space objects in object', function () {
|
|
109
|
+
strictEqual(contains_space({ a: space(0, 10), b: 5 }), true);
|
|
110
|
+
strictEqual(contains_space({ a: 1, b: 2 }), false);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('random_number', function () {
|
|
115
|
+
it('should generate number in range', function () {
|
|
116
|
+
for (let i = 0; i < 50; i++) {
|
|
117
|
+
const num = random_number(0, 10);
|
|
118
|
+
strictEqual(num >= 0 && num <= 10, true);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should return integer for integer bounds', function () {
|
|
123
|
+
for (let i = 0; i < 20; i++) {
|
|
124
|
+
const num = random_number(0, 100);
|
|
125
|
+
strictEqual(Number.isInteger(num), true);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('random_sample', function () {
|
|
131
|
+
it('should sample from arrays', function () {
|
|
132
|
+
const obj = { color: ['red', 'blue', 'green'] };
|
|
133
|
+
const sample = random_sample(obj);
|
|
134
|
+
strictEqual(['red', 'blue', 'green'].includes(sample.color), true);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should sample from spaces', function () {
|
|
138
|
+
const obj = { value: space(0, 100) };
|
|
139
|
+
const sample = random_sample(obj);
|
|
140
|
+
strictEqual(sample.value >= 0 && sample.value <= 100, true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should preserve plain values', function () {
|
|
144
|
+
const obj = { fixed: 42 };
|
|
145
|
+
const sample = random_sample(obj);
|
|
146
|
+
strictEqual(sample.fixed, 42);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('lhs_samples', function () {
|
|
151
|
+
it('should generate Latin Hypercube Sampling ranges', function () {
|
|
152
|
+
const samples = lhs_samples(0, 100, 4);
|
|
153
|
+
strictEqual(samples.length, 4);
|
|
154
|
+
samples.forEach(s => {
|
|
155
|
+
strictEqual(typeof s.min, 'number');
|
|
156
|
+
strictEqual(typeof s.max, 'number');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('extract_number', function () {
|
|
6
162
|
it('should extract number successfully', function () {
|
|
7
|
-
strictEqual(extract_number(
|
|
8
|
-
strictEqual(extract_number(
|
|
9
|
-
strictEqual(extract_number(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
strictEqual(extract_number(
|
|
163
|
+
strictEqual(extract_number('123'), 123);
|
|
164
|
+
strictEqual(extract_number('123 abc'), 123);
|
|
165
|
+
strictEqual(extract_number(' abc 134.56 efg'), 134.56);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should return 0 for multiple numbers', function () {
|
|
169
|
+
strictEqual(extract_number(' abc 134.56 efg 123 rg'), 0);
|
|
14
170
|
});
|
|
15
171
|
});
|
|
16
|
-
}
|
|
17
|
-
);
|
|
172
|
+
});
|
package/test/core/obj.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const { strictEqual, deepStrictEqual } = require('assert');
|
|
2
|
+
const { copy_obj, is_object } = require('../../core/obj');
|
|
3
|
+
|
|
4
|
+
describe('obj', function () {
|
|
5
|
+
describe('copy_obj', function () {
|
|
6
|
+
it('should copy specified attributes', function () {
|
|
7
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
8
|
+
const result = copy_obj(obj, ['a', 'c']);
|
|
9
|
+
deepStrictEqual(result, { a: 1, c: 3 });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should handle missing attributes', function () {
|
|
13
|
+
const obj = { a: 1 };
|
|
14
|
+
const result = copy_obj(obj, ['a', 'b']);
|
|
15
|
+
deepStrictEqual(result, { a: 1, b: undefined });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should return empty object for empty attrs', function () {
|
|
19
|
+
const obj = { a: 1, b: 2 };
|
|
20
|
+
const result = copy_obj(obj, []);
|
|
21
|
+
deepStrictEqual(result, {});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('is_object', function () {
|
|
26
|
+
it('should return true for plain objects', function () {
|
|
27
|
+
strictEqual(is_object({}), true);
|
|
28
|
+
strictEqual(is_object({ a: 1 }), true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should return false for null', function () {
|
|
32
|
+
strictEqual(is_object(null), false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should return false for arrays', function () {
|
|
36
|
+
strictEqual(is_object([]), false);
|
|
37
|
+
strictEqual(is_object([1, 2, 3]), false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return false for primitives', function () {
|
|
41
|
+
strictEqual(is_object('string'), false);
|
|
42
|
+
strictEqual(is_object(123), false);
|
|
43
|
+
strictEqual(is_object(true), false);
|
|
44
|
+
strictEqual(is_object(undefined), false);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { strictEqual } = require('assert');
|
|
2
|
+
const { random_code } = require('../../core/random');
|
|
3
|
+
|
|
4
|
+
describe('random', function () {
|
|
5
|
+
describe('random_code', function () {
|
|
6
|
+
it('should generate number between 0 and 999999', function () {
|
|
7
|
+
for (let i = 0; i < 100; i++) {
|
|
8
|
+
const code = random_code();
|
|
9
|
+
strictEqual(code >= 0, true);
|
|
10
|
+
strictEqual(code < 1000000, true);
|
|
11
|
+
strictEqual(Number.isInteger(code), true);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should generate different values', function () {
|
|
16
|
+
const codes = new Set();
|
|
17
|
+
for (let i = 0; i < 100; i++) {
|
|
18
|
+
codes.add(random_code());
|
|
19
|
+
}
|
|
20
|
+
// Should have at least some variation
|
|
21
|
+
strictEqual(codes.size > 1, true);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { strictEqual } = require('assert');
|
|
2
|
+
const { snooze } = require('../../core/thread');
|
|
3
|
+
|
|
4
|
+
describe('thread', function () {
|
|
5
|
+
describe('snooze', function () {
|
|
6
|
+
it('should wait for specified milliseconds', async function () {
|
|
7
|
+
const start = Date.now();
|
|
8
|
+
await snooze(100);
|
|
9
|
+
const elapsed = Date.now() - start;
|
|
10
|
+
// Allow 50ms tolerance for timing variations
|
|
11
|
+
strictEqual(elapsed >= 90, true);
|
|
12
|
+
strictEqual(elapsed < 200, true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return a Promise', function () {
|
|
16
|
+
const result = snooze(10);
|
|
17
|
+
strictEqual(result instanceof Promise, true);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|