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/node.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// holo_node.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stores a specific gun node in a given holon and lens.
|
|
5
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
6
|
+
* @param {string} holon - The holon identifier.
|
|
7
|
+
* @param {string} lens - The lens under which to store the node.
|
|
8
|
+
* @param {object} data - The node to store.
|
|
9
|
+
*/
|
|
10
|
+
export async function putNode(holoInstance, holon, lens, data) {
|
|
11
|
+
if (!holon || !lens || !data) {
|
|
12
|
+
throw new Error('putNode: Missing required parameters');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
try {
|
|
17
|
+
// Remove isHologram field before storing - NO LONGER NEEDED
|
|
18
|
+
// if (data && data.isHologram !== undefined) {
|
|
19
|
+
// delete data.isHologram;
|
|
20
|
+
// }
|
|
21
|
+
holoInstance.gun.get(holoInstance.appname)
|
|
22
|
+
.get(holon)
|
|
23
|
+
.get(lens)
|
|
24
|
+
.get('value') // Store at 'value' key
|
|
25
|
+
.put(data.value, ack => { // Store the value directly
|
|
26
|
+
if (ack.err) {
|
|
27
|
+
reject(new Error(ack.err));
|
|
28
|
+
} else {
|
|
29
|
+
resolve(true);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
} catch (error) {
|
|
33
|
+
reject(error);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Retrieves a specific gun node from the specified holon and lens.
|
|
40
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
41
|
+
* @param {string} holon - The holon identifier.
|
|
42
|
+
* @param {string} lens - The lens identifier.
|
|
43
|
+
* @param {string} key - The specific key to retrieve.
|
|
44
|
+
* @returns {Promise<any>} - The retrieved node or null if not found.
|
|
45
|
+
*/
|
|
46
|
+
export async function getNode(holoInstance, holon, lens, key) {
|
|
47
|
+
if (!holon || !lens || !key) {
|
|
48
|
+
throw new Error('getNode: Missing required parameters');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
try {
|
|
53
|
+
holoInstance.gun.get(holoInstance.appname)
|
|
54
|
+
.get(holon)
|
|
55
|
+
.get(lens)
|
|
56
|
+
.get(key)
|
|
57
|
+
.once((data) => {
|
|
58
|
+
if (!data) {
|
|
59
|
+
resolve(null);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
resolve(data); // Return the data directly
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
reject(error);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Retrieves a Gun node reference using its soul path
|
|
72
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
73
|
+
* @param {string} soul - The soul path of the node
|
|
74
|
+
* @returns {Gun.ChainReference} - The Gun node reference
|
|
75
|
+
*/
|
|
76
|
+
export function getNodeRef(holoInstance, soul) {
|
|
77
|
+
if (typeof soul !== 'string' || !soul) {
|
|
78
|
+
throw new Error('getNodeRef: Invalid soul parameter');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const parts = soul.split('/').filter(part => {
|
|
82
|
+
if (!part.trim() || /[<>:"/\\|?*]/.test(part)) { // Escaped backslash for regex
|
|
83
|
+
throw new Error('getNodeRef: Invalid path segment');
|
|
84
|
+
}
|
|
85
|
+
return part.trim();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (parts.length === 0) {
|
|
89
|
+
throw new Error('getNodeRef: Invalid soul format');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let ref = holoInstance.gun.get(holoInstance.appname);
|
|
93
|
+
parts.forEach(part => {
|
|
94
|
+
ref = ref.get(part);
|
|
95
|
+
});
|
|
96
|
+
return ref;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Retrieves a node directly using its soul path
|
|
101
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
102
|
+
* @param {string} soul - The soul path of the node
|
|
103
|
+
* @returns {Promise<any>} - The retrieved node or null if not found.
|
|
104
|
+
*/
|
|
105
|
+
export async function getNodeBySoul(holoInstance, soul) {
|
|
106
|
+
if (!soul) {
|
|
107
|
+
throw new Error('getNodeBySoul: Missing soul parameter');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log(`getNodeBySoul: Accessing soul ${soul}`);
|
|
111
|
+
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
try {
|
|
114
|
+
const ref = getNodeRef(holoInstance, soul); // Use the exported getNodeRef
|
|
115
|
+
ref.once((data) => {
|
|
116
|
+
console.log(`getNodeBySoul: Retrieved data:`, data);
|
|
117
|
+
if (!data) {
|
|
118
|
+
resolve(null);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
resolve(data); // Return the data directly
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(`getNodeBySoul error:`, error);
|
|
125
|
+
reject(error);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Deletes a specific gun node from a given holon and lens.
|
|
132
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
133
|
+
* @param {string} holon - The holon identifier.
|
|
134
|
+
* @param {string} lens - The lens identifier.
|
|
135
|
+
* @param {string} key - The key of the node to delete.
|
|
136
|
+
* @returns {Promise<boolean>} - Returns true if successful
|
|
137
|
+
*/
|
|
138
|
+
export async function deleteNode(holoInstance, holon, lens, key) {
|
|
139
|
+
if (!holon || !lens || !key) {
|
|
140
|
+
throw new Error('deleteNode: Missing required parameters');
|
|
141
|
+
}
|
|
142
|
+
return new Promise((resolve, reject) => {
|
|
143
|
+
holoInstance.gun.get(holoInstance.appname)
|
|
144
|
+
.get(holon)
|
|
145
|
+
.get(lens)
|
|
146
|
+
.get(key)
|
|
147
|
+
.put(null, ack => {
|
|
148
|
+
if (ack.err) {
|
|
149
|
+
reject(new Error(ack.err));
|
|
150
|
+
} else {
|
|
151
|
+
resolve(true);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "holosphere",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
4
4
|
"description": "Holonic Geospatial Communication Infrastructure",
|
|
5
5
|
"main": "holosphere.js",
|
|
6
6
|
"types": "holosphere.d.ts",
|
|
@@ -24,9 +24,6 @@
|
|
|
24
24
|
},
|
|
25
25
|
"jest": {
|
|
26
26
|
"testEnvironment": "node",
|
|
27
|
-
"transform": {}
|
|
28
|
-
"moduleNameMapper": {
|
|
29
|
-
"^(\\.{1,2}/.*)\\.js$": "$1"
|
|
30
|
-
}
|
|
27
|
+
"transform": {}
|
|
31
28
|
}
|
|
32
29
|
}
|
package/schema.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// holo_schema.js
|
|
2
|
+
|
|
3
|
+
import Ajv2019 from 'ajv/dist/2019.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sets the JSON schema for a specific lens.
|
|
7
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
8
|
+
* @param {string} lens - The lens identifier.
|
|
9
|
+
* @param {object} schema - The JSON schema to set.
|
|
10
|
+
* @returns {Promise<boolean>} - Resolves when the schema is set.
|
|
11
|
+
*/
|
|
12
|
+
export async function setSchema(holoInstance, lens, schema) {
|
|
13
|
+
if (!lens || !schema) {
|
|
14
|
+
throw new Error('setSchema: Missing required parameters');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Basic schema validation
|
|
18
|
+
if (!schema.type || typeof schema.type !== 'string') {
|
|
19
|
+
throw new Error('setSchema: Schema must have a type field');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const metaSchema = {
|
|
23
|
+
type: 'object',
|
|
24
|
+
required: ['type', 'properties'],
|
|
25
|
+
properties: {
|
|
26
|
+
type: { type: 'string' },
|
|
27
|
+
properties: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
additionalProperties: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
required: ['type'],
|
|
32
|
+
properties: {
|
|
33
|
+
type: { type: 'string' }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
required: {
|
|
38
|
+
type: 'array',
|
|
39
|
+
items: { type: 'string' }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Use the validator from the instance
|
|
45
|
+
const valid = holoInstance.validator.validate(metaSchema, schema);
|
|
46
|
+
if (!valid) {
|
|
47
|
+
throw new Error(`Invalid schema structure: ${JSON.stringify(holoInstance.validator.errors)}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!schema.properties || typeof schema.properties !== 'object') {
|
|
51
|
+
throw new Error('Schema must have properties in strict mode');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!schema.required || !Array.isArray(schema.required) || schema.required.length === 0) {
|
|
55
|
+
throw new Error('Schema must have required fields in strict mode');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Store schema in global table with lens as key using instance's method
|
|
59
|
+
await holoInstance.putGlobal('schemas', {
|
|
60
|
+
id: lens,
|
|
61
|
+
schema: schema,
|
|
62
|
+
timestamp: Date.now()
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Update the instance's cache with the new schema
|
|
66
|
+
holoInstance.schemaCache.set(lens, {
|
|
67
|
+
schema,
|
|
68
|
+
timestamp: Date.now()
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Retrieves the JSON schema for a specific lens.
|
|
76
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
77
|
+
* @param {string} lens - The lens identifier.
|
|
78
|
+
* @param {object} [options] - Additional options
|
|
79
|
+
* @param {boolean} [options.useCache=true] - Whether to use the schema cache
|
|
80
|
+
* @param {number} [options.maxCacheAge=3600000] - Maximum cache age in milliseconds (default: 1 hour)
|
|
81
|
+
* @returns {Promise<object|null>} - The retrieved schema or null if not found.
|
|
82
|
+
*/
|
|
83
|
+
export async function getSchema(holoInstance, lens, options = {}) {
|
|
84
|
+
if (!lens) {
|
|
85
|
+
throw new Error('getSchema: Missing lens parameter');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const { useCache = true, maxCacheAge = 3600000 } = options;
|
|
89
|
+
|
|
90
|
+
// Check instance's cache first if enabled
|
|
91
|
+
if (useCache && holoInstance.schemaCache.has(lens)) {
|
|
92
|
+
const cached = holoInstance.schemaCache.get(lens);
|
|
93
|
+
const cacheAge = Date.now() - cached.timestamp;
|
|
94
|
+
|
|
95
|
+
// Use cache if it's fresh enough
|
|
96
|
+
if (cacheAge < maxCacheAge) {
|
|
97
|
+
return cached.schema;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Cache miss or expired, fetch from storage using instance's method
|
|
102
|
+
const schemaData = await holoInstance.getGlobal('schemas', lens);
|
|
103
|
+
|
|
104
|
+
if (!schemaData || !schemaData.schema) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Update instance's cache with fetched schema
|
|
109
|
+
holoInstance.schemaCache.set(lens, {
|
|
110
|
+
schema: schemaData.schema,
|
|
111
|
+
timestamp: Date.now()
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return schemaData.schema;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Clears the schema cache or a specific schema from the cache.
|
|
119
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
120
|
+
* @param {string} [lens] - Optional lens to clear from cache. If not provided, clears entire cache.
|
|
121
|
+
* @returns {boolean} - Returns true if successful
|
|
122
|
+
*/
|
|
123
|
+
export function clearSchemaCache(holoInstance, lens = null) {
|
|
124
|
+
if (lens) {
|
|
125
|
+
// Clear specific schema from instance's cache
|
|
126
|
+
return holoInstance.schemaCache.delete(lens);
|
|
127
|
+
} else {
|
|
128
|
+
// Clear entire instance's cache
|
|
129
|
+
holoInstance.schemaCache.clear();
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
package/test/auth.test.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import Gun from 'gun';
|
|
2
1
|
import HoloSphere from '../holosphere.js';
|
|
3
|
-
import * as h3 from 'h3-js';
|
|
4
2
|
import { jest } from '@jest/globals';
|
|
5
3
|
|
|
6
4
|
// Increase timeout for all tests
|
|
7
|
-
jest.setTimeout(
|
|
5
|
+
jest.setTimeout(30000);
|
|
8
6
|
|
|
9
7
|
describe('HoloSphere Authentication and Authorization', () => {
|
|
10
8
|
let holoSphere;
|
|
@@ -12,10 +10,22 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
12
10
|
const testPassword = 'TestPass123!';
|
|
13
11
|
const testHolon = 'test-holon';
|
|
14
12
|
const testLens = 'test-lens';
|
|
13
|
+
const PUBLIC_GLOBAL_TABLE = 'publicTestTable'; // For public global data
|
|
14
|
+
const PRIVATE_GLOBAL_TABLE = 'veryPrivateGlobalTable'; // For all private global data tests
|
|
15
15
|
|
|
16
16
|
beforeAll(async () => {
|
|
17
17
|
holoSphere = new HoloSphere('test-app', false, null);
|
|
18
18
|
strictHoloSphere = new HoloSphere('test-app-strict', true, null);
|
|
19
|
+
// Wait for initialization
|
|
20
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
// Clean state before each test
|
|
25
|
+
await holoSphere.deleteAll(testHolon, testLens);
|
|
26
|
+
await holoSphere.deleteAllGlobal(PUBLIC_GLOBAL_TABLE);
|
|
27
|
+
await holoSphere.deleteAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword);
|
|
28
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
19
29
|
});
|
|
20
30
|
|
|
21
31
|
afterEach(async () => {
|
|
@@ -34,38 +44,70 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
34
44
|
|
|
35
45
|
afterAll(async () => {
|
|
36
46
|
// Clean up all test data
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
try {
|
|
48
|
+
if (holoSphere) {
|
|
49
|
+
await holoSphere.deleteAll(testHolon, testLens);
|
|
50
|
+
await holoSphere.deleteAllGlobal(PUBLIC_GLOBAL_TABLE);
|
|
51
|
+
await holoSphere.deleteAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword);
|
|
52
|
+
}
|
|
53
|
+
if (strictHoloSphere) {
|
|
54
|
+
await strictHoloSphere.deleteAll(testHolon, testLens);
|
|
55
|
+
await strictHoloSphere.deleteAllGlobal(PUBLIC_GLOBAL_TABLE);
|
|
56
|
+
await strictHoloSphere.deleteAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword);
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('Error during afterAll data cleanup:', error);
|
|
43
60
|
}
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
|
|
62
|
+
// Close HoloSphere instances
|
|
63
|
+
try {
|
|
64
|
+
if (holoSphere) {
|
|
65
|
+
console.log('Closing non-strict HoloSphere instance...');
|
|
66
|
+
await holoSphere.close();
|
|
67
|
+
console.log('Non-strict HoloSphere instance closed.');
|
|
68
|
+
}
|
|
69
|
+
if (strictHoloSphere) {
|
|
70
|
+
console.log('Closing strict HoloSphere instance...');
|
|
71
|
+
await strictHoloSphere.close();
|
|
72
|
+
console.log('Strict HoloSphere instance closed.');
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error('Error during afterAll close:', error);
|
|
46
76
|
}
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
|
|
77
|
+
|
|
78
|
+
// Add a slightly longer, more explicit wait after close calls
|
|
79
|
+
console.log('Waiting extra time for cleanup...');
|
|
80
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
81
|
+
console.log('Finished afterAll.');
|
|
50
82
|
});
|
|
51
83
|
|
|
52
84
|
describe('Authentication System', () => {
|
|
53
|
-
it('should authenticate with password and
|
|
85
|
+
it('should authenticate with password and handle auth failures', async () => {
|
|
54
86
|
const testData = { id: 'test1', value: 'private-data' };
|
|
55
87
|
|
|
56
88
|
// Test storing with authentication
|
|
57
89
|
await holoSphere.put(testHolon, testLens, testData, testPassword);
|
|
90
|
+
// Wait for data to be properly stored
|
|
91
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
58
92
|
|
|
59
|
-
// Test retrieving with
|
|
60
|
-
const
|
|
61
|
-
expect(
|
|
62
|
-
|
|
93
|
+
// Test retrieving with wrong password
|
|
94
|
+
const wrongResult = await holoSphere.get(testHolon, testLens, testData.id, 'wrong_password');
|
|
95
|
+
expect(wrongResult).toBeNull();
|
|
96
|
+
|
|
97
|
+
// Test retrieving with no password
|
|
98
|
+
const noPassResult = await holoSphere.get(testHolon, testLens, testData.id);
|
|
99
|
+
expect(noPassResult).toBeNull();
|
|
100
|
+
|
|
101
|
+
// Test retrieving with correct password
|
|
102
|
+
const correctResult = await holoSphere.get(testHolon, testLens, testData.id, testPassword);
|
|
103
|
+
expect(correctResult).toEqual(testData); // Use full object comparison
|
|
63
104
|
});
|
|
64
105
|
|
|
65
106
|
it('should handle authentication errors gracefully', async () => {
|
|
66
107
|
const testData = { id: 'test2', value: 'private-data' };
|
|
67
108
|
|
|
68
109
|
// Store data with correct password
|
|
110
|
+
await new Promise(resolve => setTimeout(resolve, 100)); // Added delay
|
|
69
111
|
await holoSphere.put(testHolon, testLens, testData, testPassword);
|
|
70
112
|
|
|
71
113
|
// Try to retrieve with wrong password
|
|
@@ -103,15 +145,16 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
103
145
|
const testData = { id: 'test', value: 123 };
|
|
104
146
|
|
|
105
147
|
// Should work in non-strict mode without schema
|
|
106
|
-
await expect(holoSphere.put(testHolon,
|
|
148
|
+
await expect(holoSphere.put(testHolon, 'nonExistentLens', testData)).resolves.toBeTruthy();
|
|
107
149
|
|
|
108
|
-
// Delete any existing schema
|
|
109
|
-
await strictHoloSphere.putGlobal('schemas', { id:
|
|
150
|
+
// Delete any existing schema for the lens
|
|
151
|
+
await strictHoloSphere.putGlobal('schemas', { id: 'nonExistentLens', schema: null });
|
|
110
152
|
|
|
111
|
-
// Should fail in strict mode without schema
|
|
112
153
|
try {
|
|
113
|
-
|
|
114
|
-
|
|
154
|
+
// This should throw an error in strict mode
|
|
155
|
+
await strictHoloSphere.put(testHolon, 'nonExistentLens', testData);
|
|
156
|
+
// If we get here, the test should fail
|
|
157
|
+
expect('Expected an error').toBe('but none was thrown');
|
|
115
158
|
} catch (error) {
|
|
116
159
|
expect(error.message).toBe('Schema required in strict mode');
|
|
117
160
|
}
|
|
@@ -186,41 +229,35 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
186
229
|
|
|
187
230
|
describe('Global Data Operations', () => {
|
|
188
231
|
it('should handle private global data', async () => {
|
|
189
|
-
const testData = { id: '
|
|
190
|
-
|
|
191
|
-
await holoSphere.
|
|
192
|
-
|
|
193
|
-
const result = await holoSphere.getGlobal('testTable', testData.id, testPassword);
|
|
232
|
+
const testData = { id: 'globalPrivateItem', value: 111 };
|
|
233
|
+
await holoSphere.putGlobal(PRIVATE_GLOBAL_TABLE, testData, testPassword);
|
|
234
|
+
const result = await holoSphere.getGlobal(PRIVATE_GLOBAL_TABLE, testData.id, testPassword);
|
|
194
235
|
expect(result).toEqual(testData);
|
|
195
236
|
});
|
|
196
237
|
|
|
197
238
|
it('should handle public global data', async () => {
|
|
198
|
-
const testData = { id: '
|
|
199
|
-
|
|
200
|
-
await holoSphere.
|
|
201
|
-
|
|
202
|
-
const result = await holoSphere.getGlobal('testTable', testData.id);
|
|
239
|
+
const testData = { id: 'publicGlobalItem', value: 222 };
|
|
240
|
+
await holoSphere.putGlobal(PUBLIC_GLOBAL_TABLE, testData);
|
|
241
|
+
const result = await holoSphere.getGlobal(PUBLIC_GLOBAL_TABLE, testData.id);
|
|
203
242
|
expect(result).toEqual(testData);
|
|
204
243
|
});
|
|
205
244
|
|
|
206
245
|
it('should handle getAllGlobal with private data', async () => {
|
|
207
246
|
const testData = [
|
|
208
|
-
{ id: '
|
|
209
|
-
{ id: '
|
|
247
|
+
{ id: 'globalPrivate1', value: 1 },
|
|
248
|
+
{ id: 'globalPrivate2', value: 2 }
|
|
210
249
|
];
|
|
211
250
|
|
|
212
|
-
// Clean up
|
|
213
|
-
await holoSphere.deleteAllGlobal(
|
|
251
|
+
// Clean up before this specific test (already done in beforeEach, but good for clarity)
|
|
252
|
+
// await holoSphere.deleteAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword); // This might be re-enabled later if tests pass without it
|
|
214
253
|
|
|
215
|
-
// Store each item
|
|
216
254
|
for (const data of testData) {
|
|
217
|
-
await holoSphere.putGlobal(
|
|
255
|
+
await holoSphere.putGlobal(PRIVATE_GLOBAL_TABLE, data, testPassword);
|
|
218
256
|
}
|
|
219
257
|
|
|
220
|
-
|
|
221
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
258
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Settle time
|
|
222
259
|
|
|
223
|
-
const results = await holoSphere.getAllGlobal(
|
|
260
|
+
const results = await holoSphere.getAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword);
|
|
224
261
|
expect(results.length).toBe(testData.length);
|
|
225
262
|
for (const data of testData) {
|
|
226
263
|
expect(results).toContainEqual(expect.objectContaining(data));
|
|
@@ -228,13 +265,10 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
228
265
|
}, 10000);
|
|
229
266
|
|
|
230
267
|
it('should handle deleteGlobal with private data', async () => {
|
|
231
|
-
const testData = { id: '
|
|
232
|
-
|
|
233
|
-
await holoSphere.
|
|
234
|
-
|
|
235
|
-
await holoSphere.deleteGlobal('testTable', testData.id, testPassword);
|
|
236
|
-
|
|
237
|
-
const result = await holoSphere.getGlobal('testTable', testData.id, testPassword);
|
|
268
|
+
const testData = { id: 'deleteGlobalPrivate', value: 333 };
|
|
269
|
+
await holoSphere.putGlobal(PRIVATE_GLOBAL_TABLE, testData, testPassword);
|
|
270
|
+
await holoSphere.deleteGlobal(PRIVATE_GLOBAL_TABLE, testData.id, testPassword);
|
|
271
|
+
const result = await holoSphere.getGlobal(PRIVATE_GLOBAL_TABLE, testData.id, testPassword);
|
|
238
272
|
expect(result).toBeNull();
|
|
239
273
|
});
|
|
240
274
|
});
|
package/test/delete.test.js
CHANGED
|
@@ -4,6 +4,9 @@ import { jest } from '@jest/globals';
|
|
|
4
4
|
// Configure Jest
|
|
5
5
|
jest.setTimeout(30000); // 30 second timeout
|
|
6
6
|
|
|
7
|
+
// Utility to wait for GunDB propagation
|
|
8
|
+
const waitForGun = (delay = 250) => new Promise(resolve => setTimeout(resolve, delay));
|
|
9
|
+
|
|
7
10
|
describe('HoloSphere Deletion Tests', () => {
|
|
8
11
|
const testAppName = 'test-app-deletion';
|
|
9
12
|
const testHolon = 'testHolonDeletion';
|
|
@@ -106,20 +109,21 @@ describe('HoloSphere Deletion Tests', () => {
|
|
|
106
109
|
test('should delete global items properly', async () => {
|
|
107
110
|
// Create global test data
|
|
108
111
|
const globalData = { id: 'global-delete-test', value: 'global delete me' };
|
|
109
|
-
|
|
110
|
-
// Store global data
|
|
112
|
+
|
|
113
|
+
// 1. Store global data
|
|
111
114
|
await holoSphere.putGlobal(testGlobalTable, globalData);
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// Delete global data
|
|
115
|
+
|
|
116
|
+
// 2. Wait significantly for put to settle
|
|
117
|
+
await waitForGun(1500); // Generous wait after put
|
|
118
|
+
|
|
119
|
+
// 3. Delete global data
|
|
119
120
|
const deleteResult = await holoSphere.deleteGlobal(testGlobalTable, globalData.id);
|
|
120
121
|
expect(deleteResult).toBe(true);
|
|
121
|
-
|
|
122
|
-
//
|
|
122
|
+
|
|
123
|
+
// 4. Wait for delete to settle
|
|
124
|
+
await waitForGun(500); // Wait after delete
|
|
125
|
+
|
|
126
|
+
// 5. Verify global data is deleted
|
|
123
127
|
const deletedGlobalData = await holoSphere.getGlobal(testGlobalTable, globalData.id);
|
|
124
128
|
expect(deletedGlobalData).toBeNull();
|
|
125
129
|
});
|