@onchaindb/sdk 0.4.5 → 2.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.
Files changed (117) hide show
  1. package/.claude/settings.local.json +10 -2
  2. package/README.md +422 -355
  3. package/dist/batch.d.ts +1 -10
  4. package/dist/batch.d.ts.map +1 -1
  5. package/dist/batch.js +4 -26
  6. package/dist/batch.js.map +1 -1
  7. package/dist/client.d.ts +31 -46
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/client.js +222 -357
  10. package/dist/client.js.map +1 -1
  11. package/dist/database.d.ts +14 -131
  12. package/dist/database.d.ts.map +1 -1
  13. package/dist/database.js +35 -131
  14. package/dist/database.js.map +1 -1
  15. package/dist/index.d.ts +10 -13
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +4 -18
  18. package/dist/index.js.map +1 -1
  19. package/dist/query-sdk/ConditionBuilder.d.ts +3 -11
  20. package/dist/query-sdk/ConditionBuilder.d.ts.map +1 -1
  21. package/dist/query-sdk/ConditionBuilder.js +10 -48
  22. package/dist/query-sdk/ConditionBuilder.js.map +1 -1
  23. package/dist/query-sdk/NestedBuilders.d.ts +33 -30
  24. package/dist/query-sdk/NestedBuilders.d.ts.map +1 -1
  25. package/dist/query-sdk/NestedBuilders.js +46 -43
  26. package/dist/query-sdk/NestedBuilders.js.map +1 -1
  27. package/{src/query-sdk/dist/OnChainDB.d.ts → dist/query-sdk/OnDB.d.ts} +10 -2
  28. package/dist/query-sdk/OnDB.d.ts.map +1 -0
  29. package/{src/query-sdk/dist/OnChainDB.js → dist/query-sdk/OnDB.js} +86 -18
  30. package/dist/query-sdk/OnDB.js.map +1 -0
  31. package/dist/query-sdk/QueryBuilder.d.ts +4 -2
  32. package/dist/query-sdk/QueryBuilder.d.ts.map +1 -1
  33. package/dist/query-sdk/QueryBuilder.js +47 -169
  34. package/dist/query-sdk/QueryBuilder.js.map +1 -1
  35. package/dist/query-sdk/QueryResult.d.ts +0 -38
  36. package/dist/query-sdk/QueryResult.d.ts.map +1 -1
  37. package/dist/query-sdk/QueryResult.js +1 -227
  38. package/dist/query-sdk/QueryResult.js.map +1 -1
  39. package/dist/query-sdk/index.d.ts +2 -2
  40. package/dist/query-sdk/index.d.ts.map +1 -1
  41. package/dist/query-sdk/index.js +3 -3
  42. package/dist/query-sdk/index.js.map +1 -1
  43. package/dist/query-sdk/operators.d.ts +32 -28
  44. package/dist/query-sdk/operators.d.ts.map +1 -1
  45. package/dist/query-sdk/operators.js +45 -155
  46. package/dist/query-sdk/operators.js.map +1 -1
  47. package/dist/types.d.ts +159 -36
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/types.js +8 -8
  50. package/dist/types.js.map +1 -1
  51. package/dist/x402/types.d.ts +1 -1
  52. package/dist/x402/types.d.ts.map +1 -1
  53. package/dist/x402/utils.js +2 -2
  54. package/dist/x402/utils.js.map +1 -1
  55. package/jest.config.js +4 -0
  56. package/package.json +1 -1
  57. package/skills.md +0 -1
  58. package/src/batch.d.ts +3 -3
  59. package/src/batch.js +1 -1
  60. package/src/client.ts +287 -823
  61. package/src/database.d.ts +1 -1
  62. package/src/database.js +4 -4
  63. package/src/database.ts +71 -494
  64. package/src/index.d.ts +18 -18
  65. package/src/index.js +16 -16
  66. package/src/index.ts +44 -198
  67. package/src/query-sdk/ConditionBuilder.ts +37 -89
  68. package/src/query-sdk/NestedBuilders.ts +90 -92
  69. package/src/query-sdk/{OnChainDB.ts → OnDB.ts} +1 -1
  70. package/src/query-sdk/QueryBuilder.ts +59 -218
  71. package/src/query-sdk/QueryResult.ts +4 -330
  72. package/src/query-sdk/README.md +218 -587
  73. package/src/query-sdk/index.ts +2 -2
  74. package/src/query-sdk/operators.ts +91 -200
  75. package/src/query-sdk/tests/FieldConditionBuilder.test.ts +70 -71
  76. package/src/query-sdk/tests/LogicalOperator.test.ts +43 -82
  77. package/src/query-sdk/tests/NestedBuilders.test.ts +229 -309
  78. package/src/query-sdk/tests/QueryBuilder.test.ts +5 -5
  79. package/src/query-sdk/tests/QueryResult.test.ts +41 -435
  80. package/src/query-sdk/tests/comprehensive.test.ts +4 -185
  81. package/src/tests/client-requests.test.ts +280 -0
  82. package/src/tests/client-validation.test.ts +80 -0
  83. package/src/types.d.ts +6 -6
  84. package/src/types.js +8 -8
  85. package/src/types.ts +239 -54
  86. package/src/x402/types.ts +3 -3
  87. package/src/x402/utils.ts +3 -3
  88. package/examples/blob-upload-example.ts +0 -140
  89. package/src/batch.ts +0 -257
  90. package/src/query-sdk/dist/ConditionBuilder.d.ts +0 -22
  91. package/src/query-sdk/dist/ConditionBuilder.js +0 -90
  92. package/src/query-sdk/dist/FieldConditionBuilder.d.ts +0 -1
  93. package/src/query-sdk/dist/FieldConditionBuilder.js +0 -6
  94. package/src/query-sdk/dist/NestedBuilders.d.ts +0 -43
  95. package/src/query-sdk/dist/NestedBuilders.js +0 -144
  96. package/src/query-sdk/dist/QueryBuilder.d.ts +0 -70
  97. package/src/query-sdk/dist/QueryBuilder.js +0 -295
  98. package/src/query-sdk/dist/QueryResult.d.ts +0 -52
  99. package/src/query-sdk/dist/QueryResult.js +0 -293
  100. package/src/query-sdk/dist/SelectionBuilder.d.ts +0 -20
  101. package/src/query-sdk/dist/SelectionBuilder.js +0 -80
  102. package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +0 -27
  103. package/src/query-sdk/dist/adapters/HttpClientAdapter.js +0 -170
  104. package/src/query-sdk/dist/index.d.ts +0 -36
  105. package/src/query-sdk/dist/index.js +0 -27
  106. package/src/query-sdk/dist/operators.d.ts +0 -56
  107. package/src/query-sdk/dist/operators.js +0 -289
  108. package/src/query-sdk/dist/tests/setup.d.ts +0 -15
  109. package/src/query-sdk/dist/tests/setup.js +0 -46
  110. package/src/query-sdk/jest.config.js +0 -25
  111. package/src/query-sdk/package.json +0 -46
  112. package/src/query-sdk/tests/aggregations.test.ts +0 -653
  113. package/src/query-sdk/tests/integration.test.ts +0 -608
  114. package/src/query-sdk/tests/operators.test.ts +0 -327
  115. package/src/query-sdk/tests/unit.test.ts +0 -794
  116. package/src/query-sdk/tsconfig.json +0 -26
  117. package/src/query-sdk/yarn.lock +0 -3092
@@ -1,794 +0,0 @@
1
- // Comprehensive unit tests for TypeScript SDK
2
- import {
3
- FieldConditionBuilder,
4
- LogicalOperator,
5
- ConditionBuilder,
6
- NestedConditionBuilder,
7
- NestedFieldConditionBuilder,
8
- QueryBuilder,
9
- QueryResult,
10
- createQueryResult
11
- } from '../index';
12
-
13
- // Test framework helpers
14
- interface TestCase {
15
- name: string;
16
- test: () => void;
17
- }
18
-
19
- class TestRunner {
20
- private tests: TestCase[] = [];
21
- private passed = 0;
22
- private failed = 0;
23
-
24
- add(name: string, test: () => void) {
25
- this.tests.push({ name, test });
26
- }
27
-
28
- run() {
29
- console.log(`🧪 Running ${this.tests.length} unit tests...\n`);
30
-
31
- for (const testCase of this.tests) {
32
- try {
33
- testCase.test();
34
- console.log(`✅ ${testCase.name}`);
35
- this.passed++;
36
- } catch (error) {
37
- console.error(`❌ ${testCase.name}: ${error instanceof Error ? error.message : error}`);
38
- this.failed++;
39
- }
40
- }
41
-
42
- console.log(`\n📊 Test Results: ${this.passed} passed, ${this.failed} failed`);
43
- if (this.failed > 0) {
44
- process.exit(1);
45
- }
46
- }
47
- }
48
-
49
- // Assertion helpers
50
- function assertEqual<T>(actual: T, expected: T, message?: string) {
51
- const actualStr = JSON.stringify(actual, null, 2);
52
- const expectedStr = JSON.stringify(expected, null, 2);
53
- if (actualStr !== expectedStr) {
54
- throw new Error(`${message || 'Values not equal'}\nExpected: ${expectedStr}\nActual: ${actualStr}`);
55
- }
56
- }
57
-
58
- function assertTrue(condition: boolean, message?: string) {
59
- if (!condition) {
60
- throw new Error(message || 'Condition is false');
61
- }
62
- }
63
-
64
- function assertFalse(condition: boolean, message?: string) {
65
- if (condition) {
66
- throw new Error(message || 'Condition is true');
67
- }
68
- }
69
-
70
- function assertThrows(fn: () => void, message?: string) {
71
- try {
72
- fn();
73
- throw new Error(message || 'Expected function to throw');
74
- } catch (error) {
75
- // Expected - function threw as required
76
- }
77
- }
78
-
79
- // Initialize test runner
80
- const runner = new TestRunner();
81
-
82
- // ===== FIELD CONDITION BUILDER TESTS =====
83
-
84
- runner.add('FieldConditionBuilder - Basic operators', () => {
85
- const builder = new FieldConditionBuilder('testField');
86
-
87
- assertEqual(builder.equals('value').operator, 'is');
88
- assertEqual(builder.notEquals('value').operator, 'isNot');
89
- assertEqual(builder.greaterThan(10).operator, 'greaterThan');
90
- assertEqual(builder.lessThan(10).operator, 'lessThan');
91
- assertEqual(builder.greaterThanOrEqual(10).operator, 'greaterThanOrEqual');
92
- assertEqual(builder.lessThanOrEqual(10).operator, 'lessThanOrEqual');
93
- });
94
-
95
- runner.add('FieldConditionBuilder - String operators', () => {
96
- const builder = new FieldConditionBuilder('text');
97
-
98
- assertEqual(builder.contains('sub').operator, 'includes');
99
- assertEqual(builder.startsWith('pre').operator, 'startsWith');
100
- assertEqual(builder.endsWith('suf').operator, 'endsWith');
101
- assertEqual(builder.regExpMatches('.*test.*').operator, 'regExpMatches');
102
- assertEqual(builder.includesCaseInsensitive('TEST').operator, 'includesCaseInsensitive');
103
- });
104
-
105
- runner.add('FieldConditionBuilder - Array operators', () => {
106
- const builder = new FieldConditionBuilder('items');
107
-
108
- const inCondition = builder.in([1, 2, 3]);
109
- assertEqual(inCondition.operator, 'in');
110
- assertEqual(inCondition.value, [1, 2, 3]);
111
-
112
- const notInCondition = builder.notIn(['a', 'b']);
113
- assertEqual(notInCondition.operator, 'notIn');
114
- assertEqual(notInCondition.value, ['a', 'b']);
115
- });
116
-
117
- runner.add('FieldConditionBuilder - Null/existence operators', () => {
118
- const builder = new FieldConditionBuilder('optional');
119
-
120
- assertEqual(builder.exists().value, true);
121
- assertEqual(builder.notExists().value, false);
122
- assertEqual(builder.isNull().value, true);
123
- assertEqual(builder.isNotNull().value, false);
124
- });
125
-
126
- runner.add('FieldConditionBuilder - Numeric operators', () => {
127
- const builder = new FieldConditionBuilder('score');
128
-
129
- const betweenCondition = builder.between(10, 20);
130
- assertEqual(betweenCondition.operator, 'between');
131
- assertEqual(betweenCondition.value, { min: 10, max: 20 });
132
-
133
- // Test additional real operators
134
- const regexCondition = builder.regExpMatches('^test.*');
135
- assertEqual(regexCondition.operator, 'regExpMatches');
136
- });
137
-
138
- runner.add('FieldConditionBuilder - String operators', () => {
139
- const builder = new FieldConditionBuilder('text');
140
-
141
- assertEqual(builder.contains('test').operator, 'includes');
142
- assertEqual(builder.startsWith('prefix').operator, 'startsWith');
143
- assertEqual(builder.endsWith('suffix').operator, 'endsWith');
144
- assertEqual(builder.regExpMatches('^test.*').operator, 'regExpMatches');
145
- assertEqual(builder.includesCaseInsensitive('Test').operator, 'includesCaseInsensitive');
146
- assertEqual(builder.startsWithCaseInsensitive('Test').operator, 'startsWithCaseInsensitive');
147
- assertEqual(builder.endsWithCaseInsensitive('Test').operator, 'endsWithCaseInsensitive');
148
- });
149
-
150
- runner.add('FieldConditionBuilder - IP address operators', () => {
151
- const builder = new FieldConditionBuilder('ipAddress');
152
-
153
- assertEqual(builder.isLocalIp().operator, 'isLocalIp');
154
- assertEqual(builder.isExternalIp().operator, 'isExternalIp');
155
- assertEqual(builder.inCountry('US').operator, 'inCountry');
156
- assertEqual(builder.cidr('192.168.1.0/24').operator, 'CIDR');
157
- assertEqual(builder.b64('dGVzdA==').operator, 'b64');
158
- assertEqual(builder.inDataset('malware_ips').operator, 'inDataset');
159
- });
160
-
161
- runner.add('FieldConditionBuilder - Misc operators', () => {
162
- const builder = new FieldConditionBuilder('data');
163
-
164
- const b64Condition = builder.b64('dGVzdA==');
165
- assertEqual(b64Condition.operator, 'b64');
166
- assertEqual(b64Condition.value, 'dGVzdA==');
167
-
168
- const countryCondition = builder.inCountry('US');
169
- assertEqual(countryCondition.operator, 'inCountry');
170
- assertEqual(countryCondition.value, 'US');
171
-
172
- const datasetCondition = builder.inDataset('malware_ips');
173
- assertEqual(datasetCondition.operator, 'inDataset');
174
- assertEqual(datasetCondition.value, 'malware_ips');
175
-
176
- const cidrCondition = builder.cidr('192.168.1.0/24');
177
- assertEqual(cidrCondition.operator, 'CIDR');
178
- assertEqual(cidrCondition.value, '192.168.1.0/24');
179
- });
180
-
181
- runner.add('FieldConditionBuilder - Boolean operators', () => {
182
- const builder = new FieldConditionBuilder('isActive');
183
-
184
- const trueCondition = builder.isTrue();
185
- assertEqual(trueCondition.operator, 'is');
186
- assertEqual(trueCondition.value, true);
187
-
188
- const falseCondition = builder.isFalse();
189
- assertEqual(falseCondition.operator, 'is');
190
- assertEqual(falseCondition.value, false);
191
- });
192
-
193
- runner.add('FieldConditionBuilder - Number operators', () => {
194
- const builder = new FieldConditionBuilder('amount');
195
-
196
- assertEqual(builder.greaterThan(100).operator, 'greaterThan');
197
- assertEqual(builder.lessThan(500).operator, 'lessThan');
198
- assertEqual(builder.greaterThanOrEqual(100).operator, 'greaterThanOrEqual');
199
- assertEqual(builder.lessThanOrEqual(500).operator, 'lessThanOrEqual');
200
- assertEqual(builder.between(100, 500).operator, 'betweenOp');
201
- });
202
-
203
- runner.add('FieldConditionBuilder - Base operators', () => {
204
- const builder = new FieldConditionBuilder('value');
205
-
206
- assertEqual(builder.equals('test').operator, 'is');
207
- assertEqual(builder.notEquals('test').operator, 'isNot');
208
- assertEqual(builder.in(['a', 'b', 'c']).operator, 'in');
209
- assertEqual(builder.notIn(['x', 'y', 'z']).operator, 'notIn');
210
- assertEqual(builder.isNull().operator, 'isNull');
211
- assertEqual(builder.isNotNull().operator, 'isNull');
212
- assertEqual(builder.exists().operator, 'exists');
213
- assertEqual(builder.notExists().operator, 'exists');
214
- // Only test boolean convenience methods that use base operators
215
- assertEqual(builder.isTrue().operator, 'is');
216
- assertEqual(builder.isFalse().operator, 'is');
217
- });
218
-
219
- runner.add('FieldConditionBuilder - Boolean convenience methods', () => {
220
- const builder = new FieldConditionBuilder('flag');
221
-
222
- const trueCondition = builder.isTrue();
223
- assertEqual(trueCondition.operator, 'is');
224
- assertEqual(trueCondition.value, true);
225
-
226
- const falseCondition = builder.isFalse();
227
- assertEqual(falseCondition.operator, 'is');
228
- assertEqual(falseCondition.value, false);
229
- });
230
-
231
- // ===== LOGICAL OPERATOR TESTS =====
232
-
233
- runner.add('LogicalOperator - Single condition', () => {
234
- const condition = new FieldConditionBuilder('name').equals('John');
235
- const logicalOp = LogicalOperator.Condition(condition);
236
-
237
- assertEqual(logicalOp.type, 'condition');
238
- assertEqual(logicalOp.condition?.field, 'name');
239
- assertEqual(logicalOp.condition?.operator, 'is');
240
- assertEqual(logicalOp.condition?.value, 'John');
241
- });
242
-
243
- runner.add('LogicalOperator - AND operation', () => {
244
- const condition1 = LogicalOperator.Condition(new FieldConditionBuilder('name').equals('John'));
245
- const condition2 = LogicalOperator.Condition(new FieldConditionBuilder('age').greaterThan(25));
246
-
247
- const andOp = LogicalOperator.And([condition1, condition2]);
248
- assertEqual(andOp.type, 'and');
249
- assertEqual(andOp.conditions.length, 2);
250
- });
251
-
252
- runner.add('LogicalOperator - OR operation', () => {
253
- const condition1 = LogicalOperator.Condition(new FieldConditionBuilder('status').equals('active'));
254
- const condition2 = LogicalOperator.Condition(new FieldConditionBuilder('status').equals('pending'));
255
-
256
- const orOp = LogicalOperator.Or([condition1, condition2]);
257
- assertEqual(orOp.type, 'or');
258
- assertEqual(orOp.conditions.length, 2);
259
- });
260
-
261
- runner.add('LogicalOperator - NOT operation', () => {
262
- const condition = LogicalOperator.Condition(new FieldConditionBuilder('deleted').isTrue());
263
- const notOp = LogicalOperator.Not([condition]);
264
-
265
- assertEqual(notOp.type, 'not');
266
- assertEqual(notOp.conditions.length, 1);
267
- });
268
-
269
- // ===== DOT NOTATION TESTS =====
270
-
271
- runner.add('createNestedQuery - Simple field', () => {
272
- const result = createNestedQuery('name', 'is', 'John');
273
- assertEqual(result, { name: { is: 'John' } });
274
- });
275
-
276
- runner.add('createNestedQuery - Nested field', () => {
277
- const result = createNestedQuery('user.profile.name', 'is', 'John');
278
- const expected = {
279
- user: {
280
- profile: {
281
- name: {
282
- is: 'John'
283
- }
284
- }
285
- }
286
- };
287
- assertEqual(result, expected);
288
- });
289
-
290
- runner.add('createNestedQuery - Deep nesting', () => {
291
- const result = createNestedQuery('a.b.c.d.e', 'contains', 'test');
292
- const expected = {
293
- a: {
294
- b: {
295
- c: {
296
- d: {
297
- e: {
298
- contains: 'test'
299
- }
300
- }
301
- }
302
- }
303
- }
304
- };
305
- assertEqual(result, expected);
306
- });
307
-
308
- runner.add('LogicalOperator.toComposable - Dot notation integration', () => {
309
- const condition = new FieldConditionBuilder('user.profile.bio').equals('Developer');
310
- const logicalOp = LogicalOperator.Condition(condition);
311
- const composable = logicalOp.toComposable();
312
-
313
- const expected = {
314
- user: {
315
- profile: {
316
- bio: {
317
- is: 'Developer'
318
- }
319
- }
320
- }
321
- };
322
- assertEqual(composable, expected);
323
- });
324
-
325
- // ===== NESTED BUILDER TESTS =====
326
-
327
- runner.add('NestedConditionBuilder - Basic usage', () => {
328
- const nestedBuilder = new NestedConditionBuilder('user');
329
- const fieldBuilder = nestedBuilder.field('name');
330
-
331
- assertTrue(fieldBuilder instanceof NestedFieldConditionBuilder);
332
- });
333
-
334
- runner.add('NestedFieldConditionBuilder - Path construction', () => {
335
- const nestedBuilder = new NestedConditionBuilder('user');
336
- const condition = nestedBuilder.field('profile').field('bio').equals('Developer');
337
-
338
- assertEqual(condition.field, 'user.profile.bio');
339
- assertEqual(condition.operator, 'is');
340
- assertEqual(condition.value, 'Developer');
341
- });
342
-
343
- runner.add('NestedFieldConditionBuilder - All operators available', () => {
344
- const nestedBuilder = new NestedConditionBuilder('user');
345
- const fieldBuilder = nestedBuilder.field('data');
346
-
347
- // Test a few key operators to ensure they work
348
- assertEqual(fieldBuilder.contains('test').operator, 'includes');
349
- assertEqual(fieldBuilder.greaterThan(10).operator, 'greaterThan');
350
- assertEqual(fieldBuilder.isNull().operator, 'isNull');
351
- assertEqual(fieldBuilder.dateToday().operator, 'dateToday');
352
- });
353
-
354
- runner.add('NestedConditionBuilder - Logical groups', () => {
355
- const nestedBuilder = new NestedConditionBuilder('user');
356
-
357
- const andGroup = nestedBuilder.andGroup(builder => [
358
- LogicalOperator.Condition(builder.field('name').equals('John')),
359
- LogicalOperator.Condition(builder.field('age').greaterThan(25))
360
- ]);
361
-
362
- assertEqual(andGroup.type, 'and');
363
- assertEqual(andGroup.conditions.length, 2);
364
- });
365
-
366
- // ===== CONDITION BUILDER TESTS =====
367
-
368
- runner.add('ConditionBuilder - Basic functionality', () => {
369
- const builder = new ConditionBuilder();
370
-
371
- assertTrue(builder.isEmpty());
372
- assertEqual(builder.getConditionCount(), 0);
373
- });
374
-
375
- runner.add('ConditionBuilder - Adding conditions', () => {
376
- const builder = new ConditionBuilder();
377
- const condition = LogicalOperator.Condition(new FieldConditionBuilder('name').equals('John'));
378
-
379
- builder.addCondition(condition);
380
- assertEqual(builder.getConditionCount(), 1);
381
- assertFalse(builder.isEmpty());
382
- });
383
-
384
- runner.add('ConditionBuilder - Logical groups', () => {
385
- const builder = new ConditionBuilder();
386
-
387
- const andGroup = builder.andGroup(b => [
388
- LogicalOperator.Condition(new FieldConditionBuilder('name').equals('John')),
389
- LogicalOperator.Condition(new FieldConditionBuilder('age').greaterThan(25))
390
- ]);
391
-
392
- assertEqual(andGroup.type, 'and');
393
- assertEqual(andGroup.conditions.length, 2);
394
- });
395
-
396
- runner.add('ConditionBuilder - Nested conditions', () => {
397
- const builder = new ConditionBuilder();
398
-
399
- const nestedCondition = builder.nested('user', nested =>
400
- LogicalOperator.Condition(nested.field('name').equals('John'))
401
- );
402
-
403
- assertEqual(nestedCondition.type, 'condition');
404
- });
405
-
406
- runner.add('ConditionBuilder - Clear functionality', () => {
407
- const builder = new ConditionBuilder();
408
- builder.addCondition(LogicalOperator.Condition(new FieldConditionBuilder('test').equals('value')));
409
-
410
- assertEqual(builder.getConditionCount(), 1);
411
- builder.clear();
412
- assertEqual(builder.getConditionCount(), 0);
413
- assertTrue(builder.isEmpty());
414
- });
415
-
416
- // ===== QUERY BUILDER TESTS =====
417
-
418
- runner.add('QueryBuilder - Basic construction', () => {
419
- const builder = new QueryBuilder();
420
- assertFalse(builder.isValid()); // No conditions yet
421
- });
422
-
423
- runner.add('QueryBuilder - Method chaining', () => {
424
- const mockClient = {
425
- async post() { return { data: { records: [], total: 0 } }; }
426
- };
427
-
428
- const builder = new QueryBuilder()
429
- .withHttpClient(mockClient)
430
- .withServerUrl('http://localhost:3000')
431
- .collection('users')
432
- .limit(10)
433
- .offset(0);
434
-
435
- const rawQuery = builder.buildRawQuery();
436
- assertEqual(rawQuery.limit, 10);
437
- assertEqual(rawQuery.offset, 0);
438
- });
439
-
440
- runner.add('QueryBuilder - Where field conditions', () => {
441
- const builder = new QueryBuilder();
442
- const result = builder.whereField('name').equals('John');
443
-
444
- assertTrue(result === builder); // Returns self for chaining
445
- assertTrue(builder.isValid()); // Now has conditions
446
- });
447
-
448
- runner.add('QueryBuilder - Complex find conditions', () => {
449
- const builder = new QueryBuilder();
450
-
451
- builder.find(conditionBuilder =>
452
- conditionBuilder.andGroup(b => [
453
- LogicalOperator.Condition(new FieldConditionBuilder('name').equals('John')),
454
- LogicalOperator.Condition(new FieldConditionBuilder('age').greaterThan(25))
455
- ])
456
- );
457
-
458
- assertTrue(builder.isValid());
459
- const rawQuery = builder.buildRawQuery();
460
- assertTrue(typeof rawQuery.find === 'object');
461
- });
462
-
463
- runner.add('QueryBuilder - Selection methods', () => {
464
- const builder = new QueryBuilder();
465
-
466
- // Test selectFields
467
- builder.selectFields(['name', 'email']);
468
- assertTrue(builder.isValid());
469
-
470
- // Test selectAll
471
- const builder2 = new QueryBuilder();
472
- builder2.selectAll();
473
- assertTrue(builder2.isValid());
474
- });
475
-
476
- runner.add('QueryBuilder - Clone functionality', () => {
477
- const original = new QueryBuilder()
478
- .collection('users')
479
- .limit(10)
480
- .whereField('name').equals('John');
481
-
482
- const cloned = original.clone();
483
-
484
- // Verify they're separate instances
485
- assertTrue(cloned !== original);
486
-
487
- // Verify cloned data
488
- assertEqual(cloned.buildRawQuery().limit, 10);
489
- });
490
-
491
- // ===== QUERY RESULT TESTS =====
492
-
493
- runner.add('QueryResult - Basic functionality', () => {
494
- const data = [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }];
495
- const result = createQueryResult(data);
496
-
497
- assertEqual(result.len(), 2);
498
- assertEqual(result.length(), 2);
499
- assertFalse(result.isEmpty());
500
- assertEqual(result.first()?.name, 'John');
501
- assertEqual(result.last()?.name, 'Jane');
502
- assertEqual(result.get(0)?.id, 1);
503
- });
504
-
505
- runner.add('QueryResult - Array input validation', () => {
506
- // Test with single item
507
- const single = createQueryResult({ id: 1, name: 'Test' } as any);
508
- assertEqual(single.len(), 1);
509
-
510
- // Test with empty array
511
- const empty = createQueryResult([]);
512
- assertTrue(empty.isEmpty());
513
- assertEqual(empty.len(), 0);
514
- });
515
-
516
- runner.add('QueryResult - Iteration methods', () => {
517
- const data = [
518
- { id: 1, name: 'John', active: true },
519
- { id: 2, name: 'Jane', active: false },
520
- { id: 3, name: 'Bob', active: true }
521
- ];
522
- const result = createQueryResult(data);
523
-
524
- // Test any/some
525
- assertTrue(result.any(item => item.active));
526
- assertTrue(result.any(item => item.name === 'Jane'));
527
-
528
- // Test all/every
529
- assertFalse(result.all(item => item.active));
530
- assertTrue(result.all(item => typeof item.id === 'number'));
531
-
532
- // Test find
533
- const found = result.find(item => item.name === 'Jane');
534
- assertEqual(found?.id, 2);
535
-
536
- // Test filter
537
- const active = result.filter(item => item.active);
538
- assertEqual(active.length, 2);
539
-
540
- // Test map
541
- const names = result.map(item => item.name);
542
- assertEqual(names, ['John', 'Jane', 'Bob']);
543
- });
544
-
545
- runner.add('QueryResult - Pluck methods', () => {
546
- const data = [
547
- { id: 1, name: 'John', score: 95.5 },
548
- { id: 2, name: 'Jane', score: 87 },
549
- { id: 3, name: 'Bob', score: null }
550
- ];
551
- const result = createQueryResult(data);
552
-
553
- // Test pluck
554
- const names = result.pluck('name');
555
- assertEqual(names, ['John', 'Jane', 'Bob']);
556
-
557
- // Test pluckStrings
558
- const stringNames = result.pluckStrings('name');
559
- assertEqual(stringNames, ['John', 'Jane', 'Bob']);
560
-
561
- // Test pluckNumbers
562
- const scores = result.pluckNumbers('score');
563
- assertEqual(scores, [95.5, 87, null]);
564
- });
565
-
566
- runner.add('QueryResult - Numeric summary', () => {
567
- const data = [
568
- { score: 85 },
569
- { score: 92 },
570
- { score: 78 },
571
- { score: 95 }
572
- ];
573
- const result = createQueryResult(data);
574
-
575
- const summary = result.summarizeNumeric('score');
576
-
577
- assertTrue(summary !== null);
578
- assertEqual(summary!.count, 4);
579
- assertEqual(summary!.sum, 350);
580
- assertEqual(summary!.mean, 87.5);
581
- assertEqual(summary!.min, 78);
582
- assertEqual(summary!.max, 95);
583
-
584
- // Test with no numeric data
585
- const textData = [{ name: 'John' }, { name: 'Jane' }];
586
- const textResult = createQueryResult(textData);
587
- const textSummary = textResult.summarizeNumeric('name');
588
- assertEqual(textSummary, null);
589
- });
590
-
591
- runner.add('QueryResult - Sorting', () => {
592
- const data = [
593
- { name: 'Charlie', age: 30 },
594
- { name: 'Alice', age: 25 },
595
- { name: 'Bob', age: 35 }
596
- ];
597
- const result = createQueryResult(data);
598
-
599
- // Test single field sort
600
- const byName = result.sortBy('name');
601
- assertEqual(byName[0].name, 'Alice');
602
- assertEqual(byName[1].name, 'Bob');
603
- assertEqual(byName[2].name, 'Charlie');
604
-
605
- // Test descending sort
606
- const byAgeDesc = result.sortBy('age', false);
607
- assertEqual(byAgeDesc[0].age, 35);
608
- assertEqual(byAgeDesc[2].age, 25);
609
-
610
- // Test multi-field sort
611
- const data2 = [
612
- { dept: 'B', name: 'Charlie' },
613
- { dept: 'A', name: 'Bob' },
614
- { dept: 'A', name: 'Alice' }
615
- ];
616
- const result2 = createQueryResult(data2);
617
- const sorted = result2.sortByMultiple([
618
- { field: 'dept', ascending: true },
619
- { field: 'name', ascending: true }
620
- ]);
621
- assertEqual(sorted[0].name, 'Alice');
622
- assertEqual(sorted[1].name, 'Bob');
623
- assertEqual(sorted[2].name, 'Charlie');
624
- });
625
-
626
- runner.add('QueryResult - Pagination and chunking', () => {
627
- const data = Array.from({ length: 10 }, (_, i) => ({ id: i + 1 }));
628
- const result = createQueryResult(data);
629
-
630
- // Test pagination
631
- const page1 = result.paginate(1, 3);
632
- assertEqual(page1.length, 3);
633
- assertEqual(page1[0].id, 1);
634
-
635
- const page2 = result.paginate(2, 3);
636
- assertEqual(page2[0].id, 4);
637
-
638
- // Test chunking
639
- const chunks = result.chunk(3);
640
- assertEqual(chunks.length, 4); // 10 items, chunks of 3 = 4 chunks
641
- assertEqual(chunks[0].length, 3);
642
- assertEqual(chunks[3].length, 1); // Last chunk has remainder
643
- });
644
-
645
- runner.add('QueryResult - Distinct and unique', () => {
646
- const data = [
647
- { category: 'A', value: 1 },
648
- { category: 'B', value: 2 },
649
- { category: 'A', value: 3 },
650
- { category: 'C', value: 4 }
651
- ];
652
- const result = createQueryResult(data);
653
-
654
- const distinctByCategory = result.distinctBy('category');
655
- assertEqual(distinctByCategory.length, 3);
656
-
657
- // Test unique (for primitive arrays)
658
- const numbers = createQueryResult([1, 2, 2, 3, 3, 4]);
659
- const uniqueNumbers = numbers.unique();
660
- assertEqual(uniqueNumbers.length, 4);
661
- });
662
-
663
- runner.add('QueryResult - Statistical functions', () => {
664
- const data = [
665
- { category: 'A', active: true },
666
- { category: 'B', active: false },
667
- { category: 'A', active: true },
668
- { category: 'C', active: true }
669
- ];
670
- const result = createQueryResult(data);
671
-
672
- // Test count with predicate
673
- const activeCount = result.count(item => item.active);
674
- assertEqual(activeCount, 3);
675
-
676
- // Test countBy
677
- const categoryCounts = result.countBy('category');
678
- assertEqual(categoryCounts['A'], 2);
679
- assertEqual(categoryCounts['B'], 1);
680
- assertEqual(categoryCounts['C'], 1);
681
- });
682
-
683
- runner.add('QueryResult - Joins', () => {
684
- const users = createQueryResult([
685
- { id: 1, name: 'John', deptId: 10 },
686
- { id: 2, name: 'Jane', deptId: 20 }
687
- ]);
688
-
689
- const departments = createQueryResult([
690
- { id: 10, name: 'Engineering' },
691
- { id: 20, name: 'Design' }
692
- ]);
693
-
694
- // Test inner join
695
- const innerJoined = users.innerJoin(departments, 'deptId', 'id');
696
- assertEqual(innerJoined.length, 2);
697
- assertEqual(innerJoined[0].name, 'Engineering'); // dept name
698
-
699
- // Test left join
700
- const leftJoined = users.leftJoin(departments, 'deptId', 'id');
701
- assertEqual(leftJoined.length, 2);
702
- });
703
-
704
- runner.add('QueryResult - Export functions', () => {
705
- const data = [
706
- { id: 1, name: 'John', active: true },
707
- { id: 2, name: 'Jane', active: false }
708
- ];
709
- const result = createQueryResult(data);
710
-
711
- // Test CSV export
712
- const csv = result.toCsv();
713
- const lines = csv.split('\n');
714
- assertEqual(lines.length, 3); // header + 2 rows
715
- assertTrue(lines[0].includes('id'));
716
- assertTrue(lines[0].includes('name'));
717
- assertTrue(lines[0].includes('active'));
718
-
719
- // Test JSON export
720
- const json = result.toJSON();
721
- const parsed = JSON.parse(json);
722
- assertEqual(parsed.length, 2);
723
- assertEqual(parsed[0].name, 'John');
724
-
725
- // Test array conversion
726
- const array = result.toArray();
727
- assertEqual(array.length, 2);
728
- assertTrue(Array.isArray(array));
729
- });
730
-
731
- runner.add('QueryResult - Nested field access', () => {
732
- const data = [
733
- { user: { profile: { name: 'John', age: 30 } } },
734
- { user: { profile: { name: 'Jane', age: 25 } } }
735
- ];
736
- const result = createQueryResult(data);
737
-
738
- // Test nested pluck
739
- const names = result.pluck('user.profile.name');
740
- assertEqual(names, ['John', 'Jane']);
741
-
742
- // Test nested sort
743
- const sorted = result.sortBy('user.profile.age');
744
- assertEqual(sorted[0].user.profile.name, 'Jane');
745
- assertEqual(sorted[1].user.profile.name, 'John');
746
-
747
- // Test nested groupBy
748
- const grouped = result.groupBy('user.profile.name');
749
- assertTrue('John' in grouped);
750
- assertTrue('Jane' in grouped);
751
- });
752
-
753
- // ===== ERROR HANDLING AND EDGE CASES =====
754
-
755
- runner.add('Error handling - Invalid conditions', () => {
756
- assertThrows(() => {
757
- const builder = new ConditionBuilder();
758
- builder.buildSingle(); // Should throw - no conditions
759
- });
760
- });
761
-
762
- runner.add('Error handling - Missing required data', () => {
763
- assertThrows(() => {
764
- const logicalOp = new LogicalOperator('condition' as any, [], undefined);
765
- logicalOp.toComposable(); // Should throw - no condition provided
766
- });
767
- });
768
-
769
- runner.add('Edge cases - Empty and null data', () => {
770
- const empty = createQueryResult([]);
771
-
772
- assertEqual(empty.len(), 0);
773
- assertTrue(empty.isEmpty());
774
- assertEqual(empty.first(), undefined);
775
- assertEqual(empty.last(), undefined);
776
-
777
- const summary = empty.summarizeNumeric('nonexistent');
778
- assertEqual(summary, null);
779
- });
780
-
781
- runner.add('Edge cases - Nested field access with missing data', () => {
782
- const data = [
783
- { user: { name: 'John' } }, // Missing profile.age
784
- { user: { profile: { age: 25 } } } // Missing name
785
- ];
786
- const result = createQueryResult(data);
787
-
788
- const ages = result.pluck('user.profile.age');
789
- assertEqual(ages[0], undefined);
790
- assertEqual(ages[1], 25);
791
- });
792
-
793
- // Run all tests
794
- runner.run();