holosphere 1.1.6 → 1.1.8
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 +108 -160
- package/README.md +140 -0
- package/examples/federation.js +154 -0
- package/examples/references.js +177 -0
- package/federation.js +637 -372
- package/holosphere.d.ts +445 -20
- package/holosphere.js +343 -126
- package/package.json +1 -4
- package/test/delete.test.js +225 -0
- package/test/federation.test.js +50 -2
- package/config/default.js +0 -1
- package/services/environmentalApi.js +0 -253
- package/services/environmentalApitest.js +0 -164
- package/test/ai.test.js +0 -425
- package/test/sea.html +0 -33
- /package/test/{holonauth.test.js → auth.test.js} +0 -0
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
|
|
3
|
-
// Mock axios before importing the modules that use it
|
|
4
|
-
jest.mock('axios', () => ({
|
|
5
|
-
default: {
|
|
6
|
-
get: jest.fn()
|
|
7
|
-
}
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
// Import axios after mocking
|
|
11
|
-
import axios from 'axios';
|
|
12
|
-
|
|
13
|
-
// Import the API functions
|
|
14
|
-
import {
|
|
15
|
-
getCarbonSequestration,
|
|
16
|
-
getSoilCarbon,
|
|
17
|
-
getBiodiversityData,
|
|
18
|
-
getVegetationCover,
|
|
19
|
-
getAirQuality,
|
|
20
|
-
getWaterRetention,
|
|
21
|
-
getDeforestationData,
|
|
22
|
-
getFloodRisk,
|
|
23
|
-
getFoodSecurity,
|
|
24
|
-
getEmploymentRate,
|
|
25
|
-
getTransparencyScore,
|
|
26
|
-
getBlockchainTransactions,
|
|
27
|
-
getCircularEconomyData,
|
|
28
|
-
getRenewableEnergyData,
|
|
29
|
-
getClimateChangeData
|
|
30
|
-
} from './environmentalApi.js';
|
|
31
|
-
|
|
32
|
-
describe('Environmental API Tests', () => {
|
|
33
|
-
// Increase timeout for real API calls
|
|
34
|
-
jest.setTimeout(60000);
|
|
35
|
-
|
|
36
|
-
const ROME_LAT = 41.9;
|
|
37
|
-
const ROME_LON = 12.5;
|
|
38
|
-
const COUNTRY_CODE = 'ITA';
|
|
39
|
-
const ETH_ADDRESS = '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe';
|
|
40
|
-
|
|
41
|
-
test('getCarbonSequestration returns NASA POWER data', async () => {
|
|
42
|
-
const result = await getCarbonSequestration(ROME_LAT, ROME_LON);
|
|
43
|
-
expect(result).not.toBeNull();
|
|
44
|
-
expect(result.messages).toBeDefined();
|
|
45
|
-
expect(result.parameters).toBeDefined();
|
|
46
|
-
expect(result.parameters).toHaveProperty('T2M');
|
|
47
|
-
expect(result.parameters).toHaveProperty('PRECTOT');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('getSoilCarbon returns ISRIC soil data', async () => {
|
|
51
|
-
const result = await getSoilCarbon(ROME_LAT, ROME_LON);
|
|
52
|
-
expect(result).not.toBeNull();
|
|
53
|
-
expect(result.properties).toBeDefined();
|
|
54
|
-
expect(result.properties.soc).toBeDefined();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('getBiodiversityData returns GBIF data', async () => {
|
|
58
|
-
const result = await getBiodiversityData(COUNTRY_CODE);
|
|
59
|
-
expect(result).not.toBeNull();
|
|
60
|
-
expect(result.count).toBeDefined();
|
|
61
|
-
expect(result.results).toBeDefined();
|
|
62
|
-
expect(Array.isArray(result.results)).toBeTruthy();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('getVegetationCover returns MODIS NDVI data', async () => {
|
|
66
|
-
const result = await getVegetationCover(ROME_LAT, ROME_LON);
|
|
67
|
-
expect(result).not.toBeNull();
|
|
68
|
-
expect(result.subset || result.data).toBeDefined();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test('getAirQuality returns OpenWeather data', async () => {
|
|
72
|
-
const result = await getAirQuality(ROME_LAT, ROME_LON);
|
|
73
|
-
expect(result).not.toBeNull();
|
|
74
|
-
expect(result.list).toBeDefined();
|
|
75
|
-
expect(result.list[0].main).toBeDefined();
|
|
76
|
-
expect(result.list[0].components).toBeDefined();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('getWaterRetention returns USGS data', async () => {
|
|
80
|
-
const result = await getWaterRetention(ROME_LAT, ROME_LON);
|
|
81
|
-
expect(result).not.toBeNull();
|
|
82
|
-
expect(result.value || result.timeSeries).toBeDefined();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test('getDeforestationData returns World Bank forest data', async () => {
|
|
86
|
-
const result = await getDeforestationData(COUNTRY_CODE);
|
|
87
|
-
expect(result).not.toBeNull();
|
|
88
|
-
expect(Array.isArray(result)).toBeTruthy();
|
|
89
|
-
if (result.length > 1) {
|
|
90
|
-
expect(result[1][0]).toHaveProperty('indicator');
|
|
91
|
-
expect(result[1][0].indicator.id).toBe('AG.LND.FRST.ZS');
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('getFloodRisk returns USGS gauge data', async () => {
|
|
96
|
-
const result = await getFloodRisk(ROME_LAT, ROME_LON);
|
|
97
|
-
expect(result).not.toBeNull();
|
|
98
|
-
expect(result.value || result.timeSeries).toBeDefined();
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test('getFoodSecurity returns World Bank data', async () => {
|
|
102
|
-
const result = await getFoodSecurity(COUNTRY_CODE);
|
|
103
|
-
expect(result).not.toBeNull();
|
|
104
|
-
expect(Array.isArray(result)).toBeTruthy();
|
|
105
|
-
if (result.length > 1) {
|
|
106
|
-
expect(result[1][0]).toHaveProperty('indicator');
|
|
107
|
-
expect(result[1][0].indicator.id).toBe('SN.ITK.DEFC.ZS');
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('getEmploymentRate returns World Bank data', async () => {
|
|
112
|
-
const result = await getEmploymentRate(COUNTRY_CODE);
|
|
113
|
-
expect(result).not.toBeNull();
|
|
114
|
-
expect(Array.isArray(result)).toBeTruthy();
|
|
115
|
-
if (result.length > 1) {
|
|
116
|
-
expect(result[1][0]).toHaveProperty('indicator');
|
|
117
|
-
expect(result[1][0].indicator.id).toBe('SL.EMP.TOTL.SP.ZS');
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('getTransparencyScore returns World Bank governance data', async () => {
|
|
122
|
-
const result = await getTransparencyScore(COUNTRY_CODE);
|
|
123
|
-
expect(result).not.toBeNull();
|
|
124
|
-
expect(Array.isArray(result)).toBeTruthy();
|
|
125
|
-
if (result.length > 1) {
|
|
126
|
-
expect(result[1][0]).toHaveProperty('indicator');
|
|
127
|
-
expect(result[1][0].indicator.id).toBe('GE.EST');
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test('getBlockchainTransactions returns Etherscan data', async () => {
|
|
132
|
-
const result = await getBlockchainTransactions(ETH_ADDRESS);
|
|
133
|
-
expect(result).not.toBeNull();
|
|
134
|
-
expect(result.status).toBeDefined();
|
|
135
|
-
expect(result.result).toBeDefined();
|
|
136
|
-
expect(Array.isArray(result.result)).toBeTruthy();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test('getCircularEconomyData returns World Bank emissions data', async () => {
|
|
140
|
-
const result = await getCircularEconomyData(COUNTRY_CODE);
|
|
141
|
-
expect(result).not.toBeNull();
|
|
142
|
-
expect(Array.isArray(result)).toBeTruthy();
|
|
143
|
-
if (result.length > 1) {
|
|
144
|
-
expect(result[1][0]).toHaveProperty('indicator');
|
|
145
|
-
expect(result[1][0].indicator.id).toBe('EN.ATM.GHGT.KT.CE');
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('getRenewableEnergyData returns World Bank energy data', async () => {
|
|
150
|
-
const result = await getRenewableEnergyData(COUNTRY_CODE);
|
|
151
|
-
expect(result).not.toBeNull();
|
|
152
|
-
expect(Array.isArray(result)).toBeTruthy();
|
|
153
|
-
if (result.length > 1) {
|
|
154
|
-
expect(result[1][0]).toHaveProperty('indicator');
|
|
155
|
-
expect(result[1][0].indicator.id).toBe('EG.FEC.RNEW.ZS');
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test('getClimateChangeData returns NOAA data', async () => {
|
|
160
|
-
const result = await getClimateChangeData(ROME_LAT, ROME_LON);
|
|
161
|
-
expect(result).not.toBeNull();
|
|
162
|
-
expect(result.metadata || result.results).toBeDefined();
|
|
163
|
-
});
|
|
164
|
-
});
|
package/test/ai.test.js
DELETED
|
@@ -1,425 +0,0 @@
|
|
|
1
|
-
import HoloSphere from '../holosphere.js';
|
|
2
|
-
import * as h3 from 'h3-js';
|
|
3
|
-
import { jest } from '@jest/globals';
|
|
4
|
-
import 'dotenv/config';
|
|
5
|
-
|
|
6
|
-
// Set global timeout for all tests
|
|
7
|
-
jest.setTimeout(120000);
|
|
8
|
-
|
|
9
|
-
describe('AI Operations', () => {
|
|
10
|
-
let holoSphere;
|
|
11
|
-
const testAppName = 'test-ai-app';
|
|
12
|
-
const testHolon = h3.latLngToCell(40.7128, -74.0060, 7);
|
|
13
|
-
const testLens = 'aiTestLens';
|
|
14
|
-
const testPassword = 'AiTest123!';
|
|
15
|
-
const federatedHolon = h3.latLngToCell(34.0522, -118.2437, 7); // LA coordinates
|
|
16
|
-
const federatedPassword = 'FedAiTest456!';
|
|
17
|
-
|
|
18
|
-
beforeAll(async () => {
|
|
19
|
-
holoSphere = new HoloSphere(testAppName, false, process.env.OPENAI_API_KEY);
|
|
20
|
-
|
|
21
|
-
// Set up base schema for compute tests
|
|
22
|
-
const baseSchema = {
|
|
23
|
-
type: 'object',
|
|
24
|
-
properties: {
|
|
25
|
-
id: { type: 'string' },
|
|
26
|
-
content: { type: 'string' },
|
|
27
|
-
value: { type: 'number' },
|
|
28
|
-
tags: { type: 'array', items: { type: 'string' } },
|
|
29
|
-
timestamp: { type: 'number' },
|
|
30
|
-
summary: { type: 'string' }
|
|
31
|
-
},
|
|
32
|
-
required: ['id', 'content']
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
await holoSphere.setSchema(testLens, baseSchema);
|
|
36
|
-
|
|
37
|
-
// Create federation between the test spaces
|
|
38
|
-
try {
|
|
39
|
-
await holoSphere.federate(testHolon, federatedHolon, testPassword, federatedPassword);
|
|
40
|
-
console.log('Federation created for AI testing');
|
|
41
|
-
} catch (error) {
|
|
42
|
-
console.warn('Federation setup failed, tests will run with limited functionality:', error.message);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('Summarize Operations', () => {
|
|
47
|
-
test('should generate summary from text content', async () => {
|
|
48
|
-
const testContent = `
|
|
49
|
-
The HoloSphere project is a decentralized data management system.
|
|
50
|
-
It uses Gun.js for peer-to-peer data synchronization and SEA for encryption.
|
|
51
|
-
The system supports federation between spaces and implements schema validation.
|
|
52
|
-
Data can be organized in holons and viewed through different lenses.
|
|
53
|
-
`;
|
|
54
|
-
|
|
55
|
-
const summary = await holoSphere.summarize(testContent);
|
|
56
|
-
expect(summary).toBeDefined();
|
|
57
|
-
expect(typeof summary).toBe('string');
|
|
58
|
-
expect(summary.length).toBeGreaterThan(0);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('should handle empty content gracefully', async () => {
|
|
62
|
-
const summary = await holoSphere.summarize('');
|
|
63
|
-
expect(summary).toBeDefined();
|
|
64
|
-
expect(typeof summary).toBe('string');
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('should handle long content', async () => {
|
|
68
|
-
const longContent = Array(10).fill(
|
|
69
|
-
'This is a long paragraph of text that needs to be summarized. ' +
|
|
70
|
-
'It contains multiple sentences with various information. ' +
|
|
71
|
-
'The summary should capture the key points while remaining concise.'
|
|
72
|
-
).join('\n');
|
|
73
|
-
|
|
74
|
-
const summary = await holoSphere.summarize(longContent);
|
|
75
|
-
expect(summary).toBeDefined();
|
|
76
|
-
expect(typeof summary).toBe('string');
|
|
77
|
-
expect(summary.length).toBeLessThan(longContent.length);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('should fail gracefully without API key', async () => {
|
|
81
|
-
const noKeyHoloSphere = new HoloSphere(testAppName, false);
|
|
82
|
-
const result = await noKeyHoloSphere.summarize('Test content');
|
|
83
|
-
expect(result).toBe('OpenAI not initialized, please specify the API key in the constructor.');
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe('Compute Operations', () => {
|
|
88
|
-
beforeEach(async () => {
|
|
89
|
-
await holoSphere.deleteAll(testHolon, testLens, testPassword);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test('should compute summaries for nested holons', async () => {
|
|
93
|
-
const childHolon = h3.cellToChildren(testHolon, 8)[0];
|
|
94
|
-
const testData = {
|
|
95
|
-
id: 'test1',
|
|
96
|
-
content: 'This is test content for the child holon that should be summarized.',
|
|
97
|
-
timestamp: Date.now()
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
await holoSphere.put(childHolon, testLens, testData, testPassword);
|
|
101
|
-
|
|
102
|
-
const result = await holoSphere.compute(childHolon, testLens, {
|
|
103
|
-
operation: 'summarize',
|
|
104
|
-
fields: ['content'],
|
|
105
|
-
targetField: 'summary'
|
|
106
|
-
}, testPassword);
|
|
107
|
-
|
|
108
|
-
expect(result).toBeDefined();
|
|
109
|
-
expect(result.id).toMatch(/_summarize$/);
|
|
110
|
-
expect(result.summary).toBeDefined();
|
|
111
|
-
expect(typeof result.summary).toBe('string');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test('should compute aggregations for numeric fields', async () => {
|
|
115
|
-
const childHolon = h3.cellToChildren(testHolon, 8)[0];
|
|
116
|
-
const testData = [
|
|
117
|
-
{ id: 'test1', content: 'test content 1', value: 10, timestamp: Date.now() },
|
|
118
|
-
{ id: 'test2', content: 'test content 2', value: 20, timestamp: Date.now() }
|
|
119
|
-
];
|
|
120
|
-
|
|
121
|
-
await Promise.all(testData.map(data =>
|
|
122
|
-
holoSphere.put(childHolon, testLens, data, testPassword)
|
|
123
|
-
));
|
|
124
|
-
|
|
125
|
-
const result = await holoSphere.compute(childHolon, testLens, {
|
|
126
|
-
operation: 'aggregate',
|
|
127
|
-
fields: ['value'],
|
|
128
|
-
targetField: 'aggregated'
|
|
129
|
-
}, testPassword);
|
|
130
|
-
|
|
131
|
-
expect(result).toBeDefined();
|
|
132
|
-
expect(result.id).toMatch(/_aggregate$/);
|
|
133
|
-
|
|
134
|
-
// Based on the compute implementation, the result structure depends on targetField usage
|
|
135
|
-
if (result.aggregated) {
|
|
136
|
-
// If targetField is used, the computed result is stored in that field
|
|
137
|
-
expect(result.aggregated.value).toBe(70);
|
|
138
|
-
} else if (result.value !== undefined && typeof result.value === 'object') {
|
|
139
|
-
// If no targetField and computed is an object, it's assigned directly
|
|
140
|
-
expect(result.value).toBe(70);
|
|
141
|
-
} else {
|
|
142
|
-
// Direct assignment
|
|
143
|
-
expect(result.value).toBe(70);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('should compute concatenations for array fields', async () => {
|
|
148
|
-
const childHolon = h3.cellToChildren(testHolon, 8)[0];
|
|
149
|
-
const testData = [
|
|
150
|
-
{ id: 'test1', content: 'test content 1', tags: ['tag1', 'tag2'], timestamp: Date.now() },
|
|
151
|
-
{ id: 'test2', content: 'test content 2', tags: ['tag2', 'tag3'], timestamp: Date.now() }
|
|
152
|
-
];
|
|
153
|
-
|
|
154
|
-
await Promise.all(testData.map(data =>
|
|
155
|
-
holoSphere.put(childHolon, testLens, data, testPassword)
|
|
156
|
-
));
|
|
157
|
-
|
|
158
|
-
const result = await holoSphere.compute(childHolon, testLens, {
|
|
159
|
-
operation: 'concatenate',
|
|
160
|
-
fields: ['tags'],
|
|
161
|
-
targetField: 'concatenated'
|
|
162
|
-
}, testPassword);
|
|
163
|
-
|
|
164
|
-
expect(result).toBeDefined();
|
|
165
|
-
expect(result.id).toMatch(/_concatenate$/);
|
|
166
|
-
|
|
167
|
-
// Check for the actual structure based on implementation
|
|
168
|
-
let tagArray;
|
|
169
|
-
if (result.concatenated) {
|
|
170
|
-
// If targetField is used
|
|
171
|
-
tagArray = result.concatenated.tags;
|
|
172
|
-
} else if (result.tags) {
|
|
173
|
-
// If computed assigned directly
|
|
174
|
-
tagArray = result.tags;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
expect(tagArray).toBeDefined();
|
|
178
|
-
expect(Array.isArray(tagArray)).toBe(true);
|
|
179
|
-
|
|
180
|
-
// Due to authentication issues in GunDB during tests, we can't guarantee
|
|
181
|
-
// which tags will be present in the final result. Instead of requiring all tags,
|
|
182
|
-
// just verify that we have a valid array with some expected tags.
|
|
183
|
-
expect(tagArray.length).toBeGreaterThan(0);
|
|
184
|
-
expect(tagArray.some(tag => ['tag1', 'tag2', 'tag3'].includes(tag))).toBe(true);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test('should handle empty holons', async () => {
|
|
188
|
-
await holoSphere.deleteAll(testHolon, testLens, testPassword);
|
|
189
|
-
|
|
190
|
-
const result = await holoSphere.compute(testHolon, testLens, {
|
|
191
|
-
operation: 'summarize',
|
|
192
|
-
fields: ['content'],
|
|
193
|
-
targetField: 'summary'
|
|
194
|
-
}, testPassword);
|
|
195
|
-
|
|
196
|
-
expect(result).toBeNull();
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test('should compute hierarchy across multiple levels', async () => {
|
|
200
|
-
const childHolon = h3.cellToChildren(testHolon, 9)[0];
|
|
201
|
-
const testData = {
|
|
202
|
-
id: 'test-hierarchy',
|
|
203
|
-
content: 'Content for testing hierarchy computation',
|
|
204
|
-
value: 42,
|
|
205
|
-
tags: ['test', 'hierarchy'],
|
|
206
|
-
timestamp: Date.now()
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
await holoSphere.put(childHolon, testLens, testData, testPassword);
|
|
210
|
-
|
|
211
|
-
const results = await holoSphere.computeHierarchy(childHolon, testLens, {
|
|
212
|
-
operation: 'summarize',
|
|
213
|
-
fields: ['content'],
|
|
214
|
-
targetField: 'summary'
|
|
215
|
-
}, 3, testPassword);
|
|
216
|
-
|
|
217
|
-
expect(Array.isArray(results)).toBe(true);
|
|
218
|
-
expect(results.length).toBeGreaterThan(0);
|
|
219
|
-
results.forEach(result => {
|
|
220
|
-
expect(result.id).toMatch(/_summarize$/);
|
|
221
|
-
expect(result.summary).toBeDefined();
|
|
222
|
-
expect(typeof result.summary).toBe('string');
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
describe('Federated AI Operations', () => {
|
|
228
|
-
beforeEach(async () => {
|
|
229
|
-
await holoSphere.deleteAll(testHolon, testLens, testPassword);
|
|
230
|
-
try {
|
|
231
|
-
await holoSphere.deleteAll(federatedHolon, testLens, federatedPassword);
|
|
232
|
-
} catch (error) {
|
|
233
|
-
console.warn('Could not clean up federated holon:', error.message);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
test('should propagate AI-summarized content to federated space', async () => {
|
|
238
|
-
// Step 1: Create content in the main space
|
|
239
|
-
const testData = {
|
|
240
|
-
id: 'federated-content',
|
|
241
|
-
content: 'This content will be summarized and then federated to another space.',
|
|
242
|
-
timestamp: Date.now(),
|
|
243
|
-
tags: ['ai', 'federation', 'test']
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
await holoSphere.put(testHolon, testLens, testData, testPassword);
|
|
247
|
-
|
|
248
|
-
// Step 2: Generate a summary
|
|
249
|
-
const summary = await holoSphere.summarize(testData.content);
|
|
250
|
-
|
|
251
|
-
// Step 3: Update the content with summary
|
|
252
|
-
const updatedData = {
|
|
253
|
-
...testData,
|
|
254
|
-
summary,
|
|
255
|
-
updated: Date.now()
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
await holoSphere.put(testHolon, testLens, updatedData, testPassword);
|
|
259
|
-
|
|
260
|
-
// Step 4: Propagate to federated space
|
|
261
|
-
try {
|
|
262
|
-
const result = await holoSphere.propagateToFederation(testHolon, testLens, updatedData);
|
|
263
|
-
|
|
264
|
-
// Even if propagation fails due to auth, the function should complete
|
|
265
|
-
expect(result).toBeDefined();
|
|
266
|
-
|
|
267
|
-
// Step 5: Verify if the data made it to the federated space (if accessible)
|
|
268
|
-
try {
|
|
269
|
-
const federatedData = await holoSphere.get(federatedHolon, testLens, 'federated-content', federatedPassword);
|
|
270
|
-
if (federatedData) {
|
|
271
|
-
expect(federatedData.summary).toBe(summary);
|
|
272
|
-
}
|
|
273
|
-
} catch (error) {
|
|
274
|
-
console.warn('Could not verify federated data, continuing with test:', error.message);
|
|
275
|
-
}
|
|
276
|
-
} catch (error) {
|
|
277
|
-
console.warn('Federation propagation failed, continuing with test:', error.message);
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
test('should aggregate AI content from federated spaces', async () => {
|
|
282
|
-
// Step 1: Create content in both spaces
|
|
283
|
-
const mainData = {
|
|
284
|
-
id: 'aggregate-content',
|
|
285
|
-
content: 'Content from the main space that will be aggregated with federated content.',
|
|
286
|
-
value: 10,
|
|
287
|
-
tags: ['main', 'aggregate'],
|
|
288
|
-
timestamp: Date.now()
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
const federatedData = {
|
|
292
|
-
id: 'aggregate-content',
|
|
293
|
-
content: 'Content from the federated space that will be aggregated with main content.',
|
|
294
|
-
value: 20,
|
|
295
|
-
tags: ['federated', 'aggregate'],
|
|
296
|
-
timestamp: Date.now() + 100 // Slightly newer
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
// Put data in both spaces
|
|
300
|
-
await holoSphere.put(testHolon, testLens, mainData, testPassword);
|
|
301
|
-
|
|
302
|
-
try {
|
|
303
|
-
await holoSphere.put(federatedHolon, testLens, federatedData, federatedPassword);
|
|
304
|
-
} catch (error) {
|
|
305
|
-
console.warn('Could not put data in federated space, test will run with limited scope:', error.message);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Step 2: Generate summaries for both pieces of content
|
|
309
|
-
try {
|
|
310
|
-
const mainSummary = await holoSphere.summarize(mainData.content);
|
|
311
|
-
await holoSphere.put(testHolon, testLens, {
|
|
312
|
-
...mainData,
|
|
313
|
-
summary: mainSummary
|
|
314
|
-
}, testPassword);
|
|
315
|
-
|
|
316
|
-
try {
|
|
317
|
-
const federatedSummary = await holoSphere.summarize(federatedData.content);
|
|
318
|
-
await holoSphere.put(federatedHolon, testLens, {
|
|
319
|
-
...federatedData,
|
|
320
|
-
summary: federatedSummary
|
|
321
|
-
}, federatedPassword);
|
|
322
|
-
} catch (error) {
|
|
323
|
-
console.warn('Could not update federated space with summary:', error.message);
|
|
324
|
-
}
|
|
325
|
-
} catch (error) {
|
|
326
|
-
console.warn('Could not generate or save summaries:', error.message);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Step 3: Get aggregated content from both spaces
|
|
330
|
-
try {
|
|
331
|
-
const aggregated = await holoSphere.getFederated(testHolon, testLens, {
|
|
332
|
-
aggregate: true,
|
|
333
|
-
sumFields: ['value'],
|
|
334
|
-
concatArrays: ['tags'],
|
|
335
|
-
timeout: 1000 // Short timeout to prevent long test runs
|
|
336
|
-
}, testPassword);
|
|
337
|
-
|
|
338
|
-
// Even if no federation data comes through, we should get something back
|
|
339
|
-
expect(aggregated).toBeDefined();
|
|
340
|
-
expect(Array.isArray(aggregated)).toBe(true);
|
|
341
|
-
|
|
342
|
-
// If we successfully got merged data
|
|
343
|
-
const mergedItem = aggregated.find(item => item.id === 'aggregate-content');
|
|
344
|
-
if (mergedItem) {
|
|
345
|
-
// If we have both data points, the values should be summed
|
|
346
|
-
if (mergedItem.value > 10) {
|
|
347
|
-
expect(mergedItem.value).toBe(30); // 10 + 20
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// And the tags should be combined
|
|
351
|
-
if (mergedItem.tags && mergedItem.tags.length > 2) {
|
|
352
|
-
expect(mergedItem.tags).toContain('main');
|
|
353
|
-
expect(mergedItem.tags).toContain('federated');
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
} catch (error) {
|
|
357
|
-
console.warn('Federation getData failed:', error.message);
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
test('should handle authentication failures gracefully in federation operations', async () => {
|
|
362
|
-
// Test with wrong password to simulate auth failure
|
|
363
|
-
const wrongPassword = 'WrongPassword123!';
|
|
364
|
-
|
|
365
|
-
// Step 1: Attempt federation with wrong password - should not throw
|
|
366
|
-
try {
|
|
367
|
-
const result = await holoSphere.federate(testHolon, federatedHolon, testPassword, wrongPassword);
|
|
368
|
-
// Should complete even with wrong password, but might have limited functionality
|
|
369
|
-
expect(result).toBe(true);
|
|
370
|
-
} catch (error) {
|
|
371
|
-
// If this does throw, it should be a different error than auth failure
|
|
372
|
-
expect(error.message).not.toContain('Authentication failed');
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Step 2: Attempt to propagate data with federation issues
|
|
376
|
-
const testData = {
|
|
377
|
-
id: 'auth-test',
|
|
378
|
-
content: 'Testing propagation with authentication issues',
|
|
379
|
-
timestamp: Date.now()
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
await holoSphere.put(testHolon, testLens, testData, testPassword);
|
|
383
|
-
|
|
384
|
-
try {
|
|
385
|
-
// This should complete even if auth fails
|
|
386
|
-
const result = await holoSphere.propagateToFederation(testHolon, testLens, testData);
|
|
387
|
-
expect(result).toBeDefined();
|
|
388
|
-
// But might not have successfully propagated
|
|
389
|
-
} catch (error) {
|
|
390
|
-
// If this throws, it should be unrelated to auth
|
|
391
|
-
expect(error.message).not.toContain('Authentication failed');
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
afterEach(async () => {
|
|
397
|
-
await holoSphere.deleteAll(testHolon, testLens, testPassword);
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
afterAll(async () => {
|
|
401
|
-
await holoSphere.deleteAll(testHolon, testLens, testPassword);
|
|
402
|
-
try {
|
|
403
|
-
await holoSphere.deleteAll(federatedHolon, testLens, federatedPassword);
|
|
404
|
-
} catch (error) {
|
|
405
|
-
console.warn('Could not clean up federated holon:', error.message);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
try {
|
|
409
|
-
await holoSphere.unfederate(testHolon, federatedHolon, testPassword, federatedPassword);
|
|
410
|
-
console.log('Federation cleaned up');
|
|
411
|
-
} catch (error) {
|
|
412
|
-
console.warn('Federation cleanup failed:', error.message);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
try {
|
|
416
|
-
await holoSphere.deleteAllGlobal('federation', testPassword);
|
|
417
|
-
} catch (error) {
|
|
418
|
-
console.warn('Could not delete federation data:', error.message);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (holoSphere.gun) {
|
|
422
|
-
holoSphere.gun.off();
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
});
|
package/test/sea.html
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
|
|
2
|
-
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
|
|
3
|
-
<script>
|
|
4
|
-
// var Gun = require('gun'); // in NodeJS
|
|
5
|
-
// require('gun/sea');
|
|
6
|
-
var SEA = Gun.SEA;
|
|
7
|
-
;(async () => {
|
|
8
|
-
var pair = await SEA.pair();
|
|
9
|
-
var enc = await SEA.encrypt('hello self', pair);
|
|
10
|
-
var data = await SEA.sign(enc, pair);
|
|
11
|
-
console.log(data);
|
|
12
|
-
var msg = await SEA.verify(data, pair.pub);
|
|
13
|
-
var dec = await SEA.decrypt(msg, pair);
|
|
14
|
-
var proof = await SEA.work(dec, pair);
|
|
15
|
-
var check = await SEA.work('hello self', pair);
|
|
16
|
-
console.log(dec);
|
|
17
|
-
console.log(proof === check);
|
|
18
|
-
// now let's share private data with someone:
|
|
19
|
-
var alice = await SEA.pair();
|
|
20
|
-
var bob = await SEA.pair();
|
|
21
|
-
var enc = await SEA.encrypt('shared data', await SEA.secret(bob.epub, alice));
|
|
22
|
-
await SEA.decrypt(enc, await SEA.secret(alice.epub, bob));
|
|
23
|
-
// `.secret` is Elliptic-curve Diffie–Hellman
|
|
24
|
-
// Bob allows Alice to write to part of his graph, he creates a certificate for Alice
|
|
25
|
-
var certificate = await SEA.certify(alice.pub, ["^AliceOnly.*"], bob)
|
|
26
|
-
// Alice logs in
|
|
27
|
-
const gun = Gun();
|
|
28
|
-
await gun.user().auth(alice);
|
|
29
|
-
// and uses the certificate
|
|
30
|
-
await gun.get('~'+bob.pub).get('AliceOnly').get('do-not-tell-anyone').put(enc, null, {opt: {cert: certificate}})
|
|
31
|
-
await gun.get('~'+bob.pub).get('AliceOnly').get('do-not-tell-anyone').once(console.log) // return 'enc'
|
|
32
|
-
})();
|
|
33
|
-
</script>
|
|
File without changes
|