holosphere 1.1.4 → 1.1.6

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