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.
- package/FEDERATION.md +265 -0
- package/babel.config.js +5 -0
- package/federation.js +723 -0
- package/holosphere.js +905 -1074
- package/package.json +3 -1
- package/services/environmentalApi.js +253 -0
- package/services/environmentalApitest.js +164 -0
- package/test/ai.test.js +268 -76
- package/test/federation.test.js +115 -356
- package/test/holonauth.test.js +241 -0
- package/test/holosphere.test.js +111 -991
- package/test/sea.html +33 -0
- package/test/spacesauth.test.js +0 -335
package/test/holosphere.test.js
CHANGED
|
@@ -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-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
let
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
await
|
|
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
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
49
|
-
|
|
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('
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
112
|
-
const
|
|
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
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
324
|
-
|
|
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
|
|
333
|
-
expect(
|
|
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
|
|
339
|
-
|
|
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
|
-
|
|
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('
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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.
|
|
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
|
|
797
|
-
const
|
|
120
|
+
test('should handle data operations gracefully', async () => {
|
|
121
|
+
const testData = { id: 'test3', value: 'test data' };
|
|
798
122
|
|
|
799
|
-
//
|
|
800
|
-
const
|
|
801
|
-
|
|
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
|
-
//
|
|
815
|
-
|
|
816
|
-
const
|
|
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
|
-
|
|
877
|
-
|
|
878
|
-
const
|
|
879
|
-
|
|
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
|
|
891
|
-
|
|
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
|
-
|
|
895
|
-
|
|
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
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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('
|
|
942
|
-
|
|
943
|
-
|
|
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
|
-
//
|
|
1005
|
-
|
|
1006
|
-
expect(
|
|
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
|
-
|
|
1017
|
-
|
|
1018
|
-
const
|
|
1019
|
-
|
|
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
|
-
|
|
1024
|
-
|
|
1025
|
-
const
|
|
1026
|
-
expect(
|
|
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
|
});
|