@onchaindb/sdk 0.4.4 → 1.0.0
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/.claude/settings.local.json +10 -2
- package/README.md +422 -355
- package/dist/batch.d.ts +1 -10
- package/dist/batch.d.ts.map +1 -1
- package/dist/batch.js +4 -26
- package/dist/batch.js.map +1 -1
- package/dist/client.d.ts +29 -43
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +199 -323
- package/dist/client.js.map +1 -1
- package/dist/database.d.ts +14 -131
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +35 -131
- package/dist/database.js.map +1 -1
- package/dist/index.d.ts +6 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -15
- package/dist/index.js.map +1 -1
- package/dist/query-sdk/ConditionBuilder.d.ts +3 -11
- package/dist/query-sdk/ConditionBuilder.d.ts.map +1 -1
- package/dist/query-sdk/ConditionBuilder.js +10 -48
- package/dist/query-sdk/ConditionBuilder.js.map +1 -1
- package/dist/query-sdk/NestedBuilders.d.ts +33 -30
- package/dist/query-sdk/NestedBuilders.d.ts.map +1 -1
- package/dist/query-sdk/NestedBuilders.js +46 -43
- package/dist/query-sdk/NestedBuilders.js.map +1 -1
- package/dist/query-sdk/QueryBuilder.d.ts +4 -2
- package/dist/query-sdk/QueryBuilder.d.ts.map +1 -1
- package/dist/query-sdk/QueryBuilder.js +47 -169
- package/dist/query-sdk/QueryBuilder.js.map +1 -1
- package/dist/query-sdk/QueryResult.d.ts +0 -38
- package/dist/query-sdk/QueryResult.d.ts.map +1 -1
- package/dist/query-sdk/QueryResult.js +1 -227
- package/dist/query-sdk/QueryResult.js.map +1 -1
- package/dist/query-sdk/index.d.ts +1 -1
- package/dist/query-sdk/index.d.ts.map +1 -1
- package/dist/query-sdk/index.js.map +1 -1
- package/dist/query-sdk/operators.d.ts +32 -28
- package/dist/query-sdk/operators.d.ts.map +1 -1
- package/dist/query-sdk/operators.js +45 -155
- package/dist/query-sdk/operators.js.map +1 -1
- package/dist/types.d.ts +153 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/jest.config.js +4 -0
- package/package.json +1 -1
- package/skills.md +0 -1
- package/src/client.ts +243 -745
- package/src/database.ts +70 -493
- package/src/index.ts +40 -193
- package/src/query-sdk/ConditionBuilder.ts +37 -89
- package/src/query-sdk/NestedBuilders.ts +90 -92
- package/src/query-sdk/QueryBuilder.ts +59 -218
- package/src/query-sdk/QueryResult.ts +4 -330
- package/src/query-sdk/README.md +214 -583
- package/src/query-sdk/index.ts +1 -1
- package/src/query-sdk/operators.ts +91 -200
- package/src/query-sdk/tests/FieldConditionBuilder.test.ts +70 -71
- package/src/query-sdk/tests/LogicalOperator.test.ts +43 -82
- package/src/query-sdk/tests/NestedBuilders.test.ts +229 -309
- package/src/query-sdk/tests/QueryBuilder.test.ts +5 -5
- package/src/query-sdk/tests/QueryResult.test.ts +41 -435
- package/src/query-sdk/tests/comprehensive.test.ts +4 -185
- package/src/tests/client-requests.test.ts +280 -0
- package/src/tests/client-validation.test.ts +80 -0
- package/src/types.ts +229 -8
- package/src/batch.ts +0 -257
- package/src/query-sdk/dist/ConditionBuilder.d.ts +0 -22
- package/src/query-sdk/dist/ConditionBuilder.js +0 -90
- package/src/query-sdk/dist/FieldConditionBuilder.d.ts +0 -1
- package/src/query-sdk/dist/FieldConditionBuilder.js +0 -6
- package/src/query-sdk/dist/NestedBuilders.d.ts +0 -43
- package/src/query-sdk/dist/NestedBuilders.js +0 -144
- package/src/query-sdk/dist/OnChainDB.d.ts +0 -19
- package/src/query-sdk/dist/OnChainDB.js +0 -123
- package/src/query-sdk/dist/QueryBuilder.d.ts +0 -70
- package/src/query-sdk/dist/QueryBuilder.js +0 -295
- package/src/query-sdk/dist/QueryResult.d.ts +0 -52
- package/src/query-sdk/dist/QueryResult.js +0 -293
- package/src/query-sdk/dist/SelectionBuilder.d.ts +0 -20
- package/src/query-sdk/dist/SelectionBuilder.js +0 -80
- package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +0 -27
- package/src/query-sdk/dist/adapters/HttpClientAdapter.js +0 -170
- package/src/query-sdk/dist/index.d.ts +0 -36
- package/src/query-sdk/dist/index.js +0 -27
- package/src/query-sdk/dist/operators.d.ts +0 -56
- package/src/query-sdk/dist/operators.js +0 -289
- package/src/query-sdk/dist/tests/setup.d.ts +0 -15
- package/src/query-sdk/dist/tests/setup.js +0 -46
- package/src/query-sdk/jest.config.js +0 -25
- package/src/query-sdk/package.json +0 -46
- package/src/query-sdk/tests/aggregations.test.ts +0 -653
- package/src/query-sdk/tests/integration.test.ts +0 -608
- package/src/query-sdk/tests/operators.test.ts +0 -327
- package/src/query-sdk/tests/unit.test.ts +0 -794
- package/src/query-sdk/tsconfig.json +0 -26
- package/src/query-sdk/yarn.lock +0 -3092
|
@@ -2,11 +2,9 @@ import { createQueryResult, QueryResult } from '../QueryResult';
|
|
|
2
2
|
|
|
3
3
|
describe('QueryResult', () => {
|
|
4
4
|
const testData = [
|
|
5
|
-
{ id: 1, name: 'Alice', age: 30
|
|
6
|
-
{ id: 2, name: 'Bob', age: 25
|
|
7
|
-
{ id: 3, name: 'Carol', age: 35
|
|
8
|
-
{ id: 4, name: 'David', age: 28, department: 'Marketing', salary: 55000, active: true },
|
|
9
|
-
{ id: 5, name: 'Eve', age: 32, department: 'Engineering', salary: 95000, active: true }
|
|
5
|
+
{ id: 1, name: 'Alice', age: 30 },
|
|
6
|
+
{ id: 2, name: 'Bob', age: 25 },
|
|
7
|
+
{ id: 3, name: 'Carol', age: 35 },
|
|
10
8
|
];
|
|
11
9
|
|
|
12
10
|
let result: QueryResult<any>;
|
|
@@ -15,450 +13,58 @@ describe('QueryResult', () => {
|
|
|
15
13
|
result = createQueryResult(testData);
|
|
16
14
|
});
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
expect(result.length()).toBe(5);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('should identify empty results', () => {
|
|
25
|
-
const emptyResult = createQueryResult([]);
|
|
26
|
-
expect(emptyResult.isEmpty()).toBe(true);
|
|
27
|
-
expect(result.isEmpty()).toBe(false);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test('should return first and last items', () => {
|
|
31
|
-
expect(result.first()?.name).toBe('Alice');
|
|
32
|
-
expect(result.last()?.name).toBe('Eve');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('should get item by index', () => {
|
|
36
|
-
expect(result.get(0)?.name).toBe('Alice');
|
|
37
|
-
expect(result.get(2)?.name).toBe('Carol');
|
|
38
|
-
expect(result.get(10)).toBeUndefined();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test('should handle single item input', () => {
|
|
42
|
-
const singleResult = createQueryResult({ id: 1, name: 'Test' } as any);
|
|
43
|
-
expect(singleResult.len()).toBe(1);
|
|
44
|
-
expect(singleResult.first()?.name).toBe('Test');
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe('Iteration methods', () => {
|
|
49
|
-
test('any should work correctly', () => {
|
|
50
|
-
expect(result.any(item => item.age > 30)).toBe(true);
|
|
51
|
-
expect(result.any(item => item.age > 100)).toBe(false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('all should work correctly', () => {
|
|
55
|
-
expect(result.all(item => item.id > 0)).toBe(true);
|
|
56
|
-
expect(result.all(item => item.active)).toBe(false);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('find should return correct item', () => {
|
|
60
|
-
const found = result.find(item => item.name === 'Carol');
|
|
61
|
-
expect(found?.id).toBe(3);
|
|
62
|
-
expect(result.find(item => item.name === 'Unknown')).toBeUndefined();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('filter should return correct items', () => {
|
|
66
|
-
const activeUsers = result.filter(item => item.active);
|
|
67
|
-
expect(activeUsers).toHaveLength(4);
|
|
68
|
-
|
|
69
|
-
const engineers = result.filter(item => item.department === 'Engineering');
|
|
70
|
-
expect(engineers).toHaveLength(3);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('map should transform items correctly', () => {
|
|
74
|
-
const names = result.map(item => item.name);
|
|
75
|
-
expect(names).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
|
|
76
|
-
|
|
77
|
-
const ages = result.map(item => item.age);
|
|
78
|
-
expect(ages).toEqual([30, 25, 35, 28, 32]);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test('forEach should iterate over all items', () => {
|
|
82
|
-
const mockCallback = jest.fn();
|
|
83
|
-
result.forEach(mockCallback);
|
|
84
|
-
expect(mockCallback).toHaveBeenCalledTimes(5);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('reduce should accumulate correctly', () => {
|
|
88
|
-
const totalAge = result.reduce((sum, item) => sum + item.age, 0);
|
|
89
|
-
expect(totalAge).toBe(150);
|
|
90
|
-
|
|
91
|
-
const nameList = result.reduce((names, item) => names + item.name + ',', '');
|
|
92
|
-
expect(nameList).toBe('Alice,Bob,Carol,David,Eve,');
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe('Data extraction', () => {
|
|
97
|
-
test('pluck should extract field values', () => {
|
|
98
|
-
const names = result.pluck('name');
|
|
99
|
-
expect(names).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
|
|
100
|
-
|
|
101
|
-
const ages = result.pluck('age');
|
|
102
|
-
expect(ages).toEqual([30, 25, 35, 28, 32]);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('pluckStrings should extract and convert to strings', () => {
|
|
106
|
-
const ageStrings = result.pluckStrings('age');
|
|
107
|
-
expect(ageStrings).toEqual(['30', '25', '35', '28', '32']);
|
|
108
|
-
|
|
109
|
-
const names = result.pluckStrings('name');
|
|
110
|
-
expect(names).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('pluckNumbers should extract and convert to numbers', () => {
|
|
114
|
-
const ages = result.pluckNumbers('age');
|
|
115
|
-
expect(ages).toEqual([30, 25, 35, 28, 32]);
|
|
116
|
-
|
|
117
|
-
const salaries = result.pluckNumbers('salary');
|
|
118
|
-
expect(salaries).toEqual([75000, 65000, 85000, 55000, 95000]);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('pluckNumbers should handle non-numeric values', () => {
|
|
122
|
-
const testDataWithNulls = [
|
|
123
|
-
{ id: 1, score: 85 },
|
|
124
|
-
{ id: 2, score: null },
|
|
125
|
-
{ id: 3, score: '90' },
|
|
126
|
-
{ id: 4, score: 'invalid' }
|
|
127
|
-
];
|
|
128
|
-
|
|
129
|
-
const resultWithNulls = createQueryResult(testDataWithNulls);
|
|
130
|
-
const scores = resultWithNulls.pluckNumbers('score');
|
|
131
|
-
expect(scores).toEqual([85, null, 90, null]);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('Numeric summary', () => {
|
|
136
|
-
test('should calculate numeric summary correctly', () => {
|
|
137
|
-
const ageSummary = result.summarizeNumeric('age');
|
|
138
|
-
|
|
139
|
-
expect(ageSummary).not.toBeNull();
|
|
140
|
-
expect(ageSummary!.count).toBe(5);
|
|
141
|
-
expect(ageSummary!.sum).toBe(150);
|
|
142
|
-
expect(ageSummary!.mean).toBe(30);
|
|
143
|
-
expect(ageSummary!.min).toBe(25);
|
|
144
|
-
expect(ageSummary!.max).toBe(35);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('should calculate median correctly', () => {
|
|
148
|
-
const ageSummary = result.summarizeNumeric('age');
|
|
149
|
-
expect(ageSummary!.median).toBe(30); // [25, 28, 30, 32, 35]
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test('should calculate standard deviation correctly', () => {
|
|
153
|
-
const ageSummary = result.summarizeNumeric('age');
|
|
154
|
-
expect(ageSummary!.standardDeviation).toBeCloseTo(3.74, 2);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test('should return null for non-numeric fields', () => {
|
|
158
|
-
const nameSummary = result.summarizeNumeric('name');
|
|
159
|
-
expect(nameSummary).toBeNull();
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test('should handle empty data', () => {
|
|
163
|
-
const emptyResult = createQueryResult([]);
|
|
164
|
-
const summary = emptyResult.summarizeNumeric('age');
|
|
165
|
-
expect(summary).toBeNull();
|
|
166
|
-
});
|
|
16
|
+
test('len() and length() return correct count', () => {
|
|
17
|
+
expect(result.len()).toBe(3);
|
|
18
|
+
expect(result.length()).toBe(3);
|
|
167
19
|
});
|
|
168
20
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
expect(byDepartment['Engineering']).toHaveLength(3);
|
|
174
|
-
expect(byDepartment['Design']).toHaveLength(1);
|
|
175
|
-
expect(byDepartment['Marketing']).toHaveLength(1);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
test('countBy should count values correctly', () => {
|
|
179
|
-
const departmentCounts = result.countBy('department');
|
|
180
|
-
|
|
181
|
-
expect(departmentCounts['Engineering']).toBe(3);
|
|
182
|
-
expect(departmentCounts['Design']).toBe(1);
|
|
183
|
-
expect(departmentCounts['Marketing']).toBe(1);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test('count with predicate should work correctly', () => {
|
|
187
|
-
const activeCount = result.count(item => item.active);
|
|
188
|
-
expect(activeCount).toBe(4);
|
|
189
|
-
|
|
190
|
-
const engineerCount = result.count(item => item.department === 'Engineering');
|
|
191
|
-
expect(engineerCount).toBe(3);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test('count without predicate should return total count', () => {
|
|
195
|
-
expect(result.count()).toBe(5);
|
|
196
|
-
});
|
|
21
|
+
test('isEmpty() works correctly', () => {
|
|
22
|
+
expect(createQueryResult([]).isEmpty()).toBe(true);
|
|
23
|
+
expect(result.isEmpty()).toBe(false);
|
|
197
24
|
});
|
|
198
25
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const ages = sortedByAge.map(item => item.age);
|
|
203
|
-
expect(ages).toEqual([25, 28, 30, 32, 35]);
|
|
204
|
-
|
|
205
|
-
const sortedByName = result.sortBy('name');
|
|
206
|
-
const names = sortedByName.map(item => item.name);
|
|
207
|
-
expect(names).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
test('sortBy should handle descending order', () => {
|
|
211
|
-
const sortedByAgeDesc = result.sortBy('age', false);
|
|
212
|
-
const ages = sortedByAgeDesc.map(item => item.age);
|
|
213
|
-
expect(ages).toEqual([35, 32, 30, 28, 25]);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
test('sortByMultiple should handle multiple sort fields', () => {
|
|
217
|
-
const sorted = result.sortByMultiple([
|
|
218
|
-
{ field: 'department', ascending: true },
|
|
219
|
-
{ field: 'salary', ascending: false }
|
|
220
|
-
]);
|
|
221
|
-
|
|
222
|
-
// Should sort by department first, then by salary descending
|
|
223
|
-
expect(sorted[0].department).toBe('Design');
|
|
224
|
-
expect(sorted[1].department).toBe('Engineering');
|
|
225
|
-
expect(sorted[1].salary).toBe(95000); // Highest paid engineer first
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test('sortBy should handle null values', () => {
|
|
229
|
-
const dataWithNulls = [
|
|
230
|
-
{ id: 1, score: 10 },
|
|
231
|
-
{ id: 2, score: null },
|
|
232
|
-
{ id: 3, score: 5 }
|
|
233
|
-
];
|
|
234
|
-
|
|
235
|
-
const resultWithNulls = createQueryResult(dataWithNulls);
|
|
236
|
-
const sorted = resultWithNulls.sortBy('score');
|
|
237
|
-
|
|
238
|
-
// Null values should sort first in ascending order
|
|
239
|
-
expect(sorted[0].score).toBeNull();
|
|
240
|
-
expect(sorted[1].score).toBe(5);
|
|
241
|
-
expect(sorted[2].score).toBe(10);
|
|
242
|
-
});
|
|
26
|
+
test('first() and last() return correct items', () => {
|
|
27
|
+
expect(result.first()?.name).toBe('Alice');
|
|
28
|
+
expect(result.last()?.name).toBe('Carol');
|
|
243
29
|
});
|
|
244
30
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
expect(page1[0].name).toBe('Alice');
|
|
250
|
-
expect(page1[1].name).toBe('Bob');
|
|
251
|
-
|
|
252
|
-
const page2 = result.paginate(2, 2);
|
|
253
|
-
expect(page2).toHaveLength(2);
|
|
254
|
-
expect(page2[0].name).toBe('Carol');
|
|
255
|
-
expect(page2[1].name).toBe('David');
|
|
256
|
-
|
|
257
|
-
const page3 = result.paginate(3, 2);
|
|
258
|
-
expect(page3).toHaveLength(1);
|
|
259
|
-
expect(page3[0].name).toBe('Eve');
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
test('chunk should split data into chunks', () => {
|
|
263
|
-
const chunks = result.chunk(2);
|
|
264
|
-
expect(chunks).toHaveLength(3);
|
|
265
|
-
expect(chunks[0]).toHaveLength(2);
|
|
266
|
-
expect(chunks[1]).toHaveLength(2);
|
|
267
|
-
expect(chunks[2]).toHaveLength(1);
|
|
268
|
-
|
|
269
|
-
expect(chunks[0][0].name).toBe('Alice');
|
|
270
|
-
expect(chunks[2][0].name).toBe('Eve');
|
|
271
|
-
});
|
|
31
|
+
test('first() and last() return undefined on empty result', () => {
|
|
32
|
+
const empty = createQueryResult([]);
|
|
33
|
+
expect(empty.first()).toBeUndefined();
|
|
34
|
+
expect(empty.last()).toBeUndefined();
|
|
272
35
|
});
|
|
273
36
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
expect(departments).toEqual(['Engineering', 'Design', 'Marketing']);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
test('unique should remove duplicates', () => {
|
|
282
|
-
const duplicateData = [1, 2, 2, 3, 3, 3, 4];
|
|
283
|
-
const duplicateResult = createQueryResult(duplicateData);
|
|
284
|
-
const unique = duplicateResult.unique();
|
|
285
|
-
expect(unique).toEqual([1, 2, 3, 4]);
|
|
286
|
-
});
|
|
37
|
+
test('get(index) returns correct item or undefined', () => {
|
|
38
|
+
expect(result.get(0)?.name).toBe('Alice');
|
|
39
|
+
expect(result.get(2)?.name).toBe('Carol');
|
|
40
|
+
expect(result.get(99)).toBeUndefined();
|
|
287
41
|
});
|
|
288
42
|
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const joined = result.innerJoin(departments, 'department', 'id');
|
|
298
|
-
|
|
299
|
-
expect(joined).toHaveLength(5);
|
|
300
|
-
expect(joined[0].budget).toBe(100000); // Alice from Engineering
|
|
301
|
-
expect(joined[1].budget).toBe(75000); // Bob from Design
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
test('leftJoin should include all left records', () => {
|
|
305
|
-
const leftJoined = result.leftJoin(departments, 'department', 'id');
|
|
306
|
-
|
|
307
|
-
expect(leftJoined).toHaveLength(5);
|
|
308
|
-
// All original records should be present
|
|
309
|
-
expect(leftJoined.map(r => r.name)).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
|
|
310
|
-
});
|
|
43
|
+
test('toArray() returns a copy of the data', () => {
|
|
44
|
+
const arr = result.toArray();
|
|
45
|
+
expect(Array.isArray(arr)).toBe(true);
|
|
46
|
+
expect(arr).toHaveLength(3);
|
|
47
|
+
expect(arr[0]).toEqual(testData[0]);
|
|
48
|
+
// Ensure it's a copy, not the same reference
|
|
49
|
+
arr.push({ id: 99, name: 'Extra', age: 0 });
|
|
50
|
+
expect(result.len()).toBe(3);
|
|
311
51
|
});
|
|
312
52
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
expect(Array.isArray(array)).toBe(true);
|
|
317
|
-
expect(array).toHaveLength(5);
|
|
318
|
-
expect(array[0]).toEqual(testData[0]);
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
test('toJSON should return JSON string', () => {
|
|
322
|
-
const json = result.toJSON();
|
|
323
|
-
const parsed = JSON.parse(json);
|
|
324
|
-
expect(parsed).toEqual(testData);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
test('toCsv should generate CSV format', () => {
|
|
328
|
-
const csv = result.toCsv();
|
|
329
|
-
const lines = csv.split('\n');
|
|
330
|
-
|
|
331
|
-
expect(lines.length).toBe(6); // Header + 5 data rows
|
|
332
|
-
expect(lines[0]).toContain('id,name,age,department,salary,active');
|
|
333
|
-
expect(lines[1]).toContain('1,Alice,30,Engineering,75000,true');
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
test('toCsv should handle custom delimiter', () => {
|
|
337
|
-
const tsv = result.toCsv('\t');
|
|
338
|
-
expect(tsv).toContain('\t');
|
|
339
|
-
expect(tsv.split('\n')[0]).toContain('id\tname\tage');
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
test('toCsv should escape values with delimiters', () => {
|
|
343
|
-
const dataWithCommas = [
|
|
344
|
-
{ name: 'Smith, John', description: 'A person, who works' }
|
|
345
|
-
];
|
|
346
|
-
const resultWithCommas = createQueryResult(dataWithCommas);
|
|
347
|
-
const csv = resultWithCommas.toCsv();
|
|
348
|
-
|
|
349
|
-
expect(csv).toContain('"Smith, John"');
|
|
350
|
-
expect(csv).toContain('"A person, who works"');
|
|
351
|
-
});
|
|
53
|
+
test('toJSON() returns valid JSON string of the data', () => {
|
|
54
|
+
const json = result.toJSON();
|
|
55
|
+
expect(JSON.parse(json)).toEqual(testData);
|
|
352
56
|
});
|
|
353
57
|
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
profile: {
|
|
360
|
-
name: 'Alice',
|
|
361
|
-
age: 30
|
|
362
|
-
},
|
|
363
|
-
settings: {
|
|
364
|
-
theme: 'dark'
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
id: 2,
|
|
370
|
-
user: {
|
|
371
|
-
profile: {
|
|
372
|
-
name: 'Bob',
|
|
373
|
-
age: 25
|
|
374
|
-
},
|
|
375
|
-
settings: {
|
|
376
|
-
theme: 'light'
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
];
|
|
381
|
-
|
|
382
|
-
test('should handle nested field plucking', () => {
|
|
383
|
-
const nestedResult = createQueryResult(nestedData);
|
|
384
|
-
|
|
385
|
-
const names = nestedResult.pluck('user.profile.name');
|
|
386
|
-
expect(names).toEqual(['Alice', 'Bob']);
|
|
387
|
-
|
|
388
|
-
const ages = nestedResult.pluck('user.profile.age');
|
|
389
|
-
expect(ages).toEqual([30, 25]);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
test('should handle nested field sorting', () => {
|
|
393
|
-
const nestedResult = createQueryResult(nestedData);
|
|
394
|
-
const sorted = nestedResult.sortBy('user.profile.age');
|
|
395
|
-
|
|
396
|
-
expect(sorted[0].user.profile.name).toBe('Bob');
|
|
397
|
-
expect(sorted[1].user.profile.name).toBe('Alice');
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
test('should handle nested field grouping', () => {
|
|
401
|
-
const nestedResult = createQueryResult(nestedData);
|
|
402
|
-
const grouped = nestedResult.groupBy('user.settings.theme');
|
|
403
|
-
|
|
404
|
-
expect(grouped['dark']).toHaveLength(1);
|
|
405
|
-
expect(grouped['light']).toHaveLength(1);
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
test('should handle missing nested fields gracefully', () => {
|
|
409
|
-
const dataWithMissing = [
|
|
410
|
-
{ id: 1, user: { profile: { name: 'Alice' } } },
|
|
411
|
-
{ id: 2, user: { name: 'Bob' } }, // Missing profile
|
|
412
|
-
{ id: 3 } // Missing user entirely
|
|
413
|
-
];
|
|
414
|
-
|
|
415
|
-
const resultWithMissing = createQueryResult(dataWithMissing);
|
|
416
|
-
const names = resultWithMissing.pluck('user.profile.name');
|
|
417
|
-
|
|
418
|
-
expect(names).toEqual(['Alice', undefined, undefined]);
|
|
419
|
-
});
|
|
58
|
+
test('cast() returns a new QueryResult with the same data', () => {
|
|
59
|
+
const casted = result.cast<{ id: number; name: string }>();
|
|
60
|
+
expect(casted).toBeInstanceOf(QueryResult);
|
|
61
|
+
expect(casted.len()).toBe(3);
|
|
62
|
+
expect(casted.first()?.name).toBe('Alice');
|
|
420
63
|
});
|
|
421
64
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
expect(casted.len()).toBe(5);
|
|
427
|
-
});
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
describe('Debugging and inspection', () => {
|
|
431
|
-
test('inspect should not throw error', () => {
|
|
432
|
-
// Mock console.log to prevent output during tests
|
|
433
|
-
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
434
|
-
|
|
435
|
-
result.inspect();
|
|
436
|
-
expect(consoleSpy).toHaveBeenCalled();
|
|
437
|
-
|
|
438
|
-
result.inspect(2); // Limited output
|
|
439
|
-
expect(consoleSpy).toHaveBeenCalledTimes(2);
|
|
440
|
-
|
|
441
|
-
consoleSpy.mockRestore();
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
describe('Edge cases', () => {
|
|
446
|
-
test('should handle empty arrays gracefully', () => {
|
|
447
|
-
const emptyResult = createQueryResult([]);
|
|
448
|
-
|
|
449
|
-
expect(emptyResult.len()).toBe(0);
|
|
450
|
-
expect(emptyResult.isEmpty()).toBe(true);
|
|
451
|
-
expect(emptyResult.first()).toBeUndefined();
|
|
452
|
-
expect(emptyResult.summarizeNumeric('age')).toBeNull();
|
|
453
|
-
expect(emptyResult.groupBy('department')).toEqual({});
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
test('should handle invalid field names', () => {
|
|
457
|
-
const invalidSummary = result.summarizeNumeric('nonexistent');
|
|
458
|
-
expect(invalidSummary).toBeNull();
|
|
459
|
-
|
|
460
|
-
const invalidPluck = result.pluck('nonexistent');
|
|
461
|
-
expect(invalidPluck.every(val => val === undefined)).toBe(true);
|
|
462
|
-
});
|
|
65
|
+
test('constructor wraps a single non-array item in an array', () => {
|
|
66
|
+
const single = createQueryResult({ id: 1, name: 'Test' } as any);
|
|
67
|
+
expect(single.len()).toBe(1);
|
|
68
|
+
expect((single.first() as any)?.name).toBe('Test');
|
|
463
69
|
});
|
|
464
|
-
});
|
|
70
|
+
});
|