holosphere 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "holosphere",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Holonic Geospatial Communication Infrastructure",
5
5
  "main": "holosphere.js",
6
6
  "types": "holosphere.d.ts",
@@ -16,7 +16,7 @@
16
16
  "ajv": "^8.12.0",
17
17
  "gun": "^0.2020.1240",
18
18
  "h3-js": "^4.1.0",
19
- "openai": "^4.29.2"
19
+ "openai": "^4.85.1"
20
20
  },
21
21
  "devDependencies": {
22
22
  "jest": "^29.7.0",
@@ -0,0 +1,418 @@
1
+ import HoloSphere from '../holosphere.js';
2
+ import * as h3 from 'h3-js';
3
+ import { jest } from '@jest/globals';
4
+
5
+ // Set global timeout for all tests
6
+ jest.setTimeout(30000);
7
+
8
+ describe('Federation Operations', () => {
9
+ const testAppName = 'test-app';
10
+ const testHolon = 'test-holon';
11
+ const testLens = 'test-lens';
12
+
13
+ // Global HoloSphere instances
14
+ let holoSphere; // Non-strict instance
15
+ let strictHoloSphere; // Strict instance
16
+ let space1, space2;
17
+
18
+ beforeAll(async () => {
19
+ // Create global instances
20
+ holoSphere = new HoloSphere(testAppName, false);
21
+ strictHoloSphere = new HoloSphere(testAppName, true);
22
+
23
+ space1 = { spacename: 'space1', password: 'pass1' };
24
+ space2 = { spacename: 'space2', password: 'pass2' };
25
+
26
+ // Clean up any existing test spaces and federation data
27
+ try {
28
+ await holoSphere.deleteAllGlobal('federation');
29
+ await holoSphere.deleteGlobal('spaces', space1.spacename);
30
+ await holoSphere.deleteGlobal('spaces', space2.spacename);
31
+ // Wait for cleanup
32
+ await new Promise(resolve => setTimeout(resolve, 2000));
33
+ } catch (error) {
34
+ // Ignore errors during cleanup
35
+ console.log('Cleanup error (expected):', error.message);
36
+ }
37
+
38
+ // Create fresh test spaces with retries
39
+ for (let i = 0; i < 3; i++) {
40
+ try {
41
+ await holoSphere.createSpace(space1.spacename, space1.password);
42
+ await holoSphere.createSpace(space2.spacename, space2.password);
43
+ break;
44
+ } catch (error) {
45
+ console.log('Space creation attempt', i + 1, 'failed:', error.message);
46
+ if (i === 2) throw error;
47
+ await new Promise(resolve => setTimeout(resolve, 2000));
48
+ }
49
+ }
50
+
51
+ // Wait for space creation to complete
52
+ await new Promise(resolve => setTimeout(resolve, 5000));
53
+
54
+ // Verify spaces were created
55
+ const space1Created = await holoSphere.getGlobal('spaces', space1.spacename);
56
+ const space2Created = await holoSphere.getGlobal('spaces', space2.spacename);
57
+
58
+ if (!space1Created || !space2Created) {
59
+ throw new Error('Failed to create test spaces');
60
+ }
61
+
62
+ // Wait for everything to settle
63
+ await new Promise(resolve => setTimeout(resolve, 2000));
64
+ }, 30000);
65
+
66
+ beforeEach(async () => {
67
+ // Clean up any existing federation data
68
+ await holoSphere.deleteAllGlobal('federation');
69
+
70
+ // Clean up any existing test data
71
+ await holoSphere.deleteAll(testHolon, testLens);
72
+
73
+ // Set up base schema for all tests
74
+ const baseSchema = {
75
+ type: 'object',
76
+ properties: {
77
+ id: { type: 'string' },
78
+ name: { type: 'string' },
79
+ data: { type: 'string' },
80
+ federation: {
81
+ type: 'object',
82
+ properties: {
83
+ origin: { type: 'string' },
84
+ timestamp: { type: 'number' }
85
+ }
86
+ }
87
+ },
88
+ required: ['id']
89
+ };
90
+
91
+ await holoSphere.setSchema(testLens, baseSchema);
92
+ await strictHoloSphere.setSchema(testLens, baseSchema);
93
+
94
+ // Wait for schema to be set
95
+ await new Promise(resolve => setTimeout(resolve, 1000));
96
+
97
+ // Verify spaces exist before proceeding
98
+ const space1Exists = await holoSphere.getGlobal('spaces', space1.spacename);
99
+ const space2Exists = await holoSphere.getGlobal('spaces', space2.spacename);
100
+
101
+ // If spaces don't exist, recreate them
102
+ if (!space1Exists) {
103
+ try {
104
+ await holoSphere.createSpace(space1.spacename, space1.password);
105
+ await new Promise(resolve => setTimeout(resolve, 2000));
106
+ } catch (error) {
107
+ if (error.message !== 'Space already exists') {
108
+ throw error;
109
+ }
110
+ }
111
+ }
112
+ if (!space2Exists) {
113
+ try {
114
+ await holoSphere.createSpace(space2.spacename, space2.password);
115
+ await new Promise(resolve => setTimeout(resolve, 2000));
116
+ } catch (error) {
117
+ if (error.message !== 'Space already exists') {
118
+ throw error;
119
+ }
120
+ }
121
+ }
122
+
123
+ // Wait for space creation to complete
124
+ await new Promise(resolve => setTimeout(resolve, 2000));
125
+
126
+ // Verify spaces again
127
+ const space1Verified = await holoSphere.getGlobal('spaces', space1.spacename);
128
+ const space2Verified = await holoSphere.getGlobal('spaces', space2.spacename);
129
+
130
+ if (!space1Verified || !space2Verified) {
131
+ throw new Error('Test spaces not found - test environment not properly set up');
132
+ }
133
+
134
+ // Ensure both instances are logged out
135
+ if (holoSphere.currentSpace) {
136
+ await holoSphere.logout();
137
+ }
138
+ if (strictHoloSphere.currentSpace) {
139
+ await strictHoloSphere.logout();
140
+ }
141
+
142
+ // Login as first space to holoSphere
143
+ await holoSphere.login(space1.spacename, space1.password);
144
+
145
+ // Wait for login to complete
146
+ await new Promise(resolve => setTimeout(resolve, 2000));
147
+ }, 20000);
148
+
149
+ test('should create federation relationship between spaces', async () => {
150
+ // Create federation relationship
151
+ await holoSphere.federate(space1.spacename, space2.spacename);
152
+
153
+ // Wait for federation to be established
154
+ await new Promise(resolve => setTimeout(resolve, 1000));
155
+
156
+ // Verify federation was created
157
+ const fedInfo = await holoSphere.getFederation(space1.spacename);
158
+ expect(fedInfo).toBeDefined();
159
+ expect(fedInfo.federation).toContain(space2.spacename);
160
+ }, 10000);
161
+
162
+ test('should establish bidirectional federation', async () => {
163
+ // Create bidirectional federation
164
+ await holoSphere.federate(space1.spacename, space2.spacename);
165
+
166
+ // Wait for federation to be established
167
+ await new Promise(resolve => setTimeout(resolve, 2000));
168
+
169
+ // Login to space1 to verify federation
170
+ await holoSphere.login(space1.spacename, space1.password);
171
+
172
+ // Verify both spaces are federated with each other
173
+ const fedInfo1 = await holoSphere.getFederation(space1.spacename);
174
+ const fedInfo2 = await holoSphere.getFederation(space2.spacename);
175
+
176
+ expect(fedInfo1).toBeDefined();
177
+ expect(fedInfo2).toBeDefined();
178
+ expect(fedInfo1.federation).toContain(space2.spacename);
179
+ expect(fedInfo2.notify).toContain(space1.spacename);
180
+ }, 10000);
181
+
182
+ test('should prevent duplicate federation relationships', async () => {
183
+ // Login to space1
184
+ await holoSphere.login(space1.spacename, space1.password);
185
+
186
+ // Create initial federation
187
+ await holoSphere.federate(space1.spacename, space2.spacename);
188
+
189
+ // Wait for federation to be established
190
+ await new Promise(resolve => setTimeout(resolve, 3000));
191
+
192
+ // Verify federation exists
193
+ const fedInfo = await holoSphere.getFederation(space1.spacename);
194
+ expect(fedInfo).toBeDefined();
195
+ expect(fedInfo.federation).toBeDefined();
196
+ expect(fedInfo.federation).toContain(space2.spacename);
197
+
198
+ // Attempt to create duplicate federation
199
+ await expect(holoSphere.federate(space1.spacename, space2.spacename))
200
+ .rejects.toThrow('Federation already exists');
201
+ }, 15000);
202
+
203
+ test('should handle federation data propagation', async () => {
204
+ // Login to space1
205
+ await holoSphere.login(space1.spacename, space1.password);
206
+
207
+ const testData = {
208
+ id: 'test1',
209
+ name: 'Test Item',
210
+ data: 'federated content'
211
+ };
212
+
213
+ // Set up federation
214
+ await holoSphere.federate(space1.spacename, space2.spacename);
215
+
216
+ // Wait for federation to be established
217
+ await new Promise(resolve => setTimeout(resolve, 5000));
218
+
219
+ // Login to space2 with strict instance
220
+ await strictHoloSphere.login(space2.spacename, space2.password);
221
+
222
+ // Put data in first space
223
+ await holoSphere.put(testHolon, testLens, testData);
224
+
225
+ // Wait for propagation
226
+ await new Promise(resolve => setTimeout(resolve, 5000));
227
+
228
+ // Verify data was propagated to federated space
229
+ const federatedData = await strictHoloSphere.get(testHolon, testLens, testData.id);
230
+ expect(federatedData).toBeDefined();
231
+ expect(federatedData).not.toBeNull();
232
+ expect(federatedData.data).toEqual(testData.data);
233
+ expect(federatedData.federation).toBeDefined();
234
+ expect(federatedData.federation.origin).toEqual(space1.spacename);
235
+ expect(federatedData.federation.timestamp).toBeGreaterThan(0);
236
+ }, 20000);
237
+
238
+ test('should handle different getFederated modalities', async () => {
239
+ // Clean up any existing test data first
240
+ await holoSphere.deleteAll(testHolon, testLens);
241
+ await strictHoloSphere.deleteAll(testHolon, testLens);
242
+
243
+ // Wait for cleanup to complete
244
+ await new Promise(resolve => setTimeout(resolve, 5000));
245
+
246
+ // Set up federation using non-strict instance (already logged in as space1)
247
+ await holoSphere.federate(space1.spacename, space2.spacename);
248
+
249
+ // Login to space2 with strict instance
250
+ await strictHoloSphere.login(space2.spacename, space2.password);
251
+
252
+ // Wait for federation to be established
253
+ await new Promise(resolve => setTimeout(resolve, 5000));
254
+
255
+ // Test data with overlapping IDs and different fields
256
+ const testData1 = {
257
+ id: 'user1',
258
+ name: 'User One',
259
+ received: 10,
260
+ sent: 5,
261
+ wants: ['item1', 'item2'],
262
+ offers: ['service1']
263
+ };
264
+ const testData2 = {
265
+ id: 'user1', // Same ID as testData1
266
+ name: 'User One Updated',
267
+ received: 15,
268
+ sent: 8,
269
+ wants: ['item3'],
270
+ offers: ['service2']
271
+ };
272
+ const testData3 = {
273
+ id: 'user2',
274
+ name: 'User Two',
275
+ received: 20,
276
+ sent: 12,
277
+ wants: ['item4'],
278
+ offers: ['service3']
279
+ };
280
+
281
+ // Put data using both instances and wait between puts
282
+ console.log('Putting test data 1:', JSON.stringify(testData1, null, 2));
283
+ await holoSphere.put(testHolon, testLens, testData1);
284
+ await new Promise(resolve => setTimeout(resolve, 5000));
285
+
286
+ console.log('Putting test data 2:', JSON.stringify(testData2, null, 2));
287
+ await strictHoloSphere.put(testHolon, testLens, testData2);
288
+ await new Promise(resolve => setTimeout(resolve, 5000));
289
+
290
+ console.log('Putting test data 3:', JSON.stringify(testData3, null, 2));
291
+ await holoSphere.put(testHolon, testLens, testData3);
292
+
293
+ // Wait longer for data propagation
294
+ await new Promise(resolve => setTimeout(resolve, 10000));
295
+
296
+ // Test 1: Simple concatenation without deduplication
297
+ const concatenatedResults = await holoSphere.getFederated(testHolon, testLens, {
298
+ aggregate: false,
299
+ removeDuplicates: false
300
+ });
301
+ console.log('Concatenated results:', JSON.stringify(concatenatedResults, null, 2));
302
+
303
+ // Verify we have all items including duplicates
304
+ expect(concatenatedResults.filter(item => item.id === 'user1').length).toBeGreaterThanOrEqual(1);
305
+ expect(concatenatedResults.filter(item => item.id === 'user2').length).toBe(1);
306
+
307
+ // Test 2: With deduplication
308
+ const dedupedResults = await strictHoloSphere.getFederated(testHolon, testLens, {
309
+ aggregate: false,
310
+ removeDuplicates: true
311
+ });
312
+ console.log('Deduped results:', JSON.stringify(dedupedResults, null, 2));
313
+ expect(dedupedResults.length).toBe(2);
314
+ const user1Deduped = dedupedResults.find(item => item.id === 'user1');
315
+ expect(user1Deduped.name).toBe('User One Updated');
316
+
317
+ // Test 3: With aggregation
318
+ const aggregationOptions = {
319
+ aggregate: true,
320
+ idField: 'id',
321
+ sumFields: ['received', 'sent'],
322
+ concatArrays: ['wants', 'offers'],
323
+ removeDuplicates: true
324
+ };
325
+ console.log('Aggregation options:', JSON.stringify(aggregationOptions, null, 2));
326
+ const aggregatedResults = await holoSphere.getFederated(testHolon, testLens, aggregationOptions);
327
+ console.log('Aggregated results:', JSON.stringify(aggregatedResults, null, 2));
328
+
329
+ // Sort results by ID for consistent testing
330
+ const sortedResults = aggregatedResults.sort((a, b) => a.id.localeCompare(b.id));
331
+ expect(sortedResults.length).toBe(2);
332
+
333
+ const user1Aggregated = sortedResults.find(item => item.id === 'user1');
334
+ expect(user1Aggregated).toBeDefined();
335
+ expect(user1Aggregated.received).toBe(15); // Latest value
336
+ expect(user1Aggregated.sent).toBe(8); // Latest value
337
+ expect(user1Aggregated.wants).toEqual(['item3']); // Latest value
338
+ expect(user1Aggregated.offers).toEqual(['service2']); // Latest value
339
+
340
+ const user2Aggregated = sortedResults.find(item => item.id === 'user2');
341
+ expect(user2Aggregated).toBeDefined();
342
+ expect(user2Aggregated.received).toBe(20);
343
+ expect(user2Aggregated.sent).toBe(12);
344
+ expect(user2Aggregated.wants).toEqual(['item4']);
345
+ expect(user2Aggregated.offers).toEqual(['service3']);
346
+ }, 60000);
347
+
348
+ test('should handle unfederation', async () => {
349
+ // Login to space1
350
+ await holoSphere.login(space1.spacename, space1.password);
351
+
352
+ // Set up federation
353
+ await holoSphere.federate(space1.spacename, space2.spacename);
354
+
355
+ // Wait for federation to be established
356
+ await new Promise(resolve => setTimeout(resolve, 5000));
357
+
358
+ // Verify federation exists
359
+ let fedInfo1 = await holoSphere.getFederation(space1.spacename);
360
+ expect(fedInfo1).toBeDefined();
361
+ expect(fedInfo1.federation).toBeDefined();
362
+ expect(fedInfo1.federation).toContain(space2.spacename);
363
+
364
+ // Remove federation
365
+ await holoSphere.unfederate(space1.spacename, space2.spacename);
366
+
367
+ // Wait for unfederation to complete
368
+ await new Promise(resolve => setTimeout(resolve, 5000));
369
+
370
+ // Verify federation is removed
371
+ fedInfo1 = await holoSphere.getFederation(space1.spacename);
372
+ const fedInfo2 = await holoSphere.getFederation(space2.spacename);
373
+
374
+ expect(fedInfo1).toBeDefined();
375
+ expect(fedInfo2).toBeDefined();
376
+ expect(fedInfo1.federation || []).not.toContain(space2.spacename);
377
+ expect(fedInfo2.notify || []).not.toContain(space1.spacename);
378
+ }, 20000);
379
+
380
+ afterEach(async () => {
381
+ // Clean up test data
382
+ await holoSphere.deleteAll(testHolon, testLens);
383
+
384
+ // Clean up federation data while still logged in
385
+ await holoSphere.deleteAllGlobal('federation');
386
+
387
+ // Logout from both instances
388
+ if (holoSphere.currentSpace) {
389
+ await holoSphere.logout();
390
+ }
391
+ if (strictHoloSphere.currentSpace) {
392
+ await strictHoloSphere.logout();
393
+ }
394
+
395
+ // Wait for cleanup
396
+ await new Promise(resolve => setTimeout(resolve, 1000));
397
+ });
398
+
399
+ afterAll(async () => {
400
+ // Clean up test spaces
401
+ try {
402
+ await holoSphere.deleteGlobal('spaces', space1.spacename);
403
+ await holoSphere.deleteGlobal('spaces', space2.spacename);
404
+ // Wait for cleanup
405
+ await new Promise(resolve => setTimeout(resolve, 1000));
406
+ } catch (error) {
407
+ console.error('Error during final cleanup:', error.message);
408
+ }
409
+
410
+ // Clean up Gun instances
411
+ if (holoSphere.gun) {
412
+ holoSphere.gun.off();
413
+ }
414
+ if (strictHoloSphere.gun) {
415
+ strictHoloSphere.gun.off();
416
+ }
417
+ });
418
+ });