holosphere 1.1.11 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,364 +0,0 @@
1
- import HoloSphere from '../holosphere.js';
2
- import { jest } from '@jest/globals';
3
-
4
- // Increase the default test timeout
5
- jest.setTimeout(30000);
6
-
7
- describe('Subscription Tests', () => {
8
- let holosphere;
9
- let testAppName; // Make app name dynamic
10
- const testHolonBase = 'test_subscription_holon';
11
- const testLens = 'items';
12
- let testHolon; // Make holon dynamic
13
-
14
- beforeEach(async () => {
15
- // Create a fresh HoloSphere instance with unique names for each test
16
- testAppName = `testApp_${Date.now()}_${Math.random().toString(36).substring(7)}`;
17
- testHolon = `${testHolonBase}_${Date.now()}`;
18
- holosphere = new HoloSphere(testAppName, false);
19
- // Add a small delay after initialization
20
- await new Promise(resolve => setTimeout(resolve, 100));
21
- });
22
-
23
- afterEach(async () => {
24
- // Clean up resources and potentially test data
25
- try {
26
- if (holosphere) {
27
- // Attempt to delete data created in the test holon/lenses
28
- try {
29
- await holosphere.deleteAll(testHolon, testLens);
30
- await holosphere.deleteAll(testHolon, 'differentLens');
31
- // Wait a bit for deletes to process
32
- await new Promise(resolve => setTimeout(resolve, 200));
33
- } catch (deleteError) {
34
- console.warn(`Error during test cleanup deleteAll:`, deleteError);
35
- }
36
- await holosphere.close();
37
- }
38
- } catch (error) {
39
- console.warn('Error during afterEach cleanup:', error);
40
- }
41
- // jest.clearAllMocks(); // Not needed if not using jest.fn()
42
- });
43
-
44
- test('should properly clean up subscription when unsubscribing', async () => {
45
- // Create test data
46
- const testData = {
47
- id: 'test-item',
48
- value: 'Test value'
49
- };
50
-
51
- // Create a mock callback function
52
- function mockCallback (data) {
53
- console.log('Callback received:', data);
54
- }
55
-
56
- // Set up subscription
57
- const subscription = await holosphere.subscribe(testHolon, testLens, mockCallback);
58
-
59
- // Verify subscription was created and stored
60
- expect(Object.keys(holosphere.subscriptions).length).toBe(1);
61
-
62
- // Get the subscription ID (should be the only key in the subscriptions object)
63
- const subscriptionId = Object.keys(holosphere.subscriptions)[0];
64
-
65
- // Verify the subscription object has the expected structure
66
- expect(holosphere.subscriptions[subscriptionId]).toBeDefined();
67
- expect(holosphere.subscriptions[subscriptionId].holon).toBe(testHolon);
68
- expect(holosphere.subscriptions[subscriptionId].lens).toBe(testLens);
69
- expect(holosphere.subscriptions[subscriptionId].callback).toBe(mockCallback);
70
- expect(holosphere.subscriptions[subscriptionId].mapChain).toBeDefined();
71
-
72
- // Now unsubscribe
73
- await subscription.unsubscribe();
74
-
75
- // Verify the subscription was properly cleaned up
76
- expect(Object.keys(holosphere.subscriptions).length).toBe(0);
77
- expect(holosphere.subscriptions[subscriptionId]).toBeUndefined();
78
- });
79
-
80
- test('should not receive updates after unsubscribing', async () => {
81
- // Create test data
82
- const testData = {
83
- id: 'test-item-unsub',
84
- value: 'Initial value for unsub test'
85
- };
86
-
87
- // Use a flag to track callback execution
88
- let callbackFired = false;
89
- let receivedData = null;
90
- const dataCallback = (data) => {
91
- console.log('Callback received (unsub test):', data);
92
- callbackFired = true;
93
- receivedData = data;
94
- };
95
-
96
- // Set up subscription
97
- const subscription = await holosphere.subscribe(testHolon, testLens, dataCallback);
98
-
99
- // Store data to trigger subscription
100
- await holosphere.put(testHolon, testLens, testData);
101
-
102
- // Wait a bit for the subscription to trigger
103
- await new Promise(resolve => setTimeout(resolve, 1000));
104
-
105
- // Verify callback was called
106
- expect(callbackFired).toBe(true);
107
- expect(receivedData).toEqual(expect.objectContaining(testData));
108
-
109
- // Reset the flag
110
- callbackFired = false;
111
- receivedData = null;
112
-
113
- // Unsubscribe
114
- await subscription.unsubscribe();
115
-
116
- // Store more data
117
- const newData = { ...testData, value: 'Updated value after unsub' };
118
- await holosphere.put(testHolon, testLens, newData);
119
-
120
- // Wait a bit to ensure no callbacks are triggered
121
- await new Promise(resolve => setTimeout(resolve, 1000));
122
-
123
- // Verify callback was NOT called after unsubscribing
124
- expect(callbackFired).toBe(false);
125
- expect(receivedData).toBeNull();
126
- });
127
-
128
- test('should handle multiple subscriptions and unsubscriptions correctly', async () => {
129
- // Use flags to track callback execution
130
- let callback1Fired = false;
131
- let callback2Fired = false;
132
- let callback3Fired = false;
133
- let dataForCb1 = null;
134
-
135
- const cb1 = (data) => {
136
- console.log('Callback 1 received (multi test):', data);
137
- callback1Fired = true;
138
- dataForCb1 = data;
139
- };
140
- const cb2 = (data) => {
141
- console.log('Callback 2 received (multi test):', data);
142
- callback2Fired = true;
143
- };
144
- const cb3 = (data) => {
145
- console.log('Callback 3 received (multi test):', data);
146
- callback3Fired = true;
147
- };
148
-
149
- // Set up multiple subscriptions
150
- const subscription1 = await holosphere.subscribe(testHolon, testLens, cb1);
151
- const subscription2 = await holosphere.subscribe(testHolon, testLens, cb2);
152
- const subscription3 = await holosphere.subscribe(testHolon, 'differentLens', cb3);
153
-
154
- // Verify subscriptions were created
155
- expect(Object.keys(holosphere.subscriptions).length).toBe(3);
156
-
157
- // Unsubscribe from one subscription (subscription2)
158
- await subscription2.unsubscribe();
159
-
160
- // Verify only the correct subscription was removed
161
- expect(Object.keys(holosphere.subscriptions).length).toBe(2);
162
-
163
- // Create test data
164
- const testData = {
165
- id: 'test-item-multi',
166
- value: 'Test value for multi sub'
167
- };
168
-
169
- // Store data to trigger remaining subscriptions
170
- await holosphere.put(testHolon, testLens, testData);
171
-
172
- // Wait a bit for the subscription to trigger
173
- await new Promise(resolve => setTimeout(resolve, 1000));
174
-
175
- // Verify only active callbacks were called
176
- expect(callback1Fired).toBe(true);
177
- expect(dataForCb1).toEqual(expect.objectContaining(testData));
178
- expect(callback2Fired).toBe(false); // This callback should not have fired
179
- expect(callback3Fired).toBe(false); // Different lens
180
-
181
- // Unsubscribe from all remaining subscriptions
182
- await subscription1.unsubscribe();
183
- await subscription3.unsubscribe();
184
-
185
- // Verify all subscriptions were removed
186
- expect(Object.keys(holosphere.subscriptions).length).toBe(0);
187
- });
188
-
189
- test('should clean up subscriptions when closing HoloSphere instance', async () => {
190
- // Use a flag - not strictly needed for assertion but good practice
191
- let callbackFired = false;
192
- const dataCallback = (data) => {
193
- console.log('Callback received (close test):', data);
194
- callbackFired = true;
195
- };
196
-
197
- // Set up subscription
198
- await holosphere.subscribe(testHolon, testLens, dataCallback);
199
-
200
- // Verify subscription was created
201
- expect(Object.keys(holosphere.subscriptions).length).toBe(1);
202
-
203
- // Close the HoloSphere instance
204
- await holosphere.close();
205
-
206
- // Verify subscriptions were cleaned up
207
- expect(Object.keys(holosphere.subscriptions).length).toBe(0);
208
- });
209
-
210
- test('should not leak memory after multiple subscription/unsubscription cycles', async () => {
211
- // Record initial memory usage
212
- const initialMemoryUsage = process.memoryUsage();
213
-
214
- // Create 100 subscriptions and immediately unsubscribe from them
215
- for (let i = 0; i < 100; i++) {
216
- // Create a proper callback function instead of a jest mock
217
- const mockCallback = (data) => {
218
- // Simple callback implementation
219
- console.log('Data received:', data);
220
- };
221
-
222
- const subscription = await holosphere.subscribe(testHolon, `${testLens}_${i}`, mockCallback);
223
-
224
- // Verify subscription was created
225
- expect(Object.keys(holosphere.subscriptions).length).toBe(1);
226
-
227
- // Unsubscribe
228
- await subscription.unsubscribe();
229
-
230
- // Verify subscription was removed
231
- expect(Object.keys(holosphere.subscriptions).length).toBe(0);
232
- }
233
-
234
- // Force garbage collection if possible (Node.js with --expose-gc flag)
235
- if (global.gc) {
236
- global.gc();
237
- }
238
-
239
- // Record final memory usage
240
- const finalMemoryUsage = process.memoryUsage();
241
-
242
- // Check if heap usage has significantly increased (more than 50MB would be suspicious)
243
- const heapIncrease = finalMemoryUsage.heapUsed - initialMemoryUsage.heapUsed;
244
- console.log(`Memory increase after subscriptions: ${heapIncrease / 1024 / 1024} MB`);
245
-
246
- // The exact threshold depends on your environment, but we expect it to be relatively small
247
- // This is a loose check, as garbage collection is unpredictable
248
- expect(heapIncrease).toBeLessThan(50 * 1024 * 1024); // Less than 50MB increase
249
-
250
- // Verify all subscriptions are cleaned up
251
- expect(Object.keys(holosphere.subscriptions).length).toBe(0);
252
- });
253
-
254
- test('should properly handle subscription to data with references', async () => {
255
- // Use simple holon names that don't require H3 formats
256
- const originHolon = `origin_${Date.now()}`;
257
- const referenceHolon = `reference_${Date.now()}`;
258
-
259
- // Create actual data to store in the origin holon
260
- const originalData = {
261
- id: `test-data-${Date.now()}`,
262
- title: 'Original Data',
263
- content: 'This is the original data content',
264
- timestamp: Date.now()
265
- };
266
-
267
- // Store the data in the origin holon
268
- await holosphere.put(originHolon, testLens, originalData);
269
-
270
- // Create a reference manually
271
- const soulPath = `${holosphere.appname}/${originHolon}/${testLens}/${originalData.id}`;
272
- const referenceData = {
273
- id: originalData.id,
274
- soul: soulPath
275
- };
276
-
277
- // Store the reference in the reference holon
278
- await holosphere.put(referenceHolon, testLens, referenceData);
279
-
280
- // Create a data collection to store received data
281
- const receivedData = [];
282
-
283
- // Create a real callback that stores the data
284
- const dataCallback = (data) => {
285
- if (data) {
286
- receivedData.push(data);
287
- }
288
- };
289
-
290
- // Subscribe to the reference holon
291
- const subscription = await holosphere.subscribe(referenceHolon, testLens, dataCallback);
292
-
293
- // Wait for subscription to receive data
294
- await new Promise(resolve => setTimeout(resolve, 1000));
295
-
296
- // Update the original data
297
- const updatedData = {
298
- ...originalData,
299
- title: 'Updated Data',
300
- content: 'This content has been updated',
301
- timestamp: Date.now()
302
- };
303
-
304
- // Update the data in the original location
305
- await holosphere.put(originHolon, testLens, updatedData);
306
-
307
- // Wait for update to propagate through reference
308
- await new Promise(resolve => setTimeout(resolve, 2000));
309
-
310
- // Unsubscribe to clean up
311
- await subscription.unsubscribe();
312
-
313
- // Verify the subscription was properly cleaned up
314
- expect(Object.keys(holosphere.subscriptions).length).toBe(0);
315
-
316
- // Verify that data was received
317
- console.log('Received data:', receivedData);
318
- expect(receivedData.length).toBeGreaterThan(0);
319
-
320
- // Get the data directly from the reference holon
321
- // This should be the reference object with a soul property
322
- const directData = await holosphere.get(referenceHolon, testLens, originalData.id);
323
- console.log('Direct data from reference holon:', directData);
324
-
325
- // Check if we got the reference back
326
- if (directData) {
327
- // The reference might be auto-resolved, in which case it will have a _federation property
328
- // instead of a direct soul property
329
- if (directData._federation) {
330
- expect(directData._federation).toBeTruthy();
331
- expect(directData._federation.soul).toEqual(soulPath);
332
- expect(directData._federation.resolved).toBe(true);
333
- } else if (directData.soul) {
334
- // Or it might be a direct reference
335
- expect(directData.soul).toBeTruthy();
336
- expect(directData.soul).toEqual(soulPath);
337
- }
338
- }
339
-
340
- // Try to resolve the reference to verify it works correctly
341
- const resolvedData = await holosphere.get(referenceHolon, testLens, originalData.id, null, { resolveHolograms: true });
342
- console.log('Resolved data:', resolvedData);
343
-
344
- // The resolved data should not be null
345
- expect(resolvedData).toBeTruthy();
346
-
347
- // If the data was properly resolved, it should match the updated data
348
- if (resolvedData && resolvedData.title) {
349
- expect(resolvedData.title).toBe(updatedData.title);
350
- expect(resolvedData.content).toBe(updatedData.content);
351
- }
352
-
353
- // Now try to get the original data directly
354
- const originalDataRetrieved = await holosphere.get(originHolon, testLens, originalData.id);
355
- console.log('Original data retrieved:', originalDataRetrieved);
356
-
357
- // Verify it was updated
358
- expect(originalDataRetrieved).toBeTruthy();
359
- if (originalDataRetrieved) {
360
- expect(originalDataRetrieved.title).toBe(updatedData.title);
361
- expect(originalDataRetrieved.content).toBe(updatedData.content);
362
- }
363
- });
364
- });