diff --git a/debug_fields.js b/debug_fields.js new file mode 100644 index 0000000..f92f1b1 --- /dev/null +++ b/debug_fields.js @@ -0,0 +1,44 @@ +// Simple debug script to test field defaults +const { execSync } = require('child_process'); + +// Run a small test using jest directly +const testCode = ` +import { BaseModel } from './src/framework/models/BaseModel'; +import { Model, Field } from './src/framework/models/decorators'; + +@Model({ + scope: 'global', + type: 'docstore' +}) +class TestUser extends BaseModel { + @Field({ type: 'string', required: true }) + username: string; + + @Field({ type: 'number', required: false, default: 0 }) + score: number; + + @Field({ type: 'boolean', required: false, default: true }) + isActive: boolean; +} + +// Debug the fields +console.log('TestUser.fields:', TestUser.fields); +console.log('TestUser.fields size:', TestUser.fields?.size); + +if (TestUser.fields) { + for (const [fieldName, fieldConfig] of TestUser.fields) { + console.log(\`Field: \${fieldName}, Config:\`, fieldConfig); + } +} + +// Test instance creation +const user = new TestUser(); +console.log('User instance score:', user.score); +console.log('User instance isActive:', user.isActive); + +// Check private fields +console.log('User _score:', (user as any)._score); +console.log('User _isActive:', (user as any)._isActive); +`; + +console.log('Test code created for debugging...'); \ No newline at end of file diff --git a/jest.real.config.cjs b/jest.real.config.cjs index b1362ac..1709077 100644 --- a/jest.real.config.cjs +++ b/jest.real.config.cjs @@ -1,52 +1,62 @@ module.exports = { - preset: 'ts-jest', + preset: 'ts-jest/presets/default-esm', testEnvironment: 'node', roots: ['/tests/real'], testMatch: ['**/real/**/*.test.ts'], + + // ES Module configuration + extensionsToTreatAsEsm: ['.ts'], + transform: { '^.+\\.ts$': [ 'ts-jest', { - isolatedModules: true, + useESM: true, }, ], }, collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts', '!src/**/index.ts', '!src/examples/**'], coverageDirectory: 'coverage-real', coverageReporters: ['text', 'lcov', 'html'], - + // Extended timeouts for real network operations testTimeout: 180000, // 3 minutes per test - + // Run tests serially to avoid port conflicts and resource contention maxWorkers: 1, - + // Setup and teardown globalSetup: '/tests/real/jest.global-setup.cjs', globalTeardown: '/tests/real/jest.global-teardown.cjs', - + // Environment variables for real tests setupFilesAfterEnv: ['/tests/real/jest.setup.ts'], - - // Longer timeout for setup/teardown - setupFilesTimeout: 120000, - + // Disable watch mode (real tests are too slow) watchman: false, - + // Clear mocks between tests clearMocks: true, restoreMocks: true, - + // Verbose output for debugging verbose: true, - + // Fail fast on first error (saves time with slow tests) bail: 1, - - // Module path mapping - moduleNameMapping: { - '^@/(.*)$': '/src/$1', - '^@tests/(.*)$': '/tests/$1' - } -}; \ No newline at end of file + + // ES Module support + extensionsToTreatAsEsm: ['.ts'], + + // Transform ES modules - more comprehensive pattern + transformIgnorePatterns: [ + 'node_modules/(?!(helia|@helia|@orbitdb|@libp2p|@chainsafe|@multiformats|multiformats|datastore-fs|blockstore-fs|libp2p)/)', + ], + + // Module resolution for ES modules + resolver: undefined, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], // Module name mapping to handle ES modules + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, +}; diff --git a/package.json b/package.json index 33cc426..32435b5 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "test:e2e": "jest tests/e2e", "test:real": "jest --config jest.real.config.cjs", "test:real:debug": "REAL_TEST_DEBUG=true jest --config jest.real.config.cjs", - "test:real:basic": "jest --config jest.real.config.cjs tests/real/basic-integration.test.ts", + "test:real:basic": "jest --config jest.real.config.cjs tests/real/real-integration.test.ts", "test:real:p2p": "jest --config jest.real.config.cjs tests/real/peer-discovery.test.ts" }, "keywords": [ diff --git a/tests/real/jest.global-setup.cjs b/tests/real/jest.global-setup.cjs index d67cc3d..e94ba37 100644 --- a/tests/real/jest.global-setup.cjs +++ b/tests/real/jest.global-setup.cjs @@ -6,11 +6,20 @@ module.exports = async () => { process.env.NODE_ENV = 'test'; process.env.DEBROS_TEST_MODE = 'real'; - // Check for required dependencies + // Check for required dependencies - skip for ES module packages try { - require('helia'); - require('@orbitdb/core'); - console.log('✅ Required dependencies available'); + // 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); diff --git a/tests/real/jest.setup.ts b/tests/real/jest.setup.ts index 315a0c5..f84fc9a 100644 --- a/tests/real/jest.setup.ts +++ b/tests/real/jest.setup.ts @@ -11,8 +11,18 @@ const debugMode = process.env.REAL_TEST_DEBUG === 'true'; if (!debugMode) { // Silence routine logs but keep errors and important messages console.log = (...args: any[]) => { - const message = args.join(' '); - if (message.includes('❌') || message.includes('✅') || message.includes('🚀') || message.includes('🧹')) { + 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); } }; diff --git a/tests/real/setup/helia-wrapper.ts b/tests/real/setup/helia-wrapper.ts new file mode 100644 index 0000000..ad81800 --- /dev/null +++ b/tests/real/setup/helia-wrapper.ts @@ -0,0 +1,66 @@ +// 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 index f0fa3d8..9cdb5a5 100644 --- a/tests/real/setup/ipfs-setup.ts +++ b/tests/real/setup/ipfs-setup.ts @@ -1,12 +1,4 @@ -import { createHelia } from 'helia'; -import { createLibp2p } from 'libp2p'; -import { tcp } from '@libp2p/tcp'; -import { noise } from '@chainsafe/libp2p-noise'; -import { yamux } from '@chainsafe/libp2p-yamux'; -import { gossipsub } from '@chainsafe/libp2p-gossipsub'; -import { identify } from '@libp2p/identify'; -import { FsBlockstore } from 'blockstore-fs'; -import { FsDatastore } from 'datastore-fs'; +import { loadModules, loadDatastoreModules } from './helia-wrapper'; import { join } from 'path'; import { PrivateSwarmSetup } from './swarm-setup'; import { IPFSInstance } from '../../../src/framework/services/OrbitDBService'; @@ -28,6 +20,11 @@ export class RealIPFSService implements IPFSInstance { 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: { diff --git a/tests/real/setup/orbitdb-setup.ts b/tests/real/setup/orbitdb-setup.ts index 821d7ab..88c7d99 100644 --- a/tests/real/setup/orbitdb-setup.ts +++ b/tests/real/setup/orbitdb-setup.ts @@ -1,4 +1,4 @@ -import { createOrbitDB } from '@orbitdb/core'; +import { loadOrbitDBModules } from './helia-wrapper'; import { RealIPFSService } from './ipfs-setup'; import { OrbitDBInstance } from '../../../src/framework/services/OrbitDBService'; @@ -17,21 +17,24 @@ export class RealOrbitDBService implements OrbitDBInstance { 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({ + this.orbitdb = await createOrbitDB({ ipfs, id: `orbitdb-node-${this.nodeIndex}`, - directory: `./orbitdb-${this.nodeIndex}` // Local directory for this node + 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); @@ -45,7 +48,7 @@ export class RealOrbitDBService implements OrbitDBInstance { } const dbKey = `${name}-${type}`; - + // Check if database is already open if (this.databases.has(dbKey)) { return this.databases.get(dbKey); @@ -55,42 +58,42 @@ export class RealOrbitDBService implements OrbitDBInstance { 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' + AccessController: 'orbitdb', }); break; - + case 'events': case 'eventlog': database = await this.orbitdb.open(name, { type: 'events', - AccessController: 'orbitdb' + AccessController: 'orbitdb', }); break; - + case 'keyvalue': case 'kvstore': database = await this.orbitdb.open(name, { type: 'keyvalue', - AccessController: 'orbitdb' + AccessController: 'orbitdb', }); break; - + default: // Default to documents store database = await this.orbitdb.open(name, { type: 'documents', - AccessController: 'orbitdb' + AccessController: 'orbitdb', }); } this.databases.set(dbKey, database); - + console.log(`✅ Database '${name}' opened on node ${this.nodeIndex}`); console.log(`🔗 Database address: ${database.address}`); @@ -134,7 +137,7 @@ export class RealOrbitDBService implements OrbitDBInstance { // 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) { @@ -159,7 +162,7 @@ export class RealOrbitDBService implements OrbitDBInstance { async getDatabaseInfo(name: string, type: string): Promise { const dbKey = `${name}-${type}`; const database = this.databases.get(dbKey); - + if (!database) { return null; } @@ -169,13 +172,15 @@ export class RealOrbitDBService implements OrbitDBInstance { type: database.type, peers: database.peers || [], all: await database.all(), - meta: database.meta || {} + meta: database.meta || {}, }; } } // Utility function to create OrbitDB network from IPFS network -export async function createOrbitDBNetwork(ipfsNodes: RealIPFSService[]): Promise { +export async function createOrbitDBNetwork( + ipfsNodes: RealIPFSService[], +): Promise { console.log(`🌀 Creating OrbitDB network with ${ipfsNodes.length} nodes...`); const orbitdbNodes: RealOrbitDBService[] = []; @@ -195,7 +200,7 @@ export async function shutdownOrbitDBNetwork(orbitdbNodes: RealOrbitDBService[]) console.log(`🛑 Shutting down OrbitDB network...`); // Stop all OrbitDB nodes - await Promise.all(orbitdbNodes.map(node => node.stop())); + await Promise.all(orbitdbNodes.map((node) => node.stop())); console.log(`✅ OrbitDB network shutdown complete`); } @@ -204,7 +209,7 @@ export async function shutdownOrbitDBNetwork(orbitdbNodes: RealOrbitDBService[]) export async function testDatabaseReplication( orbitdbNodes: RealOrbitDBService[], dbName: string, - dbType: string = 'documents' + dbType: string = 'documents', ): Promise { console.log(`🔄 Testing database replication for '${dbName}'...`); @@ -220,9 +225,9 @@ export async function testDatabaseReplication( // Open same database on second node const db2 = await orbitdbNodes[1].openDB(dbName, dbType); - + // Wait for replication - await new Promise(resolve => setTimeout(resolve, 2000)); + await new Promise((resolve) => setTimeout(resolve, 2000)); // Check if data replicated const db2Data = await db2.all(); @@ -239,4 +244,4 @@ export async function testDatabaseReplication( console.error(`❌ Error testing database replication:`, error); return false; } -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 635c155..f68923d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,8 @@ "moduleResolution": "bundler", /* Skip type checking of declaration files. */ "skipLibCheck": true, + /* Perform compilation without referencing other files. */ + "isolatedModules": true, /* Removes comments from the project's output JavaScript code. */ "removeComments": true, /* Enables experimental support for emitting type metadata for decorators which works with the module reflect-metadata. */