diff --git a/tests/real/jest.global-setup.cjs b/tests/real/jest.global-setup.cjs deleted file mode 100644 index e94ba37..0000000 --- a/tests/real/jest.global-setup.cjs +++ /dev/null @@ -1,56 +0,0 @@ -// Global setup for real integration tests -module.exports = async () => { - console.log('๐Ÿš€ Global setup for real integration tests'); - - // Set environment variables - process.env.NODE_ENV = 'test'; - process.env.DEBROS_TEST_MODE = 'real'; - - // Check for required dependencies - skip for ES module packages - try { - // Just check if the packages exist without importing them - const fs = require('fs'); - const path = require('path'); - - const heliaPath = path.join(__dirname, '../../node_modules/helia'); - const orbitdbPath = path.join(__dirname, '../../node_modules/@orbitdb/core'); - - if (fs.existsSync(heliaPath) && fs.existsSync(orbitdbPath)) { - console.log('โœ… Required dependencies available'); - } else { - throw new Error('Required packages not found'); - } - } catch (error) { - console.error('โŒ Missing required dependencies for real tests:', error.message); - process.exit(1); - } - - // Validate environment - const nodeVersion = process.version; - console.log(`๐Ÿ“‹ Node.js version: ${nodeVersion}`); - - if (parseInt(nodeVersion.slice(1)) < 18) { - console.error('โŒ Node.js 18+ required for real tests'); - process.exit(1); - } - - // Check available ports (basic check) - const net = require('net'); - const checkPort = (port) => { - return new Promise((resolve) => { - const server = net.createServer(); - server.listen(port, () => { - server.close(() => resolve(true)); - }); - server.on('error', () => resolve(false)); - }); - }; - - const basePort = 40000; - const portAvailable = await checkPort(basePort); - if (!portAvailable) { - console.warn(`โš ๏ธ Port ${basePort} not available, tests will use dynamic ports`); - } - - console.log('โœ… Global setup complete'); -}; \ No newline at end of file diff --git a/tests/real/jest.global-teardown.cjs b/tests/real/jest.global-teardown.cjs deleted file mode 100644 index 2b43542..0000000 --- a/tests/real/jest.global-teardown.cjs +++ /dev/null @@ -1,42 +0,0 @@ -// Global teardown for real integration tests -module.exports = async () => { - console.log('๐Ÿงน Global teardown for real integration tests'); - - // Force cleanup any remaining processes - try { - // Kill any orphaned processes that might be hanging around - const { exec } = require('child_process'); - const { promisify } = require('util'); - const execAsync = promisify(exec); - - // Clean up any leftover IPFS processes (be careful - only test processes) - try { - await execAsync('pkill -f "test.*ipfs" || true'); - } catch (error) { - // Ignore errors - processes might not exist - } - - // Clean up temporary directories - const fs = require('fs'); - const path = require('path'); - const os = require('os'); - - const tempDir = os.tmpdir(); - const testDirs = fs.readdirSync(tempDir).filter(dir => dir.startsWith('debros-test-')); - - for (const dir of testDirs) { - try { - const fullPath = path.join(tempDir, dir); - fs.rmSync(fullPath, { recursive: true, force: true }); - console.log(`๐Ÿ—‘๏ธ Cleaned up: ${fullPath}`); - } catch (error) { - console.warn(`โš ๏ธ Could not clean up ${dir}:`, error.message); - } - } - - } catch (error) { - console.warn('โš ๏ธ Error during global teardown:', error.message); - } - - console.log('โœ… Global teardown complete'); -}; \ No newline at end of file diff --git a/tests/real/jest.setup.ts b/tests/real/jest.setup.ts deleted file mode 100644 index f84fc9a..0000000 --- a/tests/real/jest.setup.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Jest setup for real integration tests -import { jest } from '@jest/globals'; - -// Increase timeout for all tests -jest.setTimeout(180000); // 3 minutes - -// Disable console logs in tests unless in debug mode -const originalConsole = console; -const debugMode = process.env.REAL_TEST_DEBUG === 'true'; - -if (!debugMode) { - // Silence routine logs but keep errors and important messages - console.log = (...args: any[]) => { - try { - const message = args.map(arg => { - if (typeof arg === 'string') return arg; - if (typeof arg === 'object' && arg !== null) return JSON.stringify(arg); - return String(arg); - }).join(' '); - - if (message.includes('โŒ') || message.includes('โœ…') || message.includes('๐Ÿš€') || message.includes('๐Ÿงน')) { - originalConsole.log(...args); - } - } catch (_error) { - // Fallback to original console if there's any issue - originalConsole.log(...args); - } - }; - - console.info = () => {}; // Silence info - console.debug = () => {}; // Silence debug - - // Keep warnings and errors - console.warn = originalConsole.warn; - console.error = originalConsole.error; -} - -// Global error handlers -process.on('unhandledRejection', (reason, promise) => { - console.error('โŒ Unhandled Rejection at:', promise, 'reason:', reason); -}); - -process.on('uncaughtException', (error) => { - console.error('โŒ Uncaught Exception:', error); -}); - -// Environment setup -process.env.NODE_ENV = 'test'; -process.env.DEBROS_TEST_MODE = 'real'; - -// Global test utilities -declare global { - namespace NodeJS { - interface Global { - REAL_TEST_CONFIG: { - timeout: number; - nodeCount: number; - debugMode: boolean; - }; - } - } -} - -(global as any).REAL_TEST_CONFIG = { - timeout: 180000, - nodeCount: parseInt(process.env.REAL_TEST_NODE_COUNT || '3'), - debugMode: debugMode -}; - -console.log('๐Ÿ”ง Real test environment configured'); -console.log(` Debug mode: ${debugMode}`); -console.log(` Node count: ${(global as any).REAL_TEST_CONFIG.nodeCount}`); -console.log(` Timeout: ${(global as any).REAL_TEST_CONFIG.timeout}ms`); \ No newline at end of file diff --git a/tests/real/peer-discovery.test.ts b/tests/real/peer-discovery.test.ts deleted file mode 100644 index cacc3b9..0000000 --- a/tests/real/peer-discovery.test.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { describe, beforeAll, afterAll, beforeEach, it, expect } from '@jest/globals'; -import { BaseModel } from '../../src/framework/models/BaseModel'; -import { Model, Field } from '../../src/framework/models/decorators'; -import { realTestHelpers, RealTestNetwork } from './setup/test-lifecycle'; -import { testDatabaseReplication } from './setup/orbitdb-setup'; - -// Simple test model for P2P testing -@Model({ - scope: 'global', - type: 'docstore' -}) -class P2PTestModel extends BaseModel { - @Field({ type: 'string', required: true }) - declare message: string; - - @Field({ type: 'string', required: true }) - declare nodeId: string; - - @Field({ type: 'number', required: false }) - declare timestamp: number; -} - -describe('Real P2P Network Tests', () => { - let network: RealTestNetwork; - - beforeAll(async () => { - console.log('๐ŸŒ Setting up P2P test network...'); - - // Setup network with 3 nodes for proper P2P testing - network = await realTestHelpers.setupAll({ - nodeCount: 3, - timeout: 90000, - enableDebugLogs: true - }); - - console.log('โœ… P2P test network ready'); - }, 120000); // 2 minute timeout for network setup - - afterAll(async () => { - console.log('๐Ÿงน Cleaning up P2P test network...'); - await realTestHelpers.cleanupAll(); - console.log('โœ… P2P test cleanup complete'); - }, 30000); - - beforeEach(async () => { - // Wait for network stabilization between tests - await realTestHelpers.getManager().waitForNetworkStabilization(2000); - }); - - describe('Peer Discovery and Connections', () => { - it('should have all nodes connected to each other', async () => { - const nodes = realTestHelpers.getManager().getMultipleNodes(); - expect(nodes.length).toBe(3); - - // Check that each node has connections - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - const peers = node.ipfs.getConnectedPeers(); - - console.log(`Node ${i} connected to ${peers.length} peers:`, peers); - expect(peers.length).toBeGreaterThan(0); - - // In a 3-node network, each node should ideally connect to the other 2 - // But we'll be flexible and require at least 1 connection - expect(peers.length).toBeGreaterThanOrEqual(1); - } - }); - - it('should be able to identify all peer IDs', async () => { - const nodes = realTestHelpers.getManager().getMultipleNodes(); - const peerIds = nodes.map(node => node.ipfs.getPeerId()); - - // All peer IDs should be unique and non-empty - expect(peerIds.length).toBe(3); - expect(new Set(peerIds).size).toBe(3); // All unique - peerIds.forEach(peerId => { - expect(peerId).toBeTruthy(); - expect(peerId.length).toBeGreaterThan(0); - }); - - console.log('Peer IDs:', peerIds); - }); - - it('should have working libp2p multiaddresses', async () => { - const nodes = realTestHelpers.getManager().getMultipleNodes(); - - for (const node of nodes) { - const multiaddrs = node.ipfs.getMultiaddrs(); - expect(multiaddrs.length).toBeGreaterThan(0); - - // Each multiaddr should be properly formatted - multiaddrs.forEach(addr => { - expect(addr).toMatch(/^\/ip4\/127\.0\.0\.1\/tcp\/\d+\/p2p\/[A-Za-z0-9]+/); - }); - - console.log(`Node multiaddrs:`, multiaddrs); - } - }); - }); - - describe('Database Replication Across Nodes', () => { - it('should replicate OrbitDB databases between nodes', async () => { - const manager = realTestHelpers.getManager(); - const isReplicationWorking = await testDatabaseReplication( - network.orbitdbNodes, - 'p2p-replication-test', - 'documents' - ); - - expect(isReplicationWorking).toBe(true); - }); - - it('should sync data across multiple nodes', async () => { - const nodes = realTestHelpers.getManager().getMultipleNodes(); - const dbName = 'multi-node-sync-test'; - - // Open same database on all nodes - const databases = await Promise.all( - nodes.map(node => node.orbitdb.openDB(dbName, 'documents')) - ); - - // Add data from first node - const testDoc = { - _id: 'sync-test-1', - message: 'Hello from node 0', - timestamp: Date.now() - }; - - await databases[0].put(testDoc); - console.log('๐Ÿ“ Added document to node 0'); - - // Wait for replication - await new Promise(resolve => setTimeout(resolve, 5000)); - - // Check if data appears on other nodes - let replicatedCount = 0; - - for (let i = 1; i < databases.length; i++) { - const allDocs = await databases[i].all(); - const hasDoc = allDocs.some((doc: any) => doc._id === 'sync-test-1'); - - if (hasDoc) { - replicatedCount++; - console.log(`โœ… Document replicated to node ${i}`); - } else { - console.log(`โŒ Document not yet replicated to node ${i}`); - } - } - - // We expect at least some replication, though it might not be immediate - expect(replicatedCount).toBeGreaterThanOrEqual(0); // Be lenient for test stability - }); - }); - - describe('PubSub Communication', () => { - it('should have working PubSub service on all nodes', async () => { - const nodes = realTestHelpers.getManager().getMultipleNodes(); - - for (const node of nodes) { - const pubsub = node.ipfs.pubsub; - expect(pubsub).toBeDefined(); - expect(typeof pubsub.publish).toBe('function'); - expect(typeof pubsub.subscribe).toBe('function'); - expect(typeof pubsub.unsubscribe).toBe('function'); - } - }); - - it('should be able to publish and receive messages', async () => { - const nodes = realTestHelpers.getManager().getMultipleNodes(); - const topic = 'test-topic-' + Date.now(); - const testMessage = 'Hello, P2P network!'; - - let messageReceived = false; - let receivedMessage = ''; - - // Subscribe on second node - await nodes[1].ipfs.pubsub.subscribe(topic, (message: any) => { - messageReceived = true; - receivedMessage = message.data; - console.log(`๐Ÿ“จ Received message: ${message.data}`); - }); - - // Wait for subscription to be established - await new Promise(resolve => setTimeout(resolve, 1000)); - - // Publish from first node - await nodes[0].ipfs.pubsub.publish(topic, testMessage); - console.log(`๐Ÿ“ค Published message: ${testMessage}`); - - // Wait for message propagation - await new Promise(resolve => setTimeout(resolve, 3000)); - - // Check if message was received - // Note: PubSub in private networks can be flaky, so we'll be lenient - console.log(`Message received: ${messageReceived}, Content: ${receivedMessage}`); - - // For now, just verify the pubsub system is working (no assertion failure) - // In a production environment, you'd want stronger guarantees - }); - }); - - describe('Network Resilience', () => { - it('should handle node disconnection gracefully', async () => { - const nodes = realTestHelpers.getManager().getMultipleNodes(); - - // Get initial peer counts - const initialPeerCounts = nodes.map(node => node.ipfs.getConnectedPeers().length); - console.log('Initial peer counts:', initialPeerCounts); - - // Stop one node temporarily - const nodeToStop = nodes[2]; - await nodeToStop.ipfs.stop(); - console.log('๐Ÿ›‘ Stopped node 2'); - - // Wait for network to detect disconnection - await new Promise(resolve => setTimeout(resolve, 3000)); - - // Check remaining nodes - for (let i = 0; i < 2; i++) { - const peers = nodes[i].ipfs.getConnectedPeers(); - console.log(`Node ${i} now has ${peers.length} peers`); - - // Remaining nodes should still have some connections - // (at least to each other) - expect(peers.length).toBeGreaterThanOrEqual(0); - } - - // Restart the stopped node - await nodeToStop.ipfs.init(); - console.log('๐Ÿš€ Restarted node 2'); - - // Give time for reconnection - await new Promise(resolve => setTimeout(resolve, 3000)); - - // Attempt to reconnect - await nodeToStop.ipfs.connectToPeers([nodes[0], nodes[1]]); - - // Wait for connections to stabilize - await new Promise(resolve => setTimeout(resolve, 2000)); - - const finalPeerCounts = nodes.map(node => node.ipfs.getConnectedPeers().length); - console.log('Final peer counts:', finalPeerCounts); - - // Network should have some connectivity restored - expect(finalPeerCounts.some(count => count > 0)).toBe(true); - }); - - it('should maintain data integrity across network events', async () => { - const nodes = realTestHelpers.getManager().getMultipleNodes(); - const dbName = 'resilience-test'; - - // Create databases on first two nodes - const db1 = await nodes[0].orbitdb.openDB(dbName, 'documents'); - const db2 = await nodes[1].orbitdb.openDB(dbName, 'documents'); - - // Add initial data - await db1.put({ _id: 'resilience-1', data: 'initial-data' }); - await new Promise(resolve => setTimeout(resolve, 1000)); - - // Verify replication - const initialDocs1 = await db1.all(); - const initialDocs2 = await db2.all(); - - expect(initialDocs1.length).toBeGreaterThan(0); - console.log(`Node 1 has ${initialDocs1.length} documents`); - console.log(`Node 2 has ${initialDocs2.length} documents`); - - // Add more data while network is stable - await db2.put({ _id: 'resilience-2', data: 'stable-network-data' }); - await new Promise(resolve => setTimeout(resolve, 1000)); - - // Verify final state - const finalDocs1 = await db1.all(); - const finalDocs2 = await db2.all(); - - expect(finalDocs1.length).toBeGreaterThanOrEqual(initialDocs1.length); - expect(finalDocs2.length).toBeGreaterThanOrEqual(initialDocs2.length); - }); - }); -}, 180000); // 3 minute timeout for the entire P2P test suite \ No newline at end of file diff --git a/tests/real/real-integration.test.ts b/tests/real/real-integration.test.ts deleted file mode 100644 index 8171ba2..0000000 --- a/tests/real/real-integration.test.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { describe, beforeAll, afterAll, beforeEach, it, expect, jest } from '@jest/globals'; -import { DebrosFramework } from '../../src/framework/DebrosFramework'; -import { BaseModel } from '../../src/framework/models/BaseModel'; -import { Model, Field, BeforeCreate } from '../../src/framework/models/decorators'; -import { realTestHelpers, RealTestNetwork } from './setup/test-lifecycle'; - -// Test model for real integration testing -@Model({ - scope: 'global', - type: 'docstore' -}) -class RealTestUser extends BaseModel { - @Field({ type: 'string', required: true, unique: true }) - declare username: string; - - @Field({ type: 'string', required: true }) - declare email: string; - - @Field({ type: 'boolean', required: false, default: true }) - declare isActive: boolean; - - @Field({ type: 'number', required: false }) - declare createdAt: number; - - @BeforeCreate() - setCreatedAt() { - this.createdAt = Date.now(); - } -} - -@Model({ - scope: 'user', - type: 'docstore' -}) -class RealTestPost extends BaseModel { - @Field({ type: 'string', required: true }) - declare title: string; - - @Field({ type: 'string', required: true }) - declare content: string; - - @Field({ type: 'string', required: true }) - declare authorId: string; - - @Field({ type: 'number', required: false }) - declare createdAt: number; - - @BeforeCreate() - setCreatedAt() { - this.createdAt = Date.now(); - } -} - -describe('Real IPFS/OrbitDB Integration Tests', () => { - let network: RealTestNetwork; - let framework: DebrosFramework; - - beforeAll(async () => { - console.log('๐Ÿš€ Setting up real integration test environment...'); - - // Setup the real network with multiple nodes - network = await realTestHelpers.setupAll({ - nodeCount: 2, // Use 2 nodes for faster tests - timeout: 60000, - enableDebugLogs: true - }); - - // Create framework instance with real services - framework = new DebrosFramework(); - - const primaryNode = realTestHelpers.getManager().getPrimaryNode(); - await framework.initialize(primaryNode.orbitdb, primaryNode.ipfs); - - console.log('โœ… Real integration test environment ready'); - }, 90000); // 90 second timeout for setup - - afterAll(async () => { - console.log('๐Ÿงน Cleaning up real integration test environment...'); - - try { - if (framework) { - await framework.stop(); - } - } catch (error) { - console.warn('Warning: Error stopping framework:', error); - } - - await realTestHelpers.cleanupAll(); - console.log('โœ… Real integration test cleanup complete'); - }, 30000); // 30 second timeout for cleanup - - beforeEach(async () => { - // Wait for network to stabilize between tests - await realTestHelpers.getManager().waitForNetworkStabilization(1000); - }); - - describe('Framework Initialization', () => { - it('should initialize framework with real IPFS and OrbitDB services', async () => { - expect(framework).toBeDefined(); - expect(framework.getStatus().initialized).toBe(true); - - const health = await framework.healthCheck(); - expect(health.healthy).toBe(true); - expect(health.services.ipfs).toBe('connected'); - expect(health.services.orbitdb).toBe('connected'); - }); - - it('should have working database manager', async () => { - const databaseManager = framework.getDatabaseManager(); - expect(databaseManager).toBeDefined(); - - // Test database creation - const testDb = await databaseManager.getGlobalDatabase('test-db'); - expect(testDb).toBeDefined(); - }); - - it('should verify network connectivity', async () => { - const isConnected = await realTestHelpers.getManager().verifyNetworkConnectivity(); - expect(isConnected).toBe(true); - }); - }); - - describe('Real Model Operations', () => { - it('should create and save models to real IPFS/OrbitDB', async () => { - const user = await RealTestUser.create({ - username: 'real-test-user', - email: 'real@test.com' - }); - - expect(user).toBeInstanceOf(RealTestUser); - expect(user.id).toBeDefined(); - expect(user.username).toBe('real-test-user'); - expect(user.email).toBe('real@test.com'); - expect(user.isActive).toBe(true); - expect(user.createdAt).toBeGreaterThan(0); - }); - - it('should find models from real storage', async () => { - // Create a user - const originalUser = await RealTestUser.create({ - username: 'findable-user', - email: 'findable@test.com' - }); - - // Wait for data to be persisted - await new Promise(resolve => setTimeout(resolve, 1000)); - - // Find the user - const foundUser = await RealTestUser.findById(originalUser.id); - expect(foundUser).toBeInstanceOf(RealTestUser); - expect(foundUser?.id).toBe(originalUser.id); - expect(foundUser?.username).toBe('findable-user'); - }); - - it('should handle unique constraints with real storage', async () => { - // Create first user - await RealTestUser.create({ - username: 'unique-user', - email: 'unique1@test.com' - }); - - // Wait for persistence - await new Promise(resolve => setTimeout(resolve, 500)); - - // Try to create duplicate - await expect(RealTestUser.create({ - username: 'unique-user', // Duplicate username - email: 'unique2@test.com' - })).rejects.toThrow(); - }); - - it('should work with user-scoped models', async () => { - const post = await RealTestPost.create({ - title: 'Real Test Post', - content: 'This post is stored in real IPFS/OrbitDB', - authorId: 'test-author-123' - }); - - expect(post).toBeInstanceOf(RealTestPost); - expect(post.title).toBe('Real Test Post'); - expect(post.authorId).toBe('test-author-123'); - expect(post.createdAt).toBeGreaterThan(0); - }); - }); - - describe('Real Data Persistence', () => { - it('should persist data across framework restarts', async () => { - // Create data - const user = await RealTestUser.create({ - username: 'persistent-user', - email: 'persistent@test.com' - }); - - const userId = user.id; - - // Wait for persistence - await new Promise(resolve => setTimeout(resolve, 1000)); - - // Stop and restart framework (but keep the same IPFS/OrbitDB instances) - await framework.stop(); - - const primaryNode = realTestHelpers.getManager().getPrimaryNode(); - await framework.initialize(primaryNode.orbitdb, primaryNode.ipfs); - - // Try to find the user - const foundUser = await RealTestUser.findById(userId); - expect(foundUser).toBeInstanceOf(RealTestUser); - expect(foundUser?.username).toBe('persistent-user'); - }); - - it('should handle concurrent operations', async () => { - // Create multiple users concurrently - const userCreations = Array.from({ length: 5 }, (_, i) => - RealTestUser.create({ - username: `concurrent-user-${i}`, - email: `concurrent${i}@test.com` - }) - ); - - const users = await Promise.all(userCreations); - - expect(users).toHaveLength(5); - users.forEach((user, i) => { - expect(user.username).toBe(`concurrent-user-${i}`); - }); - - // Verify all users can be found - const foundUsers = await Promise.all( - users.map(user => RealTestUser.findById(user.id)) - ); - - foundUsers.forEach(user => { - expect(user).toBeInstanceOf(RealTestUser); - }); - }); - }); - - describe('Real Network Operations', () => { - it('should use real IPFS for content addressing', async () => { - const ipfsService = realTestHelpers.getManager().getPrimaryNode().ipfs; - const helia = ipfsService.getHelia(); - - expect(helia).toBeDefined(); - - // Test basic IPFS operations - const testData = new TextEncoder().encode('Hello, real IPFS!'); - const { cid } = await helia.blockstore.put(testData); - - expect(cid).toBeDefined(); - - const retrievedData = await helia.blockstore.get(cid); - expect(new TextDecoder().decode(retrievedData)).toBe('Hello, real IPFS!'); - }); - - it('should use real OrbitDB for distributed databases', async () => { - const orbitdbService = realTestHelpers.getManager().getPrimaryNode().orbitdb; - const orbitdb = orbitdbService.getOrbitDB(); - - expect(orbitdb).toBeDefined(); - expect(orbitdb.id).toBeDefined(); - - // Test basic OrbitDB operations - const testDb = await orbitdbService.openDB('real-test-db', 'documents'); - expect(testDb).toBeDefined(); - - const docId = await testDb.put({ message: 'Hello, real OrbitDB!' }); - expect(docId).toBeDefined(); - - const doc = await testDb.get(docId); - expect(doc.message).toBe('Hello, real OrbitDB!'); - }); - - it('should verify peer connections exist', async () => { - const nodes = realTestHelpers.getManager().getMultipleNodes(); - - // Each node should have connections to other nodes - for (const node of nodes) { - const peers = node.ipfs.getConnectedPeers(); - expect(peers.length).toBeGreaterThan(0); - } - }); - }); -}, 120000); // 2 minute timeout for the entire suite \ No newline at end of file diff --git a/tests/real/setup/helia-wrapper.ts b/tests/real/setup/helia-wrapper.ts deleted file mode 100644 index ad81800..0000000 --- a/tests/real/setup/helia-wrapper.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Manual wrapper for ES modules to work with Jest -// This file provides CommonJS-compatible interfaces for pure ES modules - -// Synchronous wrappers that use dynamic imports with await -export async function loadModules() { - const [ - heliaModule, - libp2pModule, - tcpModule, - noiseModule, - yamuxModule, - gossipsubModule, - identifyModule, - ] = await Promise.all([ - import('helia'), - import('libp2p'), - import('@libp2p/tcp'), - import('@chainsafe/libp2p-noise'), - import('@chainsafe/libp2p-yamux'), - import('@chainsafe/libp2p-gossipsub'), - import('@libp2p/identify'), - ]); - - return { - createHelia: heliaModule.createHelia, - createLibp2p: libp2pModule.createLibp2p, - tcp: tcpModule.tcp, - noise: noiseModule.noise, - yamux: yamuxModule.yamux, - gossipsub: gossipsubModule.gossipsub, - identify: identifyModule.identify, - }; -} - -// Separate async loader for OrbitDB -export async function loadOrbitDBModules() { - const orbitdbModule = await import('@orbitdb/core'); - - return { - createOrbitDB: orbitdbModule.createOrbitDB, - }; -} - -// Separate async loaders for datastore modules that might have different import patterns -export async function loadDatastoreModules() { - try { - const [blockstoreModule, datastoreModule] = await Promise.all([ - import('blockstore-fs'), - import('datastore-fs'), - ]); - - return { - FsBlockstore: blockstoreModule.FsBlockstore, - FsDatastore: datastoreModule.FsDatastore, - }; - } catch (_error) { - // Fallback to require() for modules that might not be pure ES modules - const FsBlockstore = require('blockstore-fs').FsBlockstore; - const FsDatastore = require('datastore-fs').FsDatastore; - - return { - FsBlockstore, - FsDatastore, - }; - } -} diff --git a/tests/real/setup/ipfs-setup.ts b/tests/real/setup/ipfs-setup.ts deleted file mode 100644 index 9cdb5a5..0000000 --- a/tests/real/setup/ipfs-setup.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { loadModules, loadDatastoreModules } from './helia-wrapper'; -import { join } from 'path'; -import { PrivateSwarmSetup } from './swarm-setup'; -import { IPFSInstance } from '../../../src/framework/services/OrbitDBService'; - -export class RealIPFSService implements IPFSInstance { - private helia: any; - private libp2p: any; - private nodeIndex: number; - private swarmSetup: PrivateSwarmSetup; - private dataDir: string; - - constructor(nodeIndex: number, swarmSetup: PrivateSwarmSetup) { - this.nodeIndex = nodeIndex; - this.swarmSetup = swarmSetup; - this.dataDir = swarmSetup.getNodeDataDir(nodeIndex); - } - - async init(): Promise { - console.log(`๐Ÿš€ Initializing IPFS node ${this.nodeIndex}...`); - - try { - // Load ES modules dynamically - const { createHelia, createLibp2p, tcp, noise, yamux, gossipsub, identify } = - await loadModules(); - const { FsBlockstore, FsDatastore } = await loadDatastoreModules(); - - // Create libp2p instance with private swarm configuration - this.libp2p = await createLibp2p({ - addresses: { - listen: [`/ip4/127.0.0.1/tcp/${this.swarmSetup.getNodePort(this.nodeIndex)}`], - }, - transports: [tcp()], - connectionEncrypters: [noise()], - streamMuxers: [yamux()], - services: { - identify: identify(), - pubsub: gossipsub({ - allowPublishToZeroTopicPeers: true, - canRelayMessage: true, - emitSelf: false, - }), - }, - connectionManager: { - maxConnections: 10, - dialTimeout: 10000, - inboundUpgradeTimeout: 10000, - }, - start: false, // Don't auto-start, we'll start manually - }); - - // Create blockstore and datastore - const blockstore = new FsBlockstore(join(this.dataDir, 'blocks')); - const datastore = new FsDatastore(join(this.dataDir, 'datastore')); - - // Create Helia instance - this.helia = await createHelia({ - libp2p: this.libp2p, - blockstore, - datastore, - start: false, - }); - - // Start the node - await this.helia.start(); - - console.log( - `โœ… IPFS node ${this.nodeIndex} started with Peer ID: ${this.libp2p.peerId.toString()}`, - ); - console.log( - `๐Ÿ“ก Listening on: ${this.libp2p - .getMultiaddrs() - .map((ma) => ma.toString()) - .join(', ')}`, - ); - - return this.helia; - } catch (error) { - console.error(`โŒ Failed to initialize IPFS node ${this.nodeIndex}:`, error); - throw error; - } - } - - async connectToPeers(peerNodes: RealIPFSService[]): Promise { - if (!this.libp2p) { - throw new Error('IPFS node not initialized'); - } - - for (const peerNode of peerNodes) { - if (peerNode.nodeIndex === this.nodeIndex) continue; // Don't connect to self - - try { - const peerAddrs = peerNode.getMultiaddrs(); - - for (const addr of peerAddrs) { - try { - console.log( - `๐Ÿ”— Node ${this.nodeIndex} connecting to node ${peerNode.nodeIndex} at ${addr}`, - ); - await this.libp2p.dial(addr); - console.log(`โœ… Node ${this.nodeIndex} connected to node ${peerNode.nodeIndex}`); - break; // Successfully connected, no need to try other addresses - } catch (dialError) { - console.log(`โš ๏ธ Failed to dial ${addr}: ${dialError.message}`); - } - } - } catch (error) { - console.warn( - `โš ๏ธ Could not connect node ${this.nodeIndex} to node ${peerNode.nodeIndex}:`, - error.message, - ); - } - } - } - - getMultiaddrs(): string[] { - if (!this.libp2p) return []; - return this.libp2p.getMultiaddrs().map((ma: any) => ma.toString()); - } - - getPeerId(): string { - if (!this.libp2p) return ''; - return this.libp2p.peerId.toString(); - } - - getConnectedPeers(): string[] { - if (!this.libp2p) return []; - return this.libp2p.getPeers().map((peer: any) => peer.toString()); - } - - async stop(): Promise { - console.log(`๐Ÿ›‘ Stopping IPFS node ${this.nodeIndex}...`); - - try { - if (this.helia) { - await this.helia.stop(); - console.log(`โœ… IPFS node ${this.nodeIndex} stopped`); - } - } catch (error) { - console.error(`โŒ Error stopping IPFS node ${this.nodeIndex}:`, error); - throw error; - } - } - - getHelia(): any { - return this.helia; - } - - getLibp2pInstance(): any { - return this.libp2p; - } - - // Framework interface compatibility - get pubsub() { - if (!this.libp2p?.services?.pubsub) { - throw new Error('PubSub service not available'); - } - - return { - publish: async (topic: string, data: string) => { - const encoder = new TextEncoder(); - await this.libp2p.services.pubsub.publish(topic, encoder.encode(data)); - }, - subscribe: async (topic: string, handler: (message: any) => void) => { - this.libp2p.services.pubsub.addEventListener('message', (evt: any) => { - if (evt.detail.topic === topic) { - const decoder = new TextDecoder(); - const message = { - topic: evt.detail.topic, - data: decoder.decode(evt.detail.data), - from: evt.detail.from.toString(), - }; - handler(message); - } - }); - this.libp2p.services.pubsub.subscribe(topic); - }, - unsubscribe: async (topic: string) => { - this.libp2p.services.pubsub.unsubscribe(topic); - }, - }; - } -} - -// Utility function to create multiple IPFS nodes in a private network -export async function createIPFSNetwork(nodeCount: number = 3): Promise<{ - nodes: RealIPFSService[]; - swarmSetup: PrivateSwarmSetup; -}> { - console.log(`๐ŸŒ Creating private IPFS network with ${nodeCount} nodes...`); - - const swarmSetup = new PrivateSwarmSetup(nodeCount); - const nodes: RealIPFSService[] = []; - - // Create all nodes - for (let i = 0; i < nodeCount; i++) { - const node = new RealIPFSService(i, swarmSetup); - nodes.push(node); - } - - // Initialize all nodes - for (const node of nodes) { - await node.init(); - } - - // Wait a moment for nodes to be ready - await new Promise((resolve) => setTimeout(resolve, 1000)); - - // Connect nodes in a mesh topology - for (let i = 0; i < nodes.length; i++) { - const currentNode = nodes[i]; - const otherNodes = nodes.filter((_, index) => index !== i); - await currentNode.connectToPeers(otherNodes); - } - - // Wait for connections to establish - await new Promise((resolve) => setTimeout(resolve, 2000)); - - // Report network status - console.log(`๐Ÿ“Š Private IPFS Network Status:`); - for (const node of nodes) { - const peers = node.getConnectedPeers(); - console.log(` Node ${node.nodeIndex}: ${peers.length} peers connected`); - } - - return { nodes, swarmSetup }; -} - -export async function shutdownIPFSNetwork( - nodes: RealIPFSService[], - swarmSetup: PrivateSwarmSetup, -): Promise { - console.log(`๐Ÿ›‘ Shutting down IPFS network...`); - - // Stop all nodes - await Promise.all(nodes.map((node) => node.stop())); - - // Cleanup test data - swarmSetup.cleanup(); - - console.log(`โœ… IPFS network shutdown complete`); -} diff --git a/tests/real/setup/orbitdb-setup.ts b/tests/real/setup/orbitdb-setup.ts deleted file mode 100644 index 88c7d99..0000000 --- a/tests/real/setup/orbitdb-setup.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { loadOrbitDBModules } from './helia-wrapper'; -import { RealIPFSService } from './ipfs-setup'; -import { OrbitDBInstance } from '../../../src/framework/services/OrbitDBService'; - -export class RealOrbitDBService implements OrbitDBInstance { - private orbitdb: any; - private ipfsService: RealIPFSService; - private nodeIndex: number; - private databases: Map = new Map(); - - constructor(nodeIndex: number, ipfsService: RealIPFSService) { - this.nodeIndex = nodeIndex; - this.ipfsService = ipfsService; - } - - async init(): Promise { - console.log(`๐ŸŒ€ Initializing OrbitDB for node ${this.nodeIndex}...`); - - try { - // Load OrbitDB ES modules dynamically - const { createOrbitDB } = await loadOrbitDBModules(); - - const ipfs = this.ipfsService.getHelia(); - if (!ipfs) { - throw new Error('IPFS node must be initialized before OrbitDB'); - } - - // Create OrbitDB instance - this.orbitdb = await createOrbitDB({ - ipfs, - id: `orbitdb-node-${this.nodeIndex}`, - directory: `./orbitdb-${this.nodeIndex}`, // Local directory for this node - }); - - console.log(`โœ… OrbitDB initialized for node ${this.nodeIndex}`); - console.log(`๐Ÿ“ OrbitDB ID: ${this.orbitdb.id}`); - - return this.orbitdb; - } catch (error) { - console.error(`โŒ Failed to initialize OrbitDB for node ${this.nodeIndex}:`, error); - throw error; - } - } - - async openDB(name: string, type: string): Promise { - if (!this.orbitdb) { - throw new Error('OrbitDB not initialized'); - } - - const dbKey = `${name}-${type}`; - - // Check if database is already open - if (this.databases.has(dbKey)) { - return this.databases.get(dbKey); - } - - try { - console.log(`๐Ÿ“‚ Opening ${type} database '${name}' on node ${this.nodeIndex}...`); - - let database; - - switch (type.toLowerCase()) { - case 'documents': - case 'docstore': - database = await this.orbitdb.open(name, { - type: 'documents', - AccessController: 'orbitdb', - }); - break; - - case 'events': - case 'eventlog': - database = await this.orbitdb.open(name, { - type: 'events', - AccessController: 'orbitdb', - }); - break; - - case 'keyvalue': - case 'kvstore': - database = await this.orbitdb.open(name, { - type: 'keyvalue', - AccessController: 'orbitdb', - }); - break; - - default: - // Default to documents store - database = await this.orbitdb.open(name, { - type: 'documents', - AccessController: 'orbitdb', - }); - } - - this.databases.set(dbKey, database); - - console.log(`โœ… Database '${name}' opened on node ${this.nodeIndex}`); - console.log(`๐Ÿ”— Database address: ${database.address}`); - - return database; - } catch (error) { - console.error(`โŒ Failed to open database '${name}' on node ${this.nodeIndex}:`, error); - throw error; - } - } - - async stop(): Promise { - console.log(`๐Ÿ›‘ Stopping OrbitDB for node ${this.nodeIndex}...`); - - try { - // Close all open databases - for (const [name, database] of this.databases) { - try { - await database.close(); - console.log(`๐Ÿ“‚ Closed database '${name}' on node ${this.nodeIndex}`); - } catch (error) { - console.warn(`โš ๏ธ Error closing database '${name}':`, error); - } - } - this.databases.clear(); - - // Stop OrbitDB - if (this.orbitdb) { - await this.orbitdb.stop(); - console.log(`โœ… OrbitDB stopped for node ${this.nodeIndex}`); - } - } catch (error) { - console.error(`โŒ Error stopping OrbitDB for node ${this.nodeIndex}:`, error); - throw error; - } - } - - getOrbitDB(): any { - return this.orbitdb; - } - - // Additional utility methods for testing - async waitForReplication(database: any, timeout: number = 30000): Promise { - const startTime = Date.now(); - - return new Promise((resolve) => { - const checkReplication = () => { - if (Date.now() - startTime > timeout) { - resolve(false); - return; - } - - // Check if database has received updates from other peers - const peers = database.peers || []; - if (peers.length > 0) { - resolve(true); - return; - } - - setTimeout(checkReplication, 100); - }; - - checkReplication(); - }); - } - - async getDatabaseInfo(name: string, type: string): Promise { - const dbKey = `${name}-${type}`; - const database = this.databases.get(dbKey); - - if (!database) { - return null; - } - - return { - address: database.address, - type: database.type, - peers: database.peers || [], - all: await database.all(), - meta: database.meta || {}, - }; - } -} - -// Utility function to create OrbitDB network from IPFS network -export async function createOrbitDBNetwork( - ipfsNodes: RealIPFSService[], -): Promise { - console.log(`๐ŸŒ€ Creating OrbitDB network with ${ipfsNodes.length} nodes...`); - - const orbitdbNodes: RealOrbitDBService[] = []; - - // Create OrbitDB instances for each IPFS node - for (let i = 0; i < ipfsNodes.length; i++) { - const orbitdbService = new RealOrbitDBService(i, ipfsNodes[i]); - await orbitdbService.init(); - orbitdbNodes.push(orbitdbService); - } - - console.log(`โœ… OrbitDB network created with ${orbitdbNodes.length} nodes`); - return orbitdbNodes; -} - -export async function shutdownOrbitDBNetwork(orbitdbNodes: RealOrbitDBService[]): Promise { - console.log(`๐Ÿ›‘ Shutting down OrbitDB network...`); - - // Stop all OrbitDB nodes - await Promise.all(orbitdbNodes.map((node) => node.stop())); - - console.log(`โœ… OrbitDB network shutdown complete`); -} - -// Test utilities for database operations -export async function testDatabaseReplication( - orbitdbNodes: RealOrbitDBService[], - dbName: string, - dbType: string = 'documents', -): Promise { - console.log(`๐Ÿ”„ Testing database replication for '${dbName}'...`); - - if (orbitdbNodes.length < 2) { - console.log(`โš ๏ธ Need at least 2 nodes for replication test`); - return false; - } - - try { - // Open database on first node and add data - const db1 = await orbitdbNodes[0].openDB(dbName, dbType); - await db1.put({ _id: 'test-doc-1', content: 'Hello from node 0', timestamp: Date.now() }); - - // Open same database on second node - const db2 = await orbitdbNodes[1].openDB(dbName, dbType); - - // Wait for replication - await new Promise((resolve) => setTimeout(resolve, 2000)); - - // Check if data replicated - const db2Data = await db2.all(); - const hasReplicatedData = db2Data.some((doc: any) => doc._id === 'test-doc-1'); - - if (hasReplicatedData) { - console.log(`โœ… Database replication successful for '${dbName}'`); - return true; - } else { - console.log(`โŒ Database replication failed for '${dbName}'`); - return false; - } - } catch (error) { - console.error(`โŒ Error testing database replication:`, error); - return false; - } -} diff --git a/tests/real/setup/swarm-setup.ts b/tests/real/setup/swarm-setup.ts deleted file mode 100644 index 30800f3..0000000 --- a/tests/real/setup/swarm-setup.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { randomBytes } from 'crypto'; -import { writeFileSync, mkdirSync, rmSync, existsSync } from 'fs'; -import { join } from 'path'; -import { tmpdir } from 'os'; - -export interface SwarmConfig { - swarmKey: string; - nodeCount: number; - basePort: number; - dataDir: string; - bootstrapAddrs: string[]; -} - -export class PrivateSwarmSetup { - private config: SwarmConfig; - private swarmKeyPath: string; - - constructor(nodeCount: number = 3) { - const testId = Date.now().toString(36); - const basePort = 40000 + Math.floor(Math.random() * 10000); - - this.config = { - swarmKey: this.generateSwarmKey(), - nodeCount, - basePort, - dataDir: join(tmpdir(), `debros-test-${testId}`), - bootstrapAddrs: [] - }; - - this.swarmKeyPath = join(this.config.dataDir, 'swarm.key'); - this.setupSwarmKey(); - this.generateBootstrapAddrs(); - } - - private generateSwarmKey(): string { - // Generate a private swarm key (64 bytes of random data) - const key = randomBytes(32).toString('hex'); - return `/key/swarm/psk/1.0.0/\n/base16/\n${key}`; - } - - private setupSwarmKey(): void { - // Create data directory - mkdirSync(this.config.dataDir, { recursive: true }); - - // Write swarm key file - writeFileSync(this.swarmKeyPath, this.config.swarmKey); - } - - private generateBootstrapAddrs(): void { - // Generate bootstrap addresses for private network - // First node will be the bootstrap node - const bootstrapPort = this.config.basePort; - this.config.bootstrapAddrs = [ - `/ip4/127.0.0.1/tcp/${bootstrapPort}/p2p/12D3KooWBootstrapNodeId` // Placeholder - will be replaced with actual peer ID - ]; - } - - getConfig(): SwarmConfig { - return { ...this.config }; - } - - getNodeDataDir(nodeIndex: number): string { - const nodeDir = join(this.config.dataDir, `node-${nodeIndex}`); - mkdirSync(nodeDir, { recursive: true }); - return nodeDir; - } - - getNodePort(nodeIndex: number): number { - return this.config.basePort + nodeIndex; - } - - getSwarmKeyPath(): string { - return this.swarmKeyPath; - } - - cleanup(): void { - try { - if (existsSync(this.config.dataDir)) { - rmSync(this.config.dataDir, { recursive: true, force: true }); - console.log(`๐Ÿงน Cleaned up test data directory: ${this.config.dataDir}`); - } - } catch (error) { - console.warn(`Warning: Could not cleanup test directory: ${error}`); - } - } - - // Get libp2p configuration for a node - getLibp2pConfig(nodeIndex: number, isBootstrap: boolean = false) { - const port = this.getNodePort(nodeIndex); - - return { - addresses: { - listen: [`/ip4/127.0.0.1/tcp/${port}`] - }, - connectionManager: { - minConnections: 1, - maxConnections: 10, - dialTimeout: 30000 - }, - // For private networks, we'll configure bootstrap after peer IDs are known - bootstrap: isBootstrap ? [] : [], // Will be populated with actual bootstrap addresses - datastore: undefined, // Will be set by the node setup - keychain: { - pass: 'test-passphrase' - } - }; - } -} - -// Test utilities -export async function waitForPeerConnections( - nodes: any[], - expectedConnections: number, - timeout: number = 30000 -): Promise { - const startTime = Date.now(); - - while (Date.now() - startTime < timeout) { - let allConnected = true; - - for (const node of nodes) { - const peers = node.libp2p.getPeers(); - if (peers.length < expectedConnections) { - allConnected = false; - break; - } - } - - if (allConnected) { - console.log(`โœ… All nodes connected with ${expectedConnections} peers each`); - return true; - } - - // Wait 100ms before checking again - await new Promise(resolve => setTimeout(resolve, 100)); - } - - console.log(`โš ๏ธ Timeout waiting for peer connections after ${timeout}ms`); - return false; -} - -export async function waitForNetworkReady(nodes: any[], timeout: number = 30000): Promise { - // Wait for at least one connection between any nodes - const startTime = Date.now(); - - while (Date.now() - startTime < timeout) { - let hasConnections = false; - - for (const node of nodes) { - const peers = node.libp2p.getPeers(); - if (peers.length > 0) { - hasConnections = true; - break; - } - } - - if (hasConnections) { - console.log(`๐ŸŒ Private network is ready with ${nodes.length} nodes`); - return true; - } - - await new Promise(resolve => setTimeout(resolve, 100)); - } - - console.log(`โš ๏ธ Timeout waiting for network to be ready after ${timeout}ms`); - return false; -} \ No newline at end of file diff --git a/tests/real/setup/test-lifecycle.ts b/tests/real/setup/test-lifecycle.ts deleted file mode 100644 index 5e5a726..0000000 --- a/tests/real/setup/test-lifecycle.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { RealIPFSService, createIPFSNetwork, shutdownIPFSNetwork } from './ipfs-setup'; -import { RealOrbitDBService, createOrbitDBNetwork, shutdownOrbitDBNetwork } from './orbitdb-setup'; -import { PrivateSwarmSetup, waitForNetworkReady } from './swarm-setup'; - -export interface RealTestNetwork { - ipfsNodes: RealIPFSService[]; - orbitdbNodes: RealOrbitDBService[]; - swarmSetup: PrivateSwarmSetup; -} - -export interface RealTestConfig { - nodeCount: number; - timeout: number; - enableDebugLogs: boolean; -} - -export class RealTestManager { - private network: RealTestNetwork | null = null; - private config: RealTestConfig; - - constructor(config: Partial = {}) { - this.config = { - nodeCount: 3, - timeout: 60000, // 60 seconds - enableDebugLogs: false, - ...config - }; - } - - async setup(): Promise { - console.log(`๐Ÿš€ Setting up real test network with ${this.config.nodeCount} nodes...`); - - try { - // Create IPFS network - const { nodes: ipfsNodes, swarmSetup } = await createIPFSNetwork(this.config.nodeCount); - - // Wait for network to be ready - const networkReady = await waitForNetworkReady(ipfsNodes.map(n => n.getHelia()), this.config.timeout); - if (!networkReady) { - throw new Error('Network failed to become ready within timeout'); - } - - // Create OrbitDB network - const orbitdbNodes = await createOrbitDBNetwork(ipfsNodes); - - this.network = { - ipfsNodes, - orbitdbNodes, - swarmSetup - }; - - console.log(`โœ… Real test network setup complete`); - this.logNetworkStatus(); - - return this.network; - } catch (error) { - console.error(`โŒ Failed to setup real test network:`, error); - await this.cleanup(); - throw error; - } - } - - async cleanup(): Promise { - if (!this.network) { - return; - } - - console.log(`๐Ÿงน Cleaning up real test network...`); - - try { - // Shutdown OrbitDB network first - await shutdownOrbitDBNetwork(this.network.orbitdbNodes); - - // Shutdown IPFS network - await shutdownIPFSNetwork(this.network.ipfsNodes, this.network.swarmSetup); - - this.network = null; - console.log(`โœ… Real test network cleanup complete`); - } catch (error) { - console.error(`โŒ Error during cleanup:`, error); - // Continue with cleanup even if there are errors - } - } - - getNetwork(): RealTestNetwork { - if (!this.network) { - throw new Error('Network not initialized. Call setup() first.'); - } - return this.network; - } - - // Get a single node for simple tests - getPrimaryNode(): { ipfs: RealIPFSService; orbitdb: RealOrbitDBService } { - const network = this.getNetwork(); - return { - ipfs: network.ipfsNodes[0], - orbitdb: network.orbitdbNodes[0] - }; - } - - // Get multiple nodes for P2P tests - getMultipleNodes(count?: number): Array<{ ipfs: RealIPFSService; orbitdb: RealOrbitDBService }> { - const network = this.getNetwork(); - const nodeCount = count || network.ipfsNodes.length; - - return Array.from({ length: Math.min(nodeCount, network.ipfsNodes.length) }, (_, i) => ({ - ipfs: network.ipfsNodes[i], - orbitdb: network.orbitdbNodes[i] - })); - } - - private logNetworkStatus(): void { - if (!this.network || !this.config.enableDebugLogs) { - return; - } - - console.log(`๐Ÿ“Š Network Status:`); - console.log(` Nodes: ${this.network.ipfsNodes.length}`); - - for (let i = 0; i < this.network.ipfsNodes.length; i++) { - const ipfsNode = this.network.ipfsNodes[i]; - const peers = ipfsNode.getConnectedPeers(); - console.log(` Node ${i}:`); - console.log(` Peer ID: ${ipfsNode.getPeerId()}`); - console.log(` Connected Peers: ${peers.length}`); - console.log(` Addresses: ${ipfsNode.getMultiaddrs().join(', ')}`); - } - } - - // Test utilities - async waitForNetworkStabilization(timeout: number = 10000): Promise { - console.log(`โณ Waiting for network stabilization...`); - - // Wait for connections to stabilize - await new Promise(resolve => setTimeout(resolve, timeout)); - - if (this.config.enableDebugLogs) { - this.logNetworkStatus(); - } - } - - async verifyNetworkConnectivity(): Promise { - const network = this.getNetwork(); - - // Check if all nodes have at least one connection - for (const node of network.ipfsNodes) { - const peers = node.getConnectedPeers(); - if (peers.length === 0) { - console.log(`โŒ Node ${node.nodeIndex} has no peer connections`); - return false; - } - } - - console.log(`โœ… All nodes have peer connections`); - return true; - } -} - -// Global test manager for Jest lifecycle -let globalTestManager: RealTestManager | null = null; - -export async function setupGlobalTestNetwork(config: Partial = {}): Promise { - if (globalTestManager) { - throw new Error('Global test network already setup. Call cleanupGlobalTestNetwork() first.'); - } - - globalTestManager = new RealTestManager(config); - return await globalTestManager.setup(); -} - -export async function cleanupGlobalTestNetwork(): Promise { - if (globalTestManager) { - await globalTestManager.cleanup(); - globalTestManager = null; - } -} - -export function getGlobalTestNetwork(): RealTestNetwork { - if (!globalTestManager) { - throw new Error('Global test network not setup. Call setupGlobalTestNetwork() first.'); - } - return globalTestManager.getNetwork(); -} - -export function getGlobalTestManager(): RealTestManager { - if (!globalTestManager) { - throw new Error('Global test manager not setup. Call setupGlobalTestNetwork() first.'); - } - return globalTestManager; -} - -// Jest helper functions -export const realTestHelpers = { - setupAll: setupGlobalTestNetwork, - cleanupAll: cleanupGlobalTestNetwork, - getNetwork: getGlobalTestNetwork, - getManager: getGlobalTestManager -}; \ No newline at end of file