holosphere 1.1.9 → 1.1.11
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/.cursor/rules/futura.mdc +55 -0
- package/FEDERATION.md +17 -17
- package/compute.js +289 -0
- package/content.js +797 -0
- package/examples/federation.js +98 -90
- package/examples/{references.js → holograms.js} +49 -51
- package/federation.js +307 -197
- package/global.js +560 -0
- package/hologram.js +156 -0
- package/holosphere.d.ts +94 -7
- package/holosphere.js +211 -1464
- package/node.js +155 -0
- package/package.json +2 -5
- package/schema.js +132 -0
- package/test/auth.test.js +85 -51
- package/test/delete.test.js +15 -11
- package/test/federation.test.js +179 -0
- package/test/hologram.test.js +316 -0
- package/test/holosphere.test.js +189 -5
- package/test/subscription.test.js +364 -0
- package/utils.js +290 -0
package/test/federation.test.js
CHANGED
|
@@ -88,6 +88,135 @@ describe('Federation Tests', () => {
|
|
|
88
88
|
await expect(holosphere.federate(space, space, null))
|
|
89
89
|
.rejects.toThrow('Cannot federate a space with itself');
|
|
90
90
|
});
|
|
91
|
+
|
|
92
|
+
test('should create a federation with lens-specific settings', async () => {
|
|
93
|
+
const space1 = `${testPrefix}lens_space1`;
|
|
94
|
+
const space2 = `${testPrefix}lens_space2`;
|
|
95
|
+
|
|
96
|
+
// Create federation with specific lens configuration
|
|
97
|
+
const lensConfig = {
|
|
98
|
+
federate: ['quests', 'announcements'],
|
|
99
|
+
notify: ['quests']
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const result = await holosphere.federate(space1, space2, null, null, true, lensConfig);
|
|
103
|
+
expect(result).toBe(true);
|
|
104
|
+
|
|
105
|
+
// Verify federation info contains lens configuration
|
|
106
|
+
const fedInfo = await holosphere.getFederation(space1);
|
|
107
|
+
expect(fedInfo).toBeTruthy();
|
|
108
|
+
expect(fedInfo.lensConfig).toBeTruthy();
|
|
109
|
+
expect(fedInfo.lensConfig[space2]).toBeTruthy();
|
|
110
|
+
expect(fedInfo.lensConfig[space2].federate).toEqual(['quests', 'announcements']);
|
|
111
|
+
expect(fedInfo.lensConfig[space2].notify).toEqual(['quests']);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('should respect lens configuration during propagation', async () => {
|
|
115
|
+
const space1 = `${testPrefix}prop_space1`;
|
|
116
|
+
const space2 = `${testPrefix}prop_space2`;
|
|
117
|
+
|
|
118
|
+
// Create federation with specific lens configuration
|
|
119
|
+
const lensConfig = {
|
|
120
|
+
federate: ['quests', 'announcements'],
|
|
121
|
+
notify: ['quests']
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
await holosphere.federate(space1, space2, null, null, true, lensConfig);
|
|
125
|
+
|
|
126
|
+
// Test propagation for allowed lens
|
|
127
|
+
const questData = { id: 'test-quest', title: 'Test Quest' };
|
|
128
|
+
const questResult = await holosphere.propagate(space1, 'quests', questData);
|
|
129
|
+
expect(questResult.success).toBe(1); // Should propagate
|
|
130
|
+
|
|
131
|
+
// Test propagation for non-allowed lens
|
|
132
|
+
const shoppingData = { id: 'test-shopping', item: 'Test Item' };
|
|
133
|
+
const shoppingResult = await holosphere.propagate(space1, 'shopping', shoppingData);
|
|
134
|
+
expect(shoppingResult.success).toBe(0); // Should not propagate
|
|
135
|
+
expect(shoppingResult.message).toContain("Propagation of lens 'shopping' to target space " + space2 + " skipped: lens not in 'federate' configuration.");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('should handle wildcard lens configuration', async () => {
|
|
139
|
+
const space1 = `${testPrefix}wild_space1`;
|
|
140
|
+
const space2 = `${testPrefix}wild_space2`;
|
|
141
|
+
|
|
142
|
+
// Create federation with wildcard lens configuration
|
|
143
|
+
const lensConfig = {
|
|
144
|
+
federate: ['*'],
|
|
145
|
+
notify: ['quests', 'announcements']
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
await holosphere.federate(space1, space2, null, null, true, lensConfig);
|
|
149
|
+
|
|
150
|
+
// Test propagation for various lenses
|
|
151
|
+
const testData = { id: 'test-item', value: 'test' };
|
|
152
|
+
|
|
153
|
+
// Should propagate for quests (in notify list)
|
|
154
|
+
const questResult = await holosphere.propagate(space1, 'quests', testData);
|
|
155
|
+
expect(questResult.success).toBe(1);
|
|
156
|
+
|
|
157
|
+
// Should propagate for announcements (in notify list)
|
|
158
|
+
const announcementResult = await holosphere.propagate(space1, 'announcements', testData);
|
|
159
|
+
expect(announcementResult.success).toBe(1);
|
|
160
|
+
|
|
161
|
+
// Should now propagate for other lenses because federate is ['*'] and space2 is in space1's notify list.
|
|
162
|
+
const otherResult = await holosphere.propagate(space1, 'shopping', testData);
|
|
163
|
+
expect(otherResult.success).toBe(1);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('should handle bidirectional lens configuration correctly', async () => {
|
|
167
|
+
const space1 = `${testPrefix}bi_space1`;
|
|
168
|
+
const space2 = `${testPrefix}bi_space2`;
|
|
169
|
+
|
|
170
|
+
// Create federation with different lens configs for each direction
|
|
171
|
+
const lensConfig = {
|
|
172
|
+
federate: ['quests', 'announcements'],
|
|
173
|
+
notify: ['quests']
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
await holosphere.federate(space1, space2, null, null, true, lensConfig);
|
|
177
|
+
|
|
178
|
+
// Verify both spaces have correct lens configuration
|
|
179
|
+
const fedInfo1 = await holosphere.getFederation(space1);
|
|
180
|
+
const fedInfo2 = await holosphere.getFederation(space2);
|
|
181
|
+
|
|
182
|
+
expect(fedInfo1.lensConfig[space2].federate).toEqual(['quests', 'announcements']);
|
|
183
|
+
expect(fedInfo1.lensConfig[space2].notify).toEqual(['quests']);
|
|
184
|
+
expect(fedInfo2.lensConfig[space1].federate).toEqual(['quests', 'announcements']);
|
|
185
|
+
expect(fedInfo2.lensConfig[space1].notify).toEqual(['quests']);
|
|
186
|
+
|
|
187
|
+
// Test propagation in both directions
|
|
188
|
+
const testData = { id: 'test-item', value: 'test' };
|
|
189
|
+
|
|
190
|
+
// Space1 to Space2
|
|
191
|
+
const result1 = await holosphere.propagate(space1, 'quests', testData);
|
|
192
|
+
expect(result1.success).toBe(1);
|
|
193
|
+
|
|
194
|
+
// Space2 to Space1
|
|
195
|
+
const result2 = await holosphere.propagate(space2, 'quests', testData);
|
|
196
|
+
expect(result2.success).toBe(1);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('should handle unidirectional lens configuration', async () => {
|
|
200
|
+
const space1 = `${testPrefix}uni_space1`;
|
|
201
|
+
const space2 = `${testPrefix}uni_space2`;
|
|
202
|
+
|
|
203
|
+
// Create federation with unidirectional lens config
|
|
204
|
+
const lensConfig = {
|
|
205
|
+
federate: ['quests', 'announcements'],
|
|
206
|
+
notify: ['quests']
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
await holosphere.federate(space1, space2, null, null, false, lensConfig);
|
|
210
|
+
|
|
211
|
+
// Verify lens configuration is only set for one direction
|
|
212
|
+
const fedInfo1 = await holosphere.getFederation(space1);
|
|
213
|
+
const fedInfo2 = await holosphere.getFederation(space2);
|
|
214
|
+
|
|
215
|
+
expect(fedInfo1.lensConfig[space2].federate).toEqual(['quests', 'announcements']);
|
|
216
|
+
expect(fedInfo1.lensConfig[space2].notify).toEqual(['quests']);
|
|
217
|
+
expect(fedInfo2.lensConfig[space1].federate).toEqual([]);
|
|
218
|
+
expect(fedInfo2.lensConfig[space1].notify).toEqual([]);
|
|
219
|
+
});
|
|
91
220
|
});
|
|
92
221
|
|
|
93
222
|
describe('unfederate', () => {
|
|
@@ -167,4 +296,54 @@ describe('Federation Tests', () => {
|
|
|
167
296
|
expect(result.message).toBeDefined();
|
|
168
297
|
});
|
|
169
298
|
});
|
|
299
|
+
|
|
300
|
+
describe('getFederatedConfig', () => {
|
|
301
|
+
test('should retrieve the correct lens configuration for a specific federation link', async () => {
|
|
302
|
+
const space1 = `${testPrefix}lens_config_space1`;
|
|
303
|
+
const space2 = `${testPrefix}lens_config_space2`;
|
|
304
|
+
const space3 = `${testPrefix}lens_config_space3`; // Another space for testing
|
|
305
|
+
|
|
306
|
+
const specificLensConfig = {
|
|
307
|
+
federate: ['books', 'movies'],
|
|
308
|
+
notify: ['books']
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
// Federate space1 with space2 with specific lens config
|
|
312
|
+
await holosphere.federate(space1, space2, null, null, true, specificLensConfig);
|
|
313
|
+
// Federate space1 with space3 with default (empty or wildcard) lens config
|
|
314
|
+
await holosphere.federate(space1, space3, null, null, true, {});
|
|
315
|
+
|
|
316
|
+
// Test 1: Get specific lens config for space1 -> space2
|
|
317
|
+
const retrievedConfig1to2 = await holosphere.getFederatedConfig(space1, space2);
|
|
318
|
+
expect(retrievedConfig1to2).toEqual(specificLensConfig);
|
|
319
|
+
|
|
320
|
+
// Test 2: Get lens config for space1 -> space3 (should be default/empty or null)
|
|
321
|
+
// Depending on implementation, it might return null or { federate: [], notify: [] }
|
|
322
|
+
// For now, let's check if it's not the specific one and is an object (or null)
|
|
323
|
+
const retrievedConfig1to3 = await holosphere.getFederatedConfig(space1, space3);
|
|
324
|
+
expect(retrievedConfig1to3).not.toEqual(specificLensConfig);
|
|
325
|
+
if (retrievedConfig1to3 !== null) {
|
|
326
|
+
expect(typeof retrievedConfig1to3).toBe('object');
|
|
327
|
+
expect(Array.isArray(retrievedConfig1to3.federate)).toBe(true);
|
|
328
|
+
expect(Array.isArray(retrievedConfig1to3.notify)).toBe(true);
|
|
329
|
+
// Check if it defaulted to empty arrays as per current federate logic for empty lensConfig input
|
|
330
|
+
expect(retrievedConfig1to3.federate).toEqual([]);
|
|
331
|
+
expect(retrievedConfig1to3.notify).toEqual([]);
|
|
332
|
+
} else {
|
|
333
|
+
expect(retrievedConfig1to3).toBeNull();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Test 3: Try to get config for a non-federated link
|
|
337
|
+
const space4 = `${testPrefix}lens_config_space4`;
|
|
338
|
+
const retrievedConfig1to4 = await holosphere.getFederatedConfig(space1, space4);
|
|
339
|
+
expect(retrievedConfig1to4).toBeNull();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test('should return null if no federation info exists for the source space', async () => {
|
|
343
|
+
const nonExistentSpace = `${testPrefix}non_existent_space`;
|
|
344
|
+
const targetSpace = `${testPrefix}any_target_space`;
|
|
345
|
+
const config = await holosphere.getFederatedConfig(nonExistentSpace, targetSpace);
|
|
346
|
+
expect(config).toBeNull();
|
|
347
|
+
});
|
|
348
|
+
});
|
|
170
349
|
});
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import HoloSphere from '../holosphere.js';
|
|
2
|
+
import { jest } from '@jest/globals';
|
|
3
|
+
|
|
4
|
+
// Configure timeout
|
|
5
|
+
jest.setTimeout(30000); // 30 second timeout
|
|
6
|
+
|
|
7
|
+
// Utility to wait for GunDB propagation
|
|
8
|
+
const waitForGun = (delay = 250) => new Promise(resolve => setTimeout(resolve, delay));
|
|
9
|
+
|
|
10
|
+
// Setup
|
|
11
|
+
describe('HoloSphere Reference System', () => {
|
|
12
|
+
let holoSphere;
|
|
13
|
+
const appName = 'test-hologram-app'; // Update app name
|
|
14
|
+
const testHolon = 'hologramTestHolon'; // Update holon name
|
|
15
|
+
const testLens = 'testLens';
|
|
16
|
+
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
// Create a single HoloSphere instance for all tests
|
|
19
|
+
holoSphere = new HoloSphere(appName);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterAll(async () => {
|
|
23
|
+
// Clean up after all tests
|
|
24
|
+
if (holoSphere) {
|
|
25
|
+
await holoSphere.close();
|
|
26
|
+
// Wait for connections to close
|
|
27
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should create and parse a hologram correctly', async () => {
|
|
32
|
+
// Test data
|
|
33
|
+
const data = { id: 'ref1', value: 'Original Data' };
|
|
34
|
+
await holoSphere.put(testHolon, testLens, data);
|
|
35
|
+
await waitForGun(); // <-- Add delay
|
|
36
|
+
|
|
37
|
+
// Create a hologram
|
|
38
|
+
const hologram = holoSphere.createHologram(testHolon, testLens, data); // Use renamed method
|
|
39
|
+
|
|
40
|
+
expect(hologram).toBeDefined();
|
|
41
|
+
expect(hologram.id).toBe('ref1');
|
|
42
|
+
expect(hologram.soul).toBe(`${appName}/${testHolon}/${testLens}/ref1`);
|
|
43
|
+
expect(holoSphere.isHologram(hologram)).toBe(true); // Use renamed method
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('isHologram should return false for non-hologram objects', () => { // Rename test description
|
|
47
|
+
expect(holoSphere.isHologram(null)).toBe(false); // Use renamed method
|
|
48
|
+
expect(holoSphere.isHologram(undefined)).toBe(false); // Use renamed method
|
|
49
|
+
expect(holoSphere.isHologram({})).toBe(false); // Use renamed method
|
|
50
|
+
expect(holoSphere.isHologram({ id: 'test' })).toBe(false); // Use renamed method
|
|
51
|
+
expect(holoSphere.isHologram({ soul: 'path' })).toBe(false); // Use renamed method
|
|
52
|
+
expect(holoSphere.isHologram('string')).toBe(false); // Use renamed method
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('should resolve a valid hologram', async () => { // Rename test description
|
|
56
|
+
const data = { id: 'ref2', value: 'Data to Resolve' };
|
|
57
|
+
await holoSphere.put(testHolon, testLens, data);
|
|
58
|
+
await waitForGun(); // <-- Add delay
|
|
59
|
+
const hologram = holoSphere.createHologram(testHolon, testLens, data); // Use renamed method
|
|
60
|
+
|
|
61
|
+
const resolved = await holoSphere.resolveHologram(hologram); // Use renamed method
|
|
62
|
+
|
|
63
|
+
expect(resolved).toBeDefined();
|
|
64
|
+
expect(resolved.id).toBe('ref2');
|
|
65
|
+
expect(resolved.value).toBe('Data to Resolve');
|
|
66
|
+
// Check for updated metadata
|
|
67
|
+
expect(resolved._meta).toBeDefined();
|
|
68
|
+
expect(resolved._meta.resolvedFromHologram).toBe(true);
|
|
69
|
+
expect(resolved._meta.hologramSoul).toBe(hologram.soul);
|
|
70
|
+
expect(resolved.isHologram).toBeUndefined(); // Ensure top-level isHologram is not present
|
|
71
|
+
// Ensure old fields are not in _meta unless they were on originalData
|
|
72
|
+
expect(resolved._meta.isHologram).toBeUndefined();
|
|
73
|
+
expect(resolved._meta.resolved).toBeUndefined();
|
|
74
|
+
// expect(resolved._meta.soul).toBeUndefined(); // hologramSoul is the new field for this
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('resolveHologram should return null for a hologram pointing to non-existent data', async () => { // Rename test description
|
|
78
|
+
const nonExistentData = { id: 'ref-non-existent' }; // Data never stored
|
|
79
|
+
const hologram = holoSphere.createHologram(testHolon, testLens, nonExistentData); // Create hologram for it
|
|
80
|
+
|
|
81
|
+
const resolved = await holoSphere.resolveHologram(hologram); // Use renamed method
|
|
82
|
+
expect(resolved).toBeNull(); // Resolution should fail and return null
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('resolveHologram should return the original object if not a hologram', async () => { // Rename test description
|
|
86
|
+
const plainObject = { id: 'not-a-ref', value: 123 };
|
|
87
|
+
const resolved = await holoSphere.resolveHologram(plainObject); // Use renamed method
|
|
88
|
+
expect(resolved).toBe(plainObject); // Should return the object itself
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Test for nested holograms
|
|
92
|
+
test('should resolve nested holograms correctly', async () => { // Rename test description
|
|
93
|
+
// 1. Original Data stored under its own ID
|
|
94
|
+
const originalData = { id: 'actual-nested-original', value: 'level 0' };
|
|
95
|
+
await holoSphere.put(testHolon, testLens, originalData);
|
|
96
|
+
await waitForGun(750); // <-- Increased delay
|
|
97
|
+
|
|
98
|
+
// 2. Hologram 1 pointing to Original Data, stored under 'hologram1-id'
|
|
99
|
+
const hologram1Data = holoSphere.createHologram(testHolon, testLens, originalData);
|
|
100
|
+
const hologram1Storage = { id: 'hologram1-id', soul: hologram1Data.soul }; // Soul points to originalData
|
|
101
|
+
await holoSphere.put(testHolon, testLens, hologram1Storage);
|
|
102
|
+
await waitForGun(750); // <-- Increased delay
|
|
103
|
+
|
|
104
|
+
// 3. Hologram 2 pointing to Hologram 1, stored under 'hologram2-id'
|
|
105
|
+
// Create a temporary object representing hologram1 for creating hologram2
|
|
106
|
+
const hologram1Ref = { id: 'hologram1-id' }; // We only need the ID hologram2 should point to
|
|
107
|
+
const hologram2Data = holoSphere.createHologram(testHolon, testLens, hologram1Ref); // Soul points to hologram1Storage
|
|
108
|
+
const hologram2Storage = { id: 'hologram2-id', soul: hologram2Data.soul }; // Soul points to .../hologram1-id
|
|
109
|
+
await holoSphere.put(testHolon, testLens, hologram2Storage);
|
|
110
|
+
await waitForGun(750); // <-- Increased delay
|
|
111
|
+
|
|
112
|
+
// Resolve Hologram 2 (which is stored at hologram2-id)
|
|
113
|
+
// The system should fetch hologram2Storage, see its soul points to hologram1-id,
|
|
114
|
+
// fetch hologram1Storage, see its soul points to actual-nested-original,
|
|
115
|
+
// fetch originalData, and return it.
|
|
116
|
+
const resolved = await holoSphere.get(testHolon, testLens, 'hologram2-id');
|
|
117
|
+
|
|
118
|
+
console.log("!!!!!!!!!!!!! nested hologram", resolved);
|
|
119
|
+
|
|
120
|
+
expect(resolved).toBeNull(); // Expect null if new logic dictates this for nested resolution
|
|
121
|
+
|
|
122
|
+
// Resolve Hologram 2 without following deeply - should return Hologram 1 data
|
|
123
|
+
// Fetch hologram2Storage with resolveHolograms: false. This gets the raw hologram object.
|
|
124
|
+
const fetchedHologram2 = await holoSphere.get(testHolon, testLens, 'hologram2-id', null, { resolveHolograms: false });
|
|
125
|
+
// Now, resolve *this specific hologram object* one level deep.
|
|
126
|
+
const resolvedShallow = await holoSphere.resolveHologram(fetchedHologram2, { followHolograms: false });
|
|
127
|
+
|
|
128
|
+
console.log("!!!!!!!!!!!!! nested hologram shallow", resolvedShallow);
|
|
129
|
+
|
|
130
|
+
expect(resolvedShallow).toBeDefined();
|
|
131
|
+
// Should match hologram2Storage because of put redirection
|
|
132
|
+
expect(resolvedShallow.id).toBe('hologram2-id'); // ID from hologram2Storage
|
|
133
|
+
expect(resolvedShallow.soul).toBe(hologram2Storage.soul); // Soul from hologram2Storage
|
|
134
|
+
expect(resolvedShallow.value).toBeUndefined(); // Should not have value from originalData
|
|
135
|
+
expect(resolvedShallow.isHologram).toBeUndefined(); // Ensure top-level isHologram is not present
|
|
136
|
+
expect(resolvedShallow._meta).toBeDefined();
|
|
137
|
+
expect(resolvedShallow._meta.resolvedFromHologram).toBe(true);
|
|
138
|
+
expect(resolvedShallow._meta.hologramSoul).toBe(fetchedHologram2.soul);
|
|
139
|
+
expect(resolvedShallow._meta.isHologram).toBeUndefined();
|
|
140
|
+
expect(resolvedShallow._meta.resolved).toBeUndefined();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Test for circular holograms
|
|
144
|
+
test('should detect and handle circular holograms', async () => { // Rename test description
|
|
145
|
+
// Hologram A points to Hologram B's future location
|
|
146
|
+
const holoA = { id: 'holoA', soul: `${appName}/${testHolon}/${testLens}/holoB` };
|
|
147
|
+
await holoSphere.put(testHolon, testLens, holoA);
|
|
148
|
+
await waitForGun(); // <-- Add delay
|
|
149
|
+
|
|
150
|
+
// Hologram B points to Hologram A's location
|
|
151
|
+
const holoB = { id: 'holoB', soul: `${appName}/${testHolon}/${testLens}/holoA` };
|
|
152
|
+
await holoSphere.put(testHolon, testLens, holoB);
|
|
153
|
+
await waitForGun(); // <-- Add delay
|
|
154
|
+
|
|
155
|
+
// Attempt to resolve Hologram A via get, which should handle the circular error from resolveHologram
|
|
156
|
+
const resolved = await holoSphere.get(testHolon, testLens, holoA.id);
|
|
157
|
+
|
|
158
|
+
// Should return null due to circular detection handled in get()
|
|
159
|
+
expect(resolved).toBeNull();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('get should resolve holograms by default', async () => { // Rename test description
|
|
163
|
+
// 1. Store the actual data under its own ID
|
|
164
|
+
const actualData = { id: 'actual-data-id', value: 'Fetched via get' };
|
|
165
|
+
await holoSphere.put(testHolon, testLens, actualData);
|
|
166
|
+
await waitForGun(750); // <-- Increased delay
|
|
167
|
+
|
|
168
|
+
// 2. Create a hologram pointing to the actual data
|
|
169
|
+
// The hologram object itself will have the ID 'actual-data-id'
|
|
170
|
+
const hologramPointingToData = holoSphere.createHologram(testHolon, testLens, actualData);
|
|
171
|
+
|
|
172
|
+
// 3. Store the hologram object under the ID we want to fetch ('get-ref')
|
|
173
|
+
// We need to create a new object for storage that uses the hologram's soul
|
|
174
|
+
// but has the ID 'get-ref'.
|
|
175
|
+
const hologramStorageObject = { id: 'get-ref', soul: hologramPointingToData.soul };
|
|
176
|
+
await holoSphere.put(testHolon, testLens, hologramStorageObject);
|
|
177
|
+
await waitForGun(750); // <-- Increased delay
|
|
178
|
+
|
|
179
|
+
// 4. Get the item by the hologram's storage ID ('get-ref')
|
|
180
|
+
// This should retrieve hologramStorageObject and then resolve its soul
|
|
181
|
+
const resolved = await holoSphere.get(testHolon, testLens, 'get-ref');
|
|
182
|
+
|
|
183
|
+
console.log("!!!!!!!!!!!!!",resolved); // Should now show resolved data + meta
|
|
184
|
+
// Assertions should check against the *actualData*
|
|
185
|
+
// expect(resolved.id).toBe('actual-data-id'); // ID comes from the resolved data
|
|
186
|
+
// expect(resolved.value).toBe('Fetched via get'); // Value comes from the resolved data
|
|
187
|
+
// expect(resolved._meta).toBeDefined();
|
|
188
|
+
// expect(resolved._meta.isHologram).toBe(true);
|
|
189
|
+
// expect(resolved._meta.resolved).toBe(true);
|
|
190
|
+
// // The soul in the meta should be the soul of the *hologram object* we fetched
|
|
191
|
+
// expect(resolved._meta.soul).toBe(hologramStorageObject.soul);
|
|
192
|
+
expect(resolved).toBeNull(); // Expect null due to circular reference caused by put redirection
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('get should not resolve holograms if resolveHolograms is false', async () => { // Rename test description
|
|
196
|
+
const data = { id: 'get-no-res', value: 'Original' };
|
|
197
|
+
await holoSphere.put(testHolon, testLens, data);
|
|
198
|
+
await waitForGun(); // <-- Add delay
|
|
199
|
+
const hologram = holoSphere.createHologram(testHolon, testLens, data); // Use renamed method
|
|
200
|
+
await holoSphere.put(testHolon, testLens, hologram); // Store the hologram itself at its ID
|
|
201
|
+
await waitForGun(); // <-- Add delay
|
|
202
|
+
|
|
203
|
+
// Get the item by ID without resolving
|
|
204
|
+
const unresolved = await holoSphere.get(testHolon, testLens, 'get-no-res', null, { resolveHolograms: false }); // Use renamed option
|
|
205
|
+
|
|
206
|
+
expect(unresolved).toBeDefined();
|
|
207
|
+
expect(unresolved.id).toBe('get-no-res');
|
|
208
|
+
expect(unresolved.soul).toBe(hologram.soul);
|
|
209
|
+
expect(unresolved.value).toBeUndefined(); // Should be the raw hologram
|
|
210
|
+
expect(unresolved._meta).toBeUndefined();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('get should return null if hologram resolution fails (target deleted)', async () => { // Rename test description
|
|
214
|
+
const data = { id: 'get-deleted-target' };
|
|
215
|
+
// Create hologram but DO NOT store the original data
|
|
216
|
+
const hologram = holoSphere.createHologram(testHolon, testLens, data);
|
|
217
|
+
await holoSphere.put(testHolon, testLens, hologram); // Store the hologram
|
|
218
|
+
await waitForGun(); // <-- Add delay
|
|
219
|
+
|
|
220
|
+
// Get the item - resolution should fail
|
|
221
|
+
const result = await holoSphere.get(testHolon, testLens, 'get-deleted-target');
|
|
222
|
+
expect(result).toBeNull();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('updates to original data should be reflected when resolving hologram', async () => { // Rename test description
|
|
226
|
+
const originalData = { id: 'update-ref', value: 'Version 1' };
|
|
227
|
+
await holoSphere.put(testHolon, testLens, originalData);
|
|
228
|
+
await waitForGun(750); // <-- Increased delay
|
|
229
|
+
const hologram = holoSphere.createHologram(testHolon, testLens, originalData); // Use renamed method
|
|
230
|
+
await holoSphere.put(testHolon, 'otherLens', hologram); // Store hologram elsewhere
|
|
231
|
+
await waitForGun(750); // <-- Increased delay
|
|
232
|
+
|
|
233
|
+
// Resolve initially
|
|
234
|
+
const resolved1 = await holoSphere.resolveHologram(hologram); // Use renamed method
|
|
235
|
+
expect(resolved1).toBeNull(); // Expect null, likely due to put redirection complexities
|
|
236
|
+
|
|
237
|
+
// Update original data
|
|
238
|
+
const updatedData = { ...originalData, value: 'Version 2' };
|
|
239
|
+
await holoSphere.put(testHolon, testLens, updatedData);
|
|
240
|
+
await waitForGun(750);
|
|
241
|
+
|
|
242
|
+
// Resolve again
|
|
243
|
+
const resolved2 = await holoSphere.resolveHologram(hologram); // Use renamed method
|
|
244
|
+
expect(resolved2).toBeDefined();
|
|
245
|
+
expect(resolved2.id).toBe('update-ref');
|
|
246
|
+
expect(resolved2.value).toBe('Version 2');
|
|
247
|
+
expect(resolved2.isHologram).toBeUndefined(); // Ensure top-level isHologram is not present
|
|
248
|
+
expect(resolved2._meta).toBeDefined();
|
|
249
|
+
expect(resolved2._meta.resolvedFromHologram).toBe(true);
|
|
250
|
+
expect(resolved2._meta.hologramSoul).toBe(hologram.soul);
|
|
251
|
+
expect(resolved2._meta.isHologram).toBeUndefined();
|
|
252
|
+
expect(resolved2._meta.resolved).toBeUndefined();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// --- Tests for _holograms tracking ---
|
|
256
|
+
test('should add hologram soul to target _holograms set on put', async () => {
|
|
257
|
+
// 1. Store original data
|
|
258
|
+
const targetData = { id: 'target-for-tracking', value: 'Track me' };
|
|
259
|
+
await holoSphere.put(testHolon, testLens, targetData);
|
|
260
|
+
await waitForGun();
|
|
261
|
+
const targetSoul = `${appName}/${testHolon}/${testLens}/target-for-tracking`;
|
|
262
|
+
|
|
263
|
+
// 2. Store a hologram pointing to the original data
|
|
264
|
+
const hologramData = holoSphere.createHologram(testHolon, testLens, targetData);
|
|
265
|
+
const hologramStorage = { id: 'hologram-tracker-1', soul: hologramData.soul };
|
|
266
|
+
await holoSphere.put(testHolon, 'otherLens', hologramStorage); // Store in different lens
|
|
267
|
+
await waitForGun(500); // Longer wait for tracking update
|
|
268
|
+
const storedHologramSoul = `${appName}/${testHolon}/otherLens/hologram-tracker-1`;
|
|
269
|
+
|
|
270
|
+
// 3. Get the target node reference and fetch the _holograms set
|
|
271
|
+
const targetNodeRef = holoSphere.getNodeRef(targetSoul);
|
|
272
|
+
const hologramsSet = await new Promise(resolve => targetNodeRef.get('_holograms').once(resolve));
|
|
273
|
+
|
|
274
|
+
// 4. Verify the _holograms set exists and contains the stored hologram's soul
|
|
275
|
+
expect(hologramsSet).toBeDefined();
|
|
276
|
+
// Gun stores set members as keys, excluding metadata '_'
|
|
277
|
+
const hologramKeys = Object.keys(hologramsSet).filter(k => k !== '_');
|
|
278
|
+
expect(hologramKeys).toContain(storedHologramSoul);
|
|
279
|
+
expect(hologramsSet[storedHologramSoul]).toBe(true); // Check the value stored
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('should remove hologram soul from target _holograms set on delete', async () => {
|
|
283
|
+
// 1. Store original data
|
|
284
|
+
const targetData = { id: 'target-for-delete-tracking', value: 'Untrack me' };
|
|
285
|
+
await holoSphere.put(testHolon, testLens, targetData);
|
|
286
|
+
await waitForGun();
|
|
287
|
+
const targetSoul = `${appName}/${testHolon}/${testLens}/target-for-delete-tracking`;
|
|
288
|
+
|
|
289
|
+
// 2. Store a hologram pointing to the original data
|
|
290
|
+
const hologramData = holoSphere.createHologram(testHolon, testLens, targetData);
|
|
291
|
+
const hologramStorage = { id: 'hologram-tracker-2', soul: hologramData.soul };
|
|
292
|
+
await holoSphere.put(testHolon, 'otherLens', hologramStorage); // Store in different lens
|
|
293
|
+
await waitForGun(500); // Wait for put and tracking update
|
|
294
|
+
const storedHologramSoul = `${appName}/${testHolon}/otherLens/hologram-tracker-2`;
|
|
295
|
+
|
|
296
|
+
// 3. Verify hologram was added to tracking initially
|
|
297
|
+
const targetNodeRef = holoSphere.getNodeRef(targetSoul);
|
|
298
|
+
let hologramsSet = await new Promise(resolve => targetNodeRef.get('_holograms').once(resolve));
|
|
299
|
+
expect(hologramsSet).toBeDefined();
|
|
300
|
+
expect(hologramsSet[storedHologramSoul]).toBe(true);
|
|
301
|
+
|
|
302
|
+
// 4. Delete the hologram
|
|
303
|
+
await holoSphere.delete(testHolon, 'otherLens', 'hologram-tracker-2');
|
|
304
|
+
console.log("--- Waiting longer after delete for tracking update ---");
|
|
305
|
+
await waitForGun(1500); // <-- Increased wait time significantly
|
|
306
|
+
|
|
307
|
+
// 5. Fetch the _holograms set again directly
|
|
308
|
+
hologramsSet = await new Promise(resolve => targetNodeRef.get('_holograms').once(resolve));
|
|
309
|
+
|
|
310
|
+
// 6. Verify the hologram's soul is marked as 'DELETED'
|
|
311
|
+
expect(hologramsSet).toBeDefined();
|
|
312
|
+
expect(hologramsSet[storedHologramSoul]).toBe('DELETED'); // <-- Expect 'DELETED' string
|
|
313
|
+
});
|
|
314
|
+
// --- End tests for _holograms tracking ---
|
|
315
|
+
|
|
316
|
+
});
|