holosphere 1.1.5 → 1.1.7

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.
@@ -1,45 +1,62 @@
1
1
  import HoloSphere from '../holosphere.js';
2
2
  import * as h3 from 'h3-js';
3
+ import { jest } from '@jest/globals';
4
+
5
+ // Configure Jest
6
+ jest.setTimeout(30000); // 30 second timeout
3
7
 
4
8
  describe('HoloSphere', () => {
5
- const testAppName = 'test-app';
6
- const testCredentials = {
7
- spacename: 'testuser',
8
- password: 'testpass'
9
- };
9
+ const testAppName = 'test-app3';
10
+ const testHolon = 'testHolon3';
11
+ const testLens = 'testLens3';
12
+ const testPassword = 'testPassword1234';
10
13
  let holoSphere;
11
14
  let strictHoloSphere;
12
-
13
15
  beforeAll(async () => {
14
- // Initialize HoloSphere instances once for all tests
15
- holoSphere = new HoloSphere(testAppName, false);
16
- strictHoloSphere = new HoloSphere(testAppName, true);
17
-
18
- // Set up test space and authenticate
16
+ holoSphere = new HoloSphere('test-app', false, null);
17
+ strictHoloSphere = new HoloSphere('test-app-strict', true, null);
18
+ });
19
+
20
+ afterEach(async () => {
21
+ // Clean up test data
19
22
  try {
20
- await holoSphere.createSpace(testCredentials.spacename, testCredentials.password);
21
- } catch (error) {
22
- // Space might already exist, try to delete it first
23
- try {
24
- await holoSphere.deleteGlobal('spaces', testCredentials.spacename);
25
- await holoSphere.createSpace(testCredentials.spacename, testCredentials.password);
26
- } catch (error) {
27
- console.error('Failed to recreate space:', error);
28
- throw error;
23
+ if (holoSphere) {
24
+ await holoSphere.deleteAll(testHolon, testLens);
25
+ }
26
+ if (strictHoloSphere) {
27
+ await strictHoloSphere.deleteAll(testHolon, testLens);
29
28
  }
29
+ } catch (error) {
30
+ console.error('Error in afterEach cleanup:', error);
31
+ }
32
+ });
33
+
34
+ afterAll(async () => {
35
+ // Clean up all test data
36
+ await holoSphere.deleteAll(testHolon, testLens);
37
+ await holoSphere.deleteAllGlobal('testTable');
38
+
39
+ // Close Gun connections
40
+ if (holoSphere.gun) {
41
+ holoSphere.gun.off();
42
+ }
43
+ if (strictHoloSphere.gun) {
44
+ strictHoloSphere.gun.off();
30
45
  }
31
46
 
32
- // Ensure both instances are logged in
33
- await holoSphere.login(testCredentials.spacename, testCredentials.password);
34
- await strictHoloSphere.login(testCredentials.spacename, testCredentials.password);
47
+ // Wait for connections to close
48
+ await new Promise(resolve => setTimeout(resolve, 1000));
35
49
  });
36
50
 
51
+
37
52
  describe('Constructor', () => {
38
53
  test('should have initialized with correct properties', () => {
39
54
  expect(holoSphere).toBeInstanceOf(HoloSphere);
40
55
  expect(holoSphere.gun).toBeDefined();
41
56
  expect(holoSphere.validator).toBeDefined();
42
57
  expect(holoSphere.openai).toBeUndefined();
58
+ expect(holoSphere.subscriptions).toBeDefined();
59
+ expect(holoSphere.subscriptions).toEqual({});
43
60
  });
44
61
 
45
62
  test('should initialize with OpenAI', () => {
@@ -47,970 +64,107 @@ describe('HoloSphere', () => {
47
64
  });
48
65
  });
49
66
 
50
- describe('Schema Operations', () => {
51
- const testLens = 'testLens';
52
- const validSchema = {
53
- type: 'object',
54
- properties: {
55
- id: { type: 'string' },
56
- data: { type: 'string' }
57
- },
58
- required: ['id', 'data']
59
- };
60
-
61
- beforeEach(async () => {
62
- // Clean up any existing schemas
63
- await holoSphere.deleteAllGlobal('schemas');
64
- });
65
-
66
- test('should set and get schema', async () => {
67
- await holoSphere.setSchema(testLens, validSchema);
68
- const retrievedSchema = await holoSphere.getSchema(testLens);
69
- expect(retrievedSchema).toBeDefined();
70
- expect(retrievedSchema).toEqual(validSchema);
67
+ describe('Space Management', () => {
68
+ test('should handle private space authentication', async () => {
69
+ const testData = { id: 'test1', value: 'data' + Date.now() };
70
+
71
+ await holoSphere.put(testHolon, testLens, testData, testPassword);
72
+ const result = await holoSphere.get(testHolon, testLens, testData.id, testPassword);
73
+ expect(result).toBeDefined();
74
+ expect(result.value).toBe(testData.value);
71
75
  });
72
76
 
73
- test('should handle invalid schema parameters', async () => {
74
- await expect(holoSphere.setSchema(null, null))
75
- .rejects.toThrow('setSchema: Missing required parameters');
77
+ test('should handle public space access', async () => {
78
+ const testData = { id: 'public1', value: 'public data' + Date.now() };
79
+
80
+ await holoSphere.put(testHolon, testLens, testData);
81
+ const result = await holoSphere.get(testHolon, testLens, testData.id);
82
+ expect(result).toBeDefined();
83
+ expect(result.value).toBe(testData.value);
76
84
  });
77
85
 
78
- test('should enforce strict mode schema validation', async () => {
79
- const invalidSchema = {
80
- type: 'object',
81
- properties: {
82
- id: { type: 'string' }
83
- }
84
- };
86
+ test('should handle missing parameters gracefully', async () => {
87
+ const result1 = await holoSphere.get(null, testLens, 'key');
88
+ expect(result1).toBeNull();
85
89
 
86
- await expect(strictHoloSphere.setSchema(testLens, invalidSchema))
87
- .rejects.toThrow();
88
- });
90
+ const result2 = await holoSphere.get(testHolon, null, 'key');
91
+ expect(result2).toBeNull();
89
92
 
90
- test('should handle schema retrieval for non-existent lens', async () => {
91
- const result = await holoSphere.getSchema('nonexistent-lens');
92
- expect(result).toBeNull();
93
+ const result3 = await holoSphere.get(testHolon, testLens, null);
94
+ expect(result3).toBeNull();
93
95
  });
94
96
 
95
- test('should maintain schema integrity across storage and retrieval', async () => {
96
- const testLens = 'schemaTestLens';
97
-
98
- // Create test schemas of increasing complexity
99
- const testSchemas = [
100
- {
101
- type: 'object',
102
- properties: {
103
- id: { type: 'string' },
104
- data: { type: 'string' }
105
- },
106
- required: ['id', 'data']
107
- },
108
- {
109
- type: 'object',
110
- properties: {
111
- id: { type: 'string' },
112
- data: { type: 'object' },
113
- metadata: {
114
- type: 'object',
115
- properties: {
116
- timestamp: { type: 'number' },
117
- tags: {
118
- type: 'array',
119
- items: { type: 'string' }
120
- }
121
- }
122
- }
123
- },
124
- required: ['id', 'data']
125
- }
126
- ];
127
-
128
- for (let i = 0; i < testSchemas.length; i++) {
129
- const testLensWithIndex = `${testLens}_${i}`;
130
- const schema = testSchemas[i];
131
-
132
- // Store schema
133
- await strictHoloSphere.setSchema(testLensWithIndex, schema);
134
-
135
- // Retrieve schema
136
- const retrievedSchema = await strictHoloSphere.getSchema(testLensWithIndex);
137
-
138
- // Verify schema is retrieved correctly
139
- expect(retrievedSchema).toBeDefined();
140
- expect(retrievedSchema).toEqual(schema);
141
-
142
- // Test schema validation with valid data
143
- const validData = {
144
- id: 'test1',
145
- data: i === 0 ? 'test' : { field: 'value' }
146
- };
147
- if (i === 1) {
148
- validData.metadata = {
149
- timestamp: Date.now(),
150
- tags: ['test']
151
- };
152
- }
153
-
154
- // Valid data should work
155
- await expect(strictHoloSphere.put('testHolon', testLensWithIndex, validData))
156
- .resolves.toBe(true);
157
-
158
- // Invalid data should fail in strict mode
159
- const invalidData = {
160
- id: 'test2'
161
- // Missing required 'data' field
162
- };
163
-
164
- await expect(strictHoloSphere.put('testHolon', testLensWithIndex, invalidData))
165
- .rejects.toThrow('Schema validation failed');
166
-
167
- // Clean up after each schema test
168
- await strictHoloSphere.deleteAll('testHolon', testLensWithIndex);
169
- }
170
- }, 10000);
171
-
172
- test('should handle concurrent schema operations', async () => {
173
- const baseLens = 'concurrentSchemaTest';
174
- const numOperations = 5;
175
- const promises = [];
176
- const expectedSchemas = [];
177
-
178
- // Create and store schemas concurrently
179
- for (let i = 0; i < numOperations; i++) {
180
- const lens = `${baseLens}_${i}`;
181
- const schema = {
182
- type: 'object',
183
- properties: {
184
- id: { type: 'string' },
185
- value: { type: 'string' }
186
- },
187
- required: ['id', 'value']
188
- };
189
- expectedSchemas.push({ lens, schema });
190
- promises.push(holoSphere.setSchema(lens, schema));
191
- }
192
-
193
- // Wait for all operations to complete
194
- await Promise.all(promises);
195
-
196
-
197
- // Verify each schema was stored correctly
198
- for (const { lens, schema } of expectedSchemas) {
199
- const retrievedSchema = await holoSphere.getSchema(lens);
200
- expect(retrievedSchema).toEqual(schema);
201
- }
202
- }, 10000); // Increase timeout to 10 seconds
203
-
204
- test('should handle schema updates correctly', async () => {
205
- const testLens = 'schemaUpdateTest';
97
+ test('should handle authentication errors gracefully', async () => {
98
+ const testData = { id: 'test2', value: 'private data' };
206
99
 
207
- // Initial schema
208
- const initialSchema = {
209
- type: 'object',
210
- properties: {
211
- id: { type: 'string' },
212
- data: { type: 'string' }
213
- },
214
- required: ['id']
215
- };
216
-
217
- // Updated schema
218
- const updatedSchema = {
219
- type: 'object',
220
- properties: {
221
- id: { type: 'string' },
222
- data: { type: 'string' },
223
- metadata: { type: 'object' }
224
- },
225
- required: ['id', 'data']
226
- };
227
-
228
- // Set initial schema
229
- await holoSphere.setSchema(testLens, initialSchema);
230
-
231
- // Verify initial schema
232
- let retrievedSchema = await holoSphere.getSchema(testLens);
233
- expect(retrievedSchema).toEqual(initialSchema);
234
-
235
- // Update schema
236
- await holoSphere.setSchema(testLens, updatedSchema);
237
-
238
- // Verify updated schema
239
- retrievedSchema = await holoSphere.getSchema(testLens);
240
- expect(retrievedSchema).toEqual(updatedSchema);
241
- });
242
-
243
- afterEach(async () => {
244
- // Clean up schemas after each test
245
- await holoSphere.deleteAllGlobal('schemas');
100
+ await holoSphere.put(testHolon, testLens, testData, testPassword);
101
+ const result = await holoSphere.get(testHolon, testLens, testData.id, 'wrong_password');
102
+ expect(result).toBeNull();
246
103
  });
247
104
  });
248
105
 
249
106
  describe('Data Operations', () => {
250
- const testHolon = h3.latLngToCell(40.7128, -74.0060, 7);
251
- const testLens = 'testLens';
252
- const validData = { id: 'test1', data: 'test data' };
253
- const invalidData = { id: 'test456', wrongField: 'wrong data' };
254
- const expectedValidData = {
255
- ...validData,
256
- owner: testCredentials.spacename,
257
- federation: expect.objectContaining({
258
- origin: testCredentials.spacename,
259
- timestamp: expect.any(Number)
260
- })
107
+ const validSchema = {
108
+ type: 'object',
109
+ properties: {
110
+ id: { type: 'string' },
111
+ value: { type: 'string' }
112
+ },
113
+ required: ['id', 'value']
261
114
  };
262
115
 
263
116
  beforeEach(async () => {
264
- // Set up schema for validation tests
265
- const schema = {
266
- type: 'object',
267
- properties: {
268
- id: { type: 'string' },
269
- data: { type: 'string' },
270
- owner: { type: 'string' }
271
- },
272
- required: ['id', 'data']
273
- };
274
- await holoSphere.setSchema(testLens, schema);
275
- });
276
-
277
- test('should put and get data with schema validation', async () => {
278
- // Test valid data
279
- await expect(holoSphere.put(testHolon, testLens, validData))
280
- .resolves.toBe(true);
281
-
282
- // Test invalid data
283
- await expect(holoSphere.put(testHolon, testLens, invalidData))
284
- .rejects.toThrow('Schema validation failed');
285
-
286
- // Verify the valid data was stored correctly
287
- const result = await holoSphere.get(testHolon, testLens, validData.id);
288
- expect(result).toEqual(expect.objectContaining(expectedValidData));
289
-
290
- // Verify the invalid data was not stored
291
- const invalidResult = await holoSphere.get(testHolon, testLens, invalidData.id);
292
- expect(invalidResult).toBeNull();
293
- });
294
-
295
- test('should get all data with schema validation', async () => {
296
- await holoSphere.put(testHolon, testLens, validData);
297
- await holoSphere.put(testHolon, testLens, { id: 'test789', data: 'more test data' });
298
-
299
- const results = await holoSphere.getAll(testHolon, testLens);
300
- expect(Array.isArray(results)).toBeTruthy();
301
- expect(results.length).toBeGreaterThan(0);
302
- expect(results.some(item => item.id === validData.id)).toBeTruthy();
303
- });
304
-
305
- test('should delete data', async () => {
306
- await holoSphere.put(testHolon, testLens, validData);
307
- await holoSphere.delete(testHolon, testLens, validData.id);
308
-
309
- const result = await holoSphere.get(testHolon, testLens, validData.id);
310
- expect(result).toBeNull();
311
- });
312
-
313
- test('should delete all data', async () => {
314
- await holoSphere.put(testHolon, testLens, validData);
315
- await holoSphere.put(testHolon, testLens, { id: 'test789', data: 'more test data' });
316
-
317
- const deleteResult = await holoSphere.deleteAll(testHolon, testLens);
318
- expect(deleteResult).toBe(true);
319
-
320
- const results = await holoSphere.getAll(testHolon, testLens);
321
- expect(results).toEqual([]);
117
+ await holoSphere.setSchema(testLens, validSchema);
322
118
  });
323
119
 
324
- test('should enforce strict mode data validation', async () => {
325
- // Define schema for strict mode tests
326
- const strictSchema = {
327
- type: 'object',
328
- properties: {
329
- id: { type: 'string' },
330
- data: { type: 'string' }
331
- },
332
- required: ['id', 'data']
333
- };
334
-
335
- // Set up schema
336
- await strictHoloSphere.setSchema(testLens, strictSchema);
120
+ test('should handle data operations gracefully', async () => {
121
+ const testData = { id: 'test3', value: 'test data' };
337
122
 
338
- // Try to put data without schema in strict mode
339
- await expect(strictHoloSphere.put(testHolon, 'no-schema-lens', validData))
340
- .rejects.toThrow('Schema required in strict mode');
341
- });
342
-
343
- test('should maintain content integrity in holon storage', async () => {
344
- const testData = [
345
- { id: 'test1', data: 'content1' },
346
- { id: 'test2', data: 'content2' },
347
- { id: 'test3', data: 'content3' }
348
- ];
349
-
350
- const expectedData = testData.map(data => ({
351
- ...data,
352
- owner: testCredentials.spacename,
353
- federation: expect.objectContaining({
354
- origin: testCredentials.spacename,
355
- timestamp: expect.any(Number)
356
- })
357
- }));
358
-
359
- // Store all test data
360
- for (const data of testData) {
361
- await holoSphere.put(testHolon, testLens, data);
362
- }
363
-
364
- // Retrieve all data
365
- const retrievedData = await holoSphere.getAll(testHolon, testLens);
366
-
367
- // Sort both arrays by id for comparison
368
- const sortedExpectedData = [...expectedData].sort((a, b) => a.id.localeCompare(b.id));
369
- const sortedRetrievedData = [...retrievedData].sort((a, b) => a.id.localeCompare(b.id));
370
-
371
- // Verify no duplicates
372
- const uniqueIds = new Set(retrievedData.map(item => item.id));
373
- expect(uniqueIds.size).toBe(testData.length);
374
-
375
- // Verify all items are present and correct
376
- expect(sortedRetrievedData).toEqual(expect.arrayContaining(sortedExpectedData));
377
-
378
- // Verify individual item retrieval
379
- for (let i = 0; i < testData.length; i++) {
380
- const item = await holoSphere.get(testHolon, testLens, testData[i].id);
381
- expect(item).toEqual(expect.objectContaining(expectedData[i]));
382
- }
383
- });
384
-
385
- test('should handle data consistency in get operations', async () => {
386
- const testData = [
387
- { id: 'test1', data: 'content1' },
388
- { id: 'test2', data: 'content2' },
389
- { id: 'test3', data: 'content3' }
390
- ].map(data => ({
391
- ...data,
392
- owner: testCredentials.spacename,
393
- federation: expect.objectContaining({
394
- origin: testCredentials.spacename,
395
- timestamp: expect.any(Number)
396
- })
397
- }));
398
-
399
- // Store test data
400
- for (const data of testData) {
401
- const { federation, ...storeData } = data;
402
- await holoSphere.put(testHolon, testLens, storeData);
403
- }
404
-
405
- // Test individual get operations
406
- for (const expected of testData) {
407
- const result = await holoSphere.get(testHolon, testLens, expected.id);
408
- expect(result).toEqual(expect.objectContaining(expected));
409
- }
410
-
411
- // Multiple consecutive gets should return same data
412
- for (let i = 0; i < 5; i++) {
413
- const result = await holoSphere.get(testHolon, testLens, 'test1');
414
- expect(result).toEqual(expect.objectContaining(testData[0]));
415
- }
416
- });
417
-
418
- test('should handle data consistency in getAll operations', async () => {
419
- const testData = Array.from({ length: 10 }, (_, i) => ({
420
- id: `test${i}`,
421
- data: `content${i}`,
422
- owner: testCredentials.spacename,
423
- federation: expect.objectContaining({
424
- origin: testCredentials.spacename,
425
- timestamp: expect.any(Number)
426
- })
427
- }));
428
-
429
- // Store test data sequentially to ensure consistency
430
- for (const data of testData) {
431
- const { federation, ...storeData } = data;
432
- await holoSphere.put(testHolon, testLens, storeData);
433
- }
434
-
435
- // Get data multiple times
436
- const results = await Promise.all(
437
- Array.from({ length: 5 }, () => holoSphere.getAll(testHolon, testLens))
438
- );
439
-
440
- // Verify results
441
- results.forEach(result => {
442
- // Should have correct length
443
- expect(result.length).toBe(testData.length);
444
-
445
- // Should have no duplicates
446
- const ids = result.map(item => item.id);
447
- const uniqueIds = new Set(ids);
448
- expect(uniqueIds.size).toBe(testData.length);
449
-
450
- // Should contain all expected data
451
- const sortedResult = [...result].sort((a, b) => a.id.localeCompare(b.id));
452
- const sortedExpected = [...testData].sort((a, b) => a.id.localeCompare(b.id));
453
- sortedResult.forEach((item, idx) => {
454
- expect(item).toEqual(expect.objectContaining(sortedExpected[idx]));
455
- });
456
- });
457
- }, 15000);
458
-
459
- test('should handle rapid concurrent getAll operations', async () => {
460
- const testData = Array.from({ length: 5 }, (_, i) => ({
461
- id: `concurrent${i}`,
462
- data: `data${i}`
463
- }));
464
-
465
- // Store test data
466
- for (const data of testData) {
467
- await holoSphere.put(testHolon, testLens, data);
468
- }
469
-
470
- // Perform multiple concurrent getAll operations
471
- const promises = Array.from({ length: 10 }, () =>
472
- holoSphere.getAll(testHolon, testLens)
473
- );
474
-
475
- const results = await Promise.all(promises);
476
-
477
- // Verify each result has the correct number of items
478
- results.forEach(result => {
479
- expect(result.length).toBe(testData.length);
480
-
481
- // Check for duplicates within each result
482
- const ids = result.map(item => item.id);
483
- const uniqueIds = new Set(ids);
484
- expect(uniqueIds.size).toBe(ids.length);
485
- });
486
-
487
- // Verify consistency across results
488
- const sortedResults = results.map(result =>
489
- [...result].sort((a, b) => a.id.localeCompare(b.id))
490
- );
491
-
492
- for (let i = 1; i < sortedResults.length; i++) {
493
- expect(sortedResults[i]).toEqual(sortedResults[0]);
494
- }
495
- });
496
-
497
- // Add cleanup after each test
498
- afterEach(async () => {
499
- await holoSphere.deleteAll(testHolon, testLens);
500
- });
501
- });
502
-
503
- describe('Node Operations', () => {
504
- const testHolon = h3.latLngToCell(40.7128, -74.0060, 7);
505
- const testLens = 'testLens';
506
- const testNode = { value: 'test node data' };
507
-
508
- test('should put and get node', async () => {
509
- await holoSphere.putNode(testHolon, testLens, testNode);
123
+ // Test non-existent data
124
+ const nonExistent = await holoSphere.get(testHolon, testLens, 'non-existent');
125
+ expect(nonExistent).toBeNull();
510
126
 
511
- const result = await holoSphere.getNode(testHolon, testLens, 'value');
512
- expect(result).toBeDefined();
513
- expect(result).toBe('test node data');
514
- });
515
-
516
- test('should delete node', async () => {
517
- // First put the node
518
- await holoSphere.putNode(testHolon, testLens, testNode);
519
-
520
- // Verify node exists
521
- const beforeDelete = await holoSphere.getNode(testHolon, testLens, 'value');
522
- expect(beforeDelete).toBe('test node data');
523
-
524
- // Delete the node
525
- const deleteResult = await holoSphere.deleteNode(testHolon, testLens, 'value');
526
- expect(deleteResult).toBe(true);
527
-
528
- // Verify node is deleted
529
- const afterDelete = await holoSphere.getNode(testHolon, testLens, 'value');
530
- expect(afterDelete).toBeNull();
531
- });
532
-
533
- test('should handle invalid node operations', async () => {
534
- await expect(holoSphere.deleteNode(null, null, null))
535
- .rejects.toThrow('deleteNode: Missing required parameters');
536
- });
537
-
538
- afterEach(async () => {
539
- // Clean up after each test
540
- await holoSphere.deleteNode(testHolon, testLens, 'value');
541
- });
542
- });
543
-
544
- describe('Geospatial Operations', () => {
545
- const lat = 40.7128;
546
- const lng = -74.0060;
547
- const resolution = 7;
548
-
549
- test('should get holon from coordinates', async () => {
550
- const holon = await holoSphere.getHolon(lat, lng, resolution);
551
- expect(holon).toBeDefined();
552
- expect(typeof holon).toBe('string');
553
- });
554
-
555
- test('should get scalespace from coordinates', () => {
556
- const scales = holoSphere.getScalespace(lat, lng);
557
- expect(Array.isArray(scales)).toBeTruthy();
558
- expect(scales.length).toBe(15); // 0-14 resolution levels
559
- });
560
-
561
- test('should get holon scalespace', () => {
562
- const holon = h3.latLngToCell(lat, lng, resolution);
563
- const scales = holoSphere.getHolonScalespace(holon);
564
- expect(Array.isArray(scales)).toBeTruthy();
565
- expect(scales.length).toBe(resolution + 1);
566
- });
567
- });
568
-
569
- describe('Subscription Operations', () => {
570
- const testHolon = h3.latLngToCell(40.7128, -74.0060, 7);
571
- const testLens = 'testLens';
572
-
573
- beforeEach(async () => {
574
- await holoSphere.deleteAll(testHolon, testLens);
575
- });
576
-
577
- test('should receive data through subscription', async () => {
578
- const testData = { id: 'test1', data: 'test data' };
579
- const expectedData = {
580
- ...testData,
581
- owner: testCredentials.spacename,
582
- federation: expect.objectContaining({
583
- origin: testCredentials.spacename,
584
- timestamp: expect.any(Number)
585
- })
586
- };
587
- let received = false;
588
-
589
- return new Promise((resolve, reject) => {
590
- const timeout = setTimeout(() => {
591
- reject(new Error('Subscription timeout'));
592
- }, 20000);
593
-
594
- holoSphere.subscribe(testHolon, testLens, (data) => {
595
- if (!received && data.id === testData.id) {
596
- try {
597
- received = true;
598
- expect(data).toEqual(expect.objectContaining(expectedData));
599
- clearTimeout(timeout);
600
- resolve();
601
- } catch (error) {
602
- clearTimeout(timeout);
603
- reject(error);
604
- }
605
- }
606
- });
607
-
608
- // Put data after subscription
609
- setTimeout(async () => {
610
- try {
611
- await holoSphere.put(testHolon, testLens, testData);
612
- } catch (error) {
613
- clearTimeout(timeout);
614
- reject(error);
615
- }
616
- }, 1000);
617
- });
618
- }, 30000);
619
-
620
- test('should stop receiving data after unsubscribe', async () => {
621
- const testData1 = { id: 'test1', data: 'first' };
622
- const testData2 = { id: 'test2', data: 'second' };
623
- let receivedData = [];
624
-
625
- return new Promise(async (resolve, reject) => {
626
- const timeout = setTimeout(() => {
627
- // If we only received the first piece of data, test passes
628
- if (receivedData.length === 1 && receivedData[0].id === testData1.id) {
629
- resolve();
630
- } else {
631
- reject(new Error('Test timeout or received unexpected data'));
632
- }
633
- }, 5000);
634
-
635
- const subscription = await holoSphere.subscribe(testHolon, testLens, async (data) => {
636
- receivedData.push(data);
637
-
638
- if (data.id === testData1.id) {
639
- subscription.unsubscribe();
640
- resolve();
641
- } else if (data.id === testData2.id) {
642
- clearTimeout(timeout);
643
- reject(new Error('Received data after unsubscribe'));
644
- }
645
- });
646
-
647
- // Put first piece of data
648
- await holoSphere.put(testHolon, testLens, testData1);
649
- });
650
- }, 10000);
651
-
652
- test('should handle multiple subscriptions', async () => {
653
- const testData = { id: 'test1', data: 'test data' };
654
- const expectedData = {
655
- ...testData,
656
- owner: testCredentials.spacename,
657
- federation: expect.objectContaining({
658
- origin: testCredentials.spacename,
659
- timestamp: expect.any(Number)
660
- })
661
- };
662
- let received1 = false;
663
- let received2 = false;
664
-
665
- return new Promise((resolve, reject) => {
666
- const timeout = setTimeout(() => {
667
- reject(new Error('Subscription timeout'));
668
- }, 20000);
669
-
670
- function checkDone() {
671
- if (received1 && received2) {
672
- clearTimeout(timeout);
673
- resolve();
674
- }
675
- }
676
-
677
- holoSphere.subscribe(testHolon, testLens, (data) => {
678
- if (data.id === testData.id) {
679
- try {
680
- received1 = true;
681
- expect(data).toEqual(expect.objectContaining(expectedData));
682
- checkDone();
683
- } catch (error) {
684
- clearTimeout(timeout);
685
- reject(error);
686
- }
687
- }
688
- });
689
-
690
- holoSphere.subscribe(testHolon, testLens, (data) => {
691
- if (data.id === testData.id) {
692
- try {
693
- received2 = true;
694
- expect(data).toEqual(expect.objectContaining(expectedData));
695
- checkDone();
696
- } catch (error) {
697
- clearTimeout(timeout);
698
- reject(error);
699
- }
700
- }
701
- });
702
-
703
- // Put data after both subscriptions
704
- setTimeout(async () => {
705
- try {
706
- await holoSphere.put(testHolon, testLens, testData);
707
- } catch (error) {
708
- clearTimeout(timeout);
709
- reject(error);
710
- }
711
- }, 1000);
712
- });
713
- }, 30000);
714
-
715
- afterEach(async () => {
716
- await holoSphere.deleteAll(testHolon, testLens);
717
- });
718
- });
719
-
720
- describe('Parse Operations', () => {
721
- test('should handle null input', async () => {
722
- await expect(holoSphere.parse(null))
723
- .rejects.toThrow('parse: No data provided');
724
- });
725
-
726
- test('should parse valid JSON string', async () => {
727
- const result = await holoSphere.parse('{"test": "data"}');
728
- expect(result).toEqual({ test: 'data' });
729
- });
730
- });
731
-
732
- describe('Global Operations', () => {
733
- test('should put and get global data', async () => {
734
- const testData = { id: 'global1', value: 'test' };
735
- await holoSphere.putGlobal('testTable', testData);
736
-
737
- const result = await holoSphere.getGlobal('testTable', 'global1');
127
+ // Test storing and retrieving data
128
+ await holoSphere.put(testHolon, testLens, testData);
129
+ const result = await holoSphere.get(testHolon, testLens, testData.id);
738
130
  expect(result).toEqual(testData);
739
- });
740
-
741
- test('should handle missing parameters in global operations', async () => {
742
- await expect(holoSphere.putGlobal(null, null))
743
- .rejects.toThrow('Table name and data are required');
744
- });
745
131
 
746
- test('should handle getAllGlobal', async () => {
747
- await holoSphere.putGlobal('testTable', { id: 'g1', value: 'test1' });
748
- await holoSphere.putGlobal('testTable', { id: 'g2', value: 'test2' });
749
-
750
- const results = await holoSphere.getAllGlobal('testTable');
751
- expect(Array.isArray(results)).toBe(true);
752
- expect(results.length).toBeGreaterThan(0);
132
+ // Test deleting data
133
+ await holoSphere.delete(testHolon, testLens, testData.id);
134
+ const deletedResult = await holoSphere.get(testHolon, testLens, testData.id);
135
+ expect(deletedResult).toBeNull();
753
136
  });
754
137
 
755
- test('should maintain content integrity in global storage', async () => {
756
- const testTable = 'testGlobalTable';
138
+ test('should handle invalid data gracefully', async () => {
139
+ const invalidData = { wrongField: 'no id field' };
757
140
 
758
- // Create test data with unique IDs and content
759
- const testData = [
760
- { id: 'global1', value: 'value1' },
761
- { id: 'global2', value: 'value2' },
762
- { id: 'global3', value: 'value3' }
763
- ];
764
-
765
- // Store all test data
766
- for (const data of testData) {
767
- await holoSphere.putGlobal(testTable, data);
768
- }
769
-
770
- // Retrieve all data
771
- const retrievedData = await holoSphere.getAllGlobal(testTable);
772
-
773
- // Sort both arrays by id for comparison
774
- const sortedTestData = [...testData].sort((a, b) => a.id.localeCompare(b.id));
775
- const sortedRetrievedData = [...retrievedData].sort((a, b) => a.id.localeCompare(b.id));
776
-
777
- // Verify no duplicates
778
- const uniqueIds = new Set(retrievedData.map(item => item.id));
779
- expect(uniqueIds.size).toBe(testData.length);
780
-
781
- // Verify all items are present and correct
782
- expect(sortedRetrievedData).toEqual(sortedTestData);
783
-
784
- // Verify individual item retrieval
785
- for (const data of testData) {
786
- const item = await holoSphere.getGlobal(testTable, data.id);
787
- expect(item).toEqual(data);
788
- }
789
-
790
- // Clean up test data
791
- await holoSphere.deleteAllGlobal(testTable);
792
- });
793
-
794
- test('should handle concurrent global operations without data corruption', async () => {
795
- const testTable = 'concurrentGlobalTest';
796
- const numOperations = 5;
797
- const promises = [];
798
- const expectedData = [];
799
-
800
- // Create and store data concurrently with small delays
801
- for (let i = 0; i < numOperations; i++) {
802
- const data = { id: `concurrent${i}`, value: `value${i}` };
803
- expectedData.push(data);
804
- promises.push(holoSphere.putGlobal(testTable, data));
805
- }
806
-
807
- // Wait for all operations to complete
808
- await Promise.all(promises);
809
-
810
-
811
-
812
- // Retrieve and verify data
813
- const retrievedData = await holoSphere.getAllGlobal(testTable);
814
-
815
- // Sort both arrays by id for comparison
816
- const sortedExpectedData = [...expectedData].sort((a, b) => a.id.localeCompare(b.id));
817
- const sortedRetrievedData = [...retrievedData].sort((a, b) => a.id.localeCompare(b.id));
818
-
819
- // Verify no duplicates
820
- const uniqueIds = new Set(retrievedData.map(item => item.id));
821
- expect(uniqueIds.size).toBe(numOperations);
822
-
823
- // Verify all items are present and correct
824
- expect(sortedRetrievedData).toEqual(sortedExpectedData);
825
-
826
- // Clean up test data
827
- await holoSphere.deleteAllGlobal(testTable);
828
- }, 15000); // Increase timeout to 15 seconds
829
- });
830
-
831
- describe('Compute Operations', () => {
832
- const testHolon = h3.latLngToCell(40.7128, -74.0060, 7);
833
- const testLens = 'testLens';
834
-
835
- beforeEach(async () => {
836
- // Set up schema for compute tests
837
- const schema = {
838
- type: 'object',
839
- properties: {
840
- id: { type: 'string' },
841
- content: { type: 'string' },
842
- timestamp: { type: 'number' }
843
- },
844
- required: ['id', 'content']
845
- };
846
- await holoSphere.setSchema(testLens, schema);
847
- });
848
-
849
- test('should validate required parameters', async () => {
850
- await expect(holoSphere.compute(null, null, null))
851
- .rejects.toThrow('compute: Missing required parameters');
852
-
853
- await expect(holoSphere.compute(testHolon, null, null))
854
- .rejects.toThrow('compute: Missing required parameters');
855
-
856
- await expect(holoSphere.compute(testHolon, testLens, null))
857
- .rejects.toThrow('compute: Missing required parameters');
858
- });
859
-
860
-
861
- test('should validate holon resolution', async () => {
862
- const invalidHolon = h3.latLngToCell(40.7128, -74.0060, 0); // Resolution 0
863
- await expect(holoSphere.compute(invalidHolon, testLens, { operation: 'summarize' }))
864
- .rejects.toThrow('compute: Invalid holon resolution (must be between 1 and 15)');
865
- });
866
-
867
- test('should validate depth parameters', async () => {
868
- await expect(holoSphere.compute(testHolon, testLens, {
869
- operation: 'summarize',
870
- depth: -1
871
- })).rejects.toThrow('compute: Invalid depth parameter');
872
-
873
- await expect(holoSphere.compute(testHolon, testLens, {
874
- operation: 'summarize',
875
- maxDepth: 0
876
- })).rejects.toThrow('compute: Invalid maxDepth parameter (must be between 1 and 15)');
877
-
878
- await expect(holoSphere.compute(testHolon, testLens, {
879
- operation: 'summarize',
880
- maxDepth: 16
881
- })).rejects.toThrow('compute: Invalid maxDepth parameter (must be between 1 and 15)');
882
- });
883
-
884
- test('should validate operation type', async () => {
885
- await expect(holoSphere.compute(testHolon, testLens, {
886
- operation: 'invalid-operation'
887
- })).rejects.toThrow('compute: Invalid operation (must be one of summarize, aggregate, concatenate)');
888
- });
141
+ // Should not throw when storing invalid data in non-strict mode
142
+ await expect(holoSphere.put(testHolon, testLens, invalidData))
143
+ .resolves.toBeTruthy();
889
144
 
890
- afterEach(async () => {
891
- // Clean up test data
892
- await holoSphere.deleteAll(testHolon, testLens);
893
- await holoSphere.gun.get(holoSphere.appname)
894
- .get(testLens)
895
- .get('schema')
896
- .put(null);
145
+ // Should return null when retrieving invalid data
146
+ const result = await holoSphere.get(testHolon, testLens, 'undefined');
147
+ expect(result).toBeNull();
897
148
  });
898
149
  });
899
150
 
900
- describe('Error Handling', () => {
901
- const testHolon = h3.latLngToCell(40.7128, -74.0060, 7);
902
- const testLens = 'testLens';
903
-
904
- beforeEach(async () => {
905
- // Clear all existing data
906
- await holoSphere.deleteAll(testHolon, testLens);
907
-
908
- // Set up fresh schema
909
- const schema = {
910
- type: 'object',
911
- properties: {
912
- id: { type: 'string' },
913
- data: { type: 'string' }
914
- },
915
- required: ['id', 'data']
916
- };
917
- await holoSphere.setSchema(testLens, schema);
918
- });
919
-
920
- test('should handle concurrent operations', async () => {
921
- const numOperations = 10;
922
- const promises = [];
923
- const expectedIds = new Set();
924
-
925
- // Create concurrent put operations with small delays between them
926
- for (let i = 0; i < numOperations; i++) {
927
- const id = `concurrent${i}`;
928
- expectedIds.add(id);
929
- promises.push(holoSphere.put(testHolon, testLens, {
930
- id: id,
931
- data: 'test'
932
- }));
933
- }
934
-
935
- // Wait for all operations to complete
936
- await Promise.all(promises);
937
-
938
- // Get and verify results
939
- const results = await holoSphere.getAll(testHolon, testLens);
940
- const resultIds = new Set(results.map(r => r.id));
941
-
942
- // Verify we have exactly the expected number of unique results
943
- expect(resultIds.size).toBe(numOperations);
151
+ describe('Global Operations', () => {
152
+ test('should handle global operations gracefully', async () => {
153
+ const globalData = { id: 'global1', value: 'global test data' };
944
154
 
945
- // Verify all expected IDs are present
946
- expectedIds.forEach(id => {
947
- expect(resultIds.has(id)).toBe(true);
948
- });
949
- }, 15000); // Increase timeout to 15 seconds
155
+ // Test non-existent data
156
+ const nonExistent = await holoSphere.getGlobal(testHolon, testLens, 'non-existent');
157
+ expect(nonExistent).toBeNull();
950
158
 
951
- test('should handle large data sets', async () => {
952
- const largeData = {
953
- id: 'large',
954
- data: 'x'.repeat(1000000)
955
- };
956
-
957
- // Put the data
958
- await holoSphere.put(testHolon, testLens, largeData);
959
-
960
- // Get the data back
961
- const result = await holoSphere.get(testHolon, testLens, 'large');
962
-
963
- // Verify the data
964
- expect(result).toBeDefined();
965
- expect(result.id).toBe(largeData.id);
966
- expect(result.data).toBe(largeData.data);
967
- });
159
+ // Test storing and retrieving data
160
+ await holoSphere.putGlobal(testLens, globalData);
161
+ const result = await holoSphere.getGlobal(testLens, globalData.id);
162
+ expect(result).toEqual(globalData);
968
163
 
969
- afterEach(async () => {
970
- // Clean up after each test
971
- await holoSphere.deleteAll(testHolon, testLens);
164
+ // Test deleting data
165
+ await holoSphere.deleteGlobal( testLens, globalData.id);
166
+ const deletedResult = await holoSphere.getGlobal( testLens, globalData.id);
167
+ expect(deletedResult).toBeNull();
972
168
  });
973
169
  });
974
-
975
- describe('OpenAI Integration', () => {
976
- test('should handle missing OpenAI key', async () => {
977
- expect(await holoSphere.summarize('test content'))
978
- .toBe('OpenAI not initialized, please specify the API key in the constructor.');
979
- });
980
-
981
- test.skip('should summarize content with valid OpenAI key', async () => {
982
- const summary = await new HoloSphere('test', false, process.env.OPENAI_API_KEY)
983
- .summarize('Test content to summarize');
984
- expect(typeof summary).toBe('string');
985
- expect(summary.length).toBeGreaterThan(0);
986
- });
987
- });
988
-
989
- afterAll(async () => {
990
- // Clean up test data
991
- const testLens = 'testLens';
992
- const testHolon = h3.latLngToCell(40.7128, -74.0060, 7);
993
- await holoSphere.deleteAll(testHolon, testLens);
994
-
995
- // Clean up test tables
996
- await holoSphere.deleteAllGlobal('testTable');
997
- await holoSphere.deleteAllGlobal('testGlobalTable');
998
- await holoSphere.deleteAllGlobal('concurrentGlobalTest');
999
- await holoSphere.deleteAllGlobal('schemas');
1000
-
1001
- // Clean up test space
1002
- await holoSphere.deleteGlobal('spaces', testCredentials.spacename);
1003
-
1004
- // Logout both instances
1005
- if (holoSphere.currentSpace) {
1006
- await holoSphere.logout();
1007
- }
1008
- if (strictHoloSphere.currentSpace) {
1009
- await strictHoloSphere.logout();
1010
- }
1011
-
1012
- // Clean up Gun instances
1013
- if (holoSphere.gun) holoSphere.gun.off();
1014
- if (strictHoloSphere.gun) strictHoloSphere.gun.off();
1015
- });
1016
170
  });