holosphere 1.1.3 → 1.1.5

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/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "holosphere",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Holonic Geospatial Communication Infrastructure",
5
5
  "main": "holosphere.js",
6
6
  "types": "holosphere.d.ts",
7
7
  "type": "module",
8
8
  "scripts": {
9
- "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
9
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js ./test",
10
10
  "build": "",
11
11
  "prepare": "npm run build"
12
12
  },
@@ -14,11 +14,14 @@
14
14
  "license": "GPL-3.0-or-later",
15
15
  "dependencies": {
16
16
  "ajv": "^8.12.0",
17
+ "axios": "^1.6.7",
18
+ "dotenv": "^16.4.7",
17
19
  "gun": "^0.2020.1240",
18
20
  "h3-js": "^4.1.0",
19
21
  "openai": "^4.85.1"
20
22
  },
21
23
  "devDependencies": {
24
+ "@babel/preset-env": "^7.24.0",
22
25
  "jest": "^29.7.0",
23
26
  "jest-environment-node": "^29.7.0"
24
27
  },
@@ -27,6 +30,9 @@
27
30
  "transform": {},
28
31
  "moduleNameMapper": {
29
32
  "^(\\.{1,2}/.*)\\.js$": "$1"
30
- }
33
+ },
34
+ "setupFilesAfterEnv": [
35
+ "<rootDir>/test/jest.setup.js"
36
+ ]
31
37
  }
32
38
  }
@@ -0,0 +1,253 @@
1
+ import axios from 'axios';
2
+ import dotenv from 'dotenv';
3
+
4
+ dotenv.config();
5
+
6
+ const logError = (service, error) => {
7
+ const status = error.response?.status;
8
+ const message = error.response?.data?.message || error.message;
9
+ console.error(`${service} API Error:`, { status, message });
10
+ };
11
+
12
+ /**
13
+ * Fetch Carbon Sequestration Data (Using NASA POWER API)
14
+ */
15
+ export async function getCarbonSequestration(lat = 41.9, lon = 12.5) {
16
+ try {
17
+ const response = await axios.default.get(
18
+ `https://power.larc.nasa.gov/api/temporal/daily/point?parameters=T2M,PRECTOT,PS,WS2M&community=RE&longitude=${lon}&latitude=${lat}&start=20230101&end=20231231&format=JSON`
19
+ );
20
+ return response.data;
21
+ } catch (error) {
22
+ logError('Carbon Sequestration', error);
23
+ return null;
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Fetch Soil Carbon Data (Using ISRIC World Soil Information)
29
+ */
30
+ export async function getSoilCarbon(lat = 41.9, lon = 12.5) {
31
+ try {
32
+ const response = await axios.default.get(
33
+ `https://rest.isric.org/soilgrids/v2.0/properties/query?lat=${lat}&lon=${lon}&property=soc&depth=0-30cm&value=mean`
34
+ );
35
+ return response.data;
36
+ } catch (error) {
37
+ logError('Soil Carbon', error);
38
+ return null;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Fetch Biodiversity Data (GBIF API)
44
+ */
45
+ export async function getBiodiversityData(countryCode = "ITA") {
46
+ try {
47
+ const response = await axios.default.get(`https://api.gbif.org/v1/occurrence/search?country=${countryCode}&limit=100`);
48
+ return response.data;
49
+ } catch (error) {
50
+ logError('Biodiversity', error);
51
+ return null;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Fetch Vegetation Cover Data (Using NASA MODIS Vegetation Index)
57
+ */
58
+ export async function getVegetationCover(lat = 41.9, lon = 12.5) {
59
+ try {
60
+ const response = await axios.default.get(
61
+ `https://modis.ornl.gov/rst/api/v1/MOD13Q1/subset?latitude=${lat}&longitude=${lon}&band=NDVI&startDate=2023-01-01&endDate=2023-12-31`,
62
+ {
63
+ headers: {
64
+ 'Accept': 'application/json'
65
+ }
66
+ }
67
+ );
68
+ return response.data;
69
+ } catch (error) {
70
+ logError('Vegetation Cover', error);
71
+ return null;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Fetch Air Quality Data (OpenWeather API)
77
+ */
78
+ export async function getAirQuality(lat = 41.9, lon = 12.5) {
79
+ try {
80
+ const response = await axios.default.get(
81
+ `https://api.openweathermap.org/data/2.5/air_pollution?lat=${lat}&lon=${lon}&appid=${process.env.OPENWEATHER_API_KEY}`
82
+ );
83
+ return response.data;
84
+ } catch (error) {
85
+ logError('Air Quality', error);
86
+ return null;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Fetch Water Retention Data (Using USGS Water Services)
92
+ */
93
+ export async function getWaterRetention(lat = 41.9, lon = 12.5) {
94
+ try {
95
+ const response = await axios.default.get(
96
+ `https://waterservices.usgs.gov/nwis/iv/?format=json&sites=11447650&siteStatus=active`,
97
+ {
98
+ headers: {
99
+ 'Accept': 'application/json'
100
+ }
101
+ }
102
+ );
103
+ return response.data;
104
+ } catch (error) {
105
+ logError('Water Retention', error);
106
+ return null;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Fetch Deforestation Data (Using World Bank Forest Data)
112
+ */
113
+ export async function getDeforestationData(countryCode = "ITA") {
114
+ try {
115
+ const response = await axios.default.get(
116
+ `https://api.worldbank.org/v2/country/${countryCode}/indicator/AG.LND.FRST.ZS?format=json`
117
+ );
118
+ return response.data;
119
+ } catch (error) {
120
+ logError('Deforestation', error);
121
+ return null;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Fetch Flood Risk Data (Using USGS Water Services)
127
+ */
128
+ export async function getFloodRisk(lat = 41.9, lon = 12.5) {
129
+ try {
130
+ const response = await axios.default.get(
131
+ `https://waterservices.usgs.gov/nwis/iv/?format=json&stateCd=CA&parameterCd=00065&siteStatus=active`,
132
+ {
133
+ headers: {
134
+ 'Accept': 'application/json'
135
+ }
136
+ }
137
+ );
138
+ return response.data;
139
+ } catch (error) {
140
+ logError('Flood Risk', error);
141
+ return null;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Fetch Food Security Data (World Bank API)
147
+ */
148
+ export async function getFoodSecurity(countryCode = "ITA") {
149
+ try {
150
+ const response = await axios.default.get(
151
+ `https://api.worldbank.org/v2/country/${countryCode}/indicator/SN.ITK.DEFC.ZS?format=json`
152
+ );
153
+ return response.data;
154
+ } catch (error) {
155
+ logError('Food Security', error);
156
+ return null;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Fetch Local Employment Rate (World Bank API)
162
+ */
163
+ export async function getEmploymentRate(countryCode = "ITA") {
164
+ try {
165
+ const response = await axios.default.get(
166
+ `https://api.worldbank.org/v2/country/${countryCode}/indicator/SL.EMP.TOTL.SP.ZS?format=json`
167
+ );
168
+ return response.data;
169
+ } catch (error) {
170
+ logError('Employment Rate', error);
171
+ return null;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Fetch Governance Score (World Bank API)
177
+ */
178
+ export async function getTransparencyScore(countryCode = "ITA") {
179
+ try {
180
+ const response = await axios.default.get(
181
+ `https://api.worldbank.org/v2/country/${countryCode}/indicator/GE.EST?format=json`
182
+ );
183
+ return response.data;
184
+ } catch (error) {
185
+ logError('Transparency Score', error);
186
+ return null;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Fetch Blockchain Transactions (Etherscan API)
192
+ */
193
+ export async function getBlockchainTransactions(address = "0x0000000000000000000000000000000000000000") {
194
+ try {
195
+ const response = await axios.default.get(
196
+ `https://api.etherscan.io/api?module=account&action=txlist&address=${address}&startblock=0&endblock=99999999&sort=desc&apikey=${process.env.ETHERSCAN_API_KEY}`
197
+ );
198
+ return response.data;
199
+ } catch (error) {
200
+ logError('Blockchain Transactions', error);
201
+ return null;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Fetch Circular Economy Data (Using World Bank Development Indicators)
207
+ */
208
+ export async function getCircularEconomyData(countryCode = "ITA") {
209
+ try {
210
+ const response = await axios.default.get(
211
+ `https://api.worldbank.org/v2/country/${countryCode}/indicator/EN.ATM.GHGT.KT.CE?format=json`
212
+ );
213
+ return response.data;
214
+ } catch (error) {
215
+ logError('Circular Economy', error);
216
+ return null;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Fetch Renewable Energy Data (World Bank API)
222
+ */
223
+ export async function getRenewableEnergyData(countryCode = "ITA") {
224
+ try {
225
+ const response = await axios.default.get(
226
+ `https://api.worldbank.org/v2/country/${countryCode}/indicator/EG.FEC.RNEW.ZS?format=json`
227
+ );
228
+ return response.data;
229
+ } catch (error) {
230
+ logError('Renewable Energy', error);
231
+ return null;
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Fetch Climate Change Data (NOAA Climate API)
237
+ */
238
+ export async function getClimateChangeData(lat = 41.9, lon = 12.5) {
239
+ try {
240
+ const response = await axios.default.get(
241
+ `https://www.ncdc.noaa.gov/cdo-web/api/v2/data?datasetid=GHCND&latitude=${lat}&longitude=${lon}&startdate=2023-01-01&enddate=2023-12-31&limit=1000`,
242
+ {
243
+ headers: {
244
+ 'token': process.env.NOAA_API_KEY
245
+ }
246
+ }
247
+ );
248
+ return response.data;
249
+ } catch (error) {
250
+ logError('Climate Change', error);
251
+ return null;
252
+ }
253
+ }
@@ -0,0 +1,164 @@
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
+ });
@@ -0,0 +1,233 @@
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 testCredentials = {
15
+ spacename: 'aitest@example.com',
16
+ password: 'AiTest123!'
17
+ };
18
+
19
+ beforeAll(async () => {
20
+ holoSphere = new HoloSphere(testAppName, false, process.env.OPENAI_API_KEY);
21
+
22
+ // Clean up any existing test space and data
23
+ try {
24
+ await holoSphere.deleteAllGlobal('federation');
25
+ await holoSphere.deleteGlobal('spaces', testCredentials.spacename);
26
+ } catch (error) {
27
+ console.log('Cleanup error (can be ignored):', error);
28
+ }
29
+ // Create and login to test space
30
+ await holoSphere.createSpace(testCredentials.spacename, testCredentials.password);
31
+ await holoSphere.login(testCredentials.spacename, testCredentials.password);
32
+ // Set up base schema for compute tests
33
+ const baseSchema = {
34
+ type: 'object',
35
+ properties: {
36
+ id: { type: 'string' },
37
+ content: { type: 'string' },
38
+ value: { type: 'number' },
39
+ tags: { type: 'array', items: { type: 'string' } },
40
+ timestamp: { type: 'number' }
41
+ },
42
+ required: ['id']
43
+ };
44
+
45
+ await holoSphere.setSchema(testLens, baseSchema);
46
+ }, 30000);
47
+
48
+ describe('Summarize Operations', () => {
49
+ test('should generate summary from text content', async () => {
50
+ const testContent = `
51
+ The HoloSphere project is a decentralized data management system.
52
+ It uses Gun.js for peer-to-peer data synchronization and SEA for encryption.
53
+ The system supports federation between spaces and implements schema validation.
54
+ Data can be organized in holons and viewed through different lenses.
55
+ `;
56
+
57
+ const summary = await holoSphere.summarize(testContent);
58
+ console.log("summary",summary);
59
+ expect(summary).toBeDefined();
60
+ expect(typeof summary).toBe('string');
61
+ expect(summary.length).toBeGreaterThan(0);
62
+ }, 15000);
63
+
64
+ test('should handle empty content gracefully', async () => {
65
+ const summary = await holoSphere.summarize('');
66
+ expect(summary).toBeDefined();
67
+ expect(typeof summary).toBe('string');
68
+ }, 10000);
69
+
70
+ test('should handle long content', async () => {
71
+ const longContent = Array(10).fill(
72
+ 'This is a long paragraph of text that needs to be summarized. ' +
73
+ 'It contains multiple sentences with various information. ' +
74
+ 'The summary should capture the key points while remaining concise.'
75
+ ).join('\n');
76
+
77
+ const summary = await holoSphere.summarize(longContent);
78
+ expect(summary).toBeDefined();
79
+ expect(typeof summary).toBe('string');
80
+ expect(summary.length).toBeLessThan(longContent.length);
81
+ }, 20000);
82
+
83
+ test('should fail gracefully without API key', async () => {
84
+ const noKeyHoloSphere = new HoloSphere(testAppName, false);
85
+ const result = await noKeyHoloSphere.summarize('Test content');
86
+ expect(result).toBe('OpenAI not initialized, please specify the API key in the constructor.');
87
+ });
88
+ });
89
+
90
+ describe('Compute Operations', () => {
91
+ beforeEach(async () => {
92
+ // Ensure we're logged in
93
+ if (!holoSphere.currentSpace) {
94
+ await holoSphere.login(testCredentials.spacename, testCredentials.password);
95
+ }
96
+
97
+ // Clean up any existing test data
98
+ await holoSphere.deleteAll(testHolon, testLens);
99
+ }, 15000);
100
+
101
+ test('should compute summaries for nested holons', async () => {
102
+ const childHolon = h3.cellToChildren(testHolon, 8)[0];
103
+ const testData = {
104
+ id: 'test1',
105
+ content: 'This is test content for the child holon that should be summarized.',
106
+ timestamp: Date.now()
107
+ };
108
+
109
+ // Put data in child holon
110
+ await holoSphere.put(childHolon, testLens, testData);
111
+
112
+ // Compute summaries
113
+ const result = await holoSphere.compute(childHolon, testLens, {
114
+ operation: 'summarize',
115
+ fields: ['content'],
116
+ targetField: 'summary'
117
+ });
118
+
119
+ expect(result).toBeDefined();
120
+ expect(result.id).toMatch(/_summarize$/);
121
+ expect(result.summary).toBeDefined();
122
+ expect(typeof result.summary).toBe('string');
123
+ }, 60000);
124
+
125
+ test('should compute aggregations for numeric fields', async () => {
126
+ const childHolon = h3.cellToChildren(testHolon, 8)[0];
127
+ const testData = [
128
+ { id: 'test1', value: 10, timestamp: Date.now() },
129
+ { id: 'test2', value: 20, timestamp: Date.now() }
130
+ ];
131
+
132
+ // Put test data
133
+ await Promise.all(testData.map(data => holoSphere.put(childHolon, testLens, data)));
134
+
135
+ // Compute aggregation
136
+ const result = await holoSphere.compute(childHolon, testLens, {
137
+ operation: 'aggregate',
138
+ fields: ['value']
139
+ });
140
+
141
+ expect(result).toBeDefined();
142
+ expect(result.id).toMatch(/_aggregate$/);
143
+ expect(result.value).toBe(30);
144
+ }, 30000);
145
+
146
+ test('should compute concatenations for array fields', async () => {
147
+ const childHolon = h3.cellToChildren(testHolon, 8)[0];
148
+ const testData = [
149
+ { id: 'test1', tags: ['tag1', 'tag2'], timestamp: Date.now() },
150
+ { id: 'test2', tags: ['tag2', 'tag3'], timestamp: Date.now() }
151
+ ];
152
+
153
+ // Put test data
154
+ await Promise.all(testData.map(data => holoSphere.put(childHolon, testLens, data)));
155
+
156
+ // Compute concatenation
157
+ const result = await holoSphere.compute(childHolon, testLens, {
158
+ operation: 'concatenate',
159
+ fields: ['tags']
160
+ });
161
+
162
+ expect(result).toBeDefined();
163
+ expect(result.id).toMatch(/_concatenate$/);
164
+ expect(result.tags).toEqual(['tag1', 'tag2', 'tag3']);
165
+ }, 30000);
166
+
167
+
168
+ test('should handle empty holons', async () => {
169
+ // Clean up any existing data first
170
+ await holoSphere.deleteAll(testHolon, testLens);
171
+
172
+ // Try to compute on empty holon
173
+ const result = await holoSphere.compute(testHolon, testLens, {
174
+ operation: 'summarize',
175
+ fields: ['content']
176
+ });
177
+
178
+ expect(result).toBeNull();
179
+ }, 30000);
180
+
181
+ test('should compute hierarchy across multiple levels', async () => {
182
+ const childHolon = h3.cellToChildren(testHolon, 9)[0];
183
+ const testData = {
184
+ id: 'test-hierarchy',
185
+ content: 'Content for testing hierarchy computation',
186
+ value: 42,
187
+ tags: ['test', 'hierarchy'],
188
+ timestamp: Date.now()
189
+ };
190
+
191
+ // Put test data
192
+ await holoSphere.put(childHolon, testLens, testData);
193
+
194
+ // Compute hierarchy
195
+ const results = await holoSphere.computeHierarchy(childHolon, testLens, {
196
+ operation: 'summarize',
197
+ fields: ['content'],
198
+ targetField: 'summary'
199
+ }, 3);
200
+
201
+ expect(Array.isArray(results)).toBe(true);
202
+ expect(results.length).toBeGreaterThan(0);
203
+ results.forEach(result => {
204
+ expect(result.id).toMatch(/_summarize$/);
205
+ expect(result.summary).toBeDefined();
206
+ expect(typeof result.summary).toBe('string');
207
+ });
208
+ }, 60000);
209
+ });
210
+
211
+ afterEach(async () => {
212
+ // Clean up test data
213
+ if (holoSphere.currentSpace) {
214
+ await holoSphere.deleteAll(testHolon, testLens);
215
+ }
216
+ }, 15000);
217
+
218
+ afterAll(async () => {
219
+ // Clean up test space and data
220
+ try {
221
+ await holoSphere.deleteAll(testHolon, testLens);
222
+ await holoSphere.deleteGlobal('spaces', testCredentials.spacename);
223
+ await holoSphere.deleteAllGlobal('federation');
224
+ } catch (error) {
225
+ console.log('Cleanup error (can be ignored):', error);
226
+ }
227
+
228
+ // Clean up Gun instance
229
+ if (holoSphere.gun) {
230
+ holoSphere.gun.off();
231
+ }
232
+ }, 30000);
233
+ });