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