feat: Add debug script for field defaults and enhance Jest configuration for ES modules

This commit is contained in:
anonpenguin 2025-06-19 21:47:35 +03:00
parent 1e3c5d46be
commit 831c977eda
9 changed files with 202 additions and 59 deletions

44
debug_fields.js Normal file
View File

@ -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...');

View File

@ -1,52 +1,62 @@
module.exports = { module.exports = {
preset: 'ts-jest', preset: 'ts-jest/presets/default-esm',
testEnvironment: 'node', testEnvironment: 'node',
roots: ['<rootDir>/tests/real'], roots: ['<rootDir>/tests/real'],
testMatch: ['**/real/**/*.test.ts'], testMatch: ['**/real/**/*.test.ts'],
// ES Module configuration
extensionsToTreatAsEsm: ['.ts'],
transform: { transform: {
'^.+\\.ts$': [ '^.+\\.ts$': [
'ts-jest', 'ts-jest',
{ {
isolatedModules: true, useESM: true,
}, },
], ],
}, },
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts', '!src/**/index.ts', '!src/examples/**'], collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts', '!src/**/index.ts', '!src/examples/**'],
coverageDirectory: 'coverage-real', coverageDirectory: 'coverage-real',
coverageReporters: ['text', 'lcov', 'html'], coverageReporters: ['text', 'lcov', 'html'],
// Extended timeouts for real network operations // Extended timeouts for real network operations
testTimeout: 180000, // 3 minutes per test testTimeout: 180000, // 3 minutes per test
// Run tests serially to avoid port conflicts and resource contention // Run tests serially to avoid port conflicts and resource contention
maxWorkers: 1, maxWorkers: 1,
// Setup and teardown // Setup and teardown
globalSetup: '<rootDir>/tests/real/jest.global-setup.cjs', globalSetup: '<rootDir>/tests/real/jest.global-setup.cjs',
globalTeardown: '<rootDir>/tests/real/jest.global-teardown.cjs', globalTeardown: '<rootDir>/tests/real/jest.global-teardown.cjs',
// Environment variables for real tests // Environment variables for real tests
setupFilesAfterEnv: ['<rootDir>/tests/real/jest.setup.ts'], setupFilesAfterEnv: ['<rootDir>/tests/real/jest.setup.ts'],
// Longer timeout for setup/teardown
setupFilesTimeout: 120000,
// Disable watch mode (real tests are too slow) // Disable watch mode (real tests are too slow)
watchman: false, watchman: false,
// Clear mocks between tests // Clear mocks between tests
clearMocks: true, clearMocks: true,
restoreMocks: true, restoreMocks: true,
// Verbose output for debugging // Verbose output for debugging
verbose: true, verbose: true,
// Fail fast on first error (saves time with slow tests) // Fail fast on first error (saves time with slow tests)
bail: 1, bail: 1,
// Module path mapping // ES Module support
moduleNameMapping: { extensionsToTreatAsEsm: ['.ts'],
'^@/(.*)$': '<rootDir>/src/$1',
'^@tests/(.*)$': '<rootDir>/tests/$1' // 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',
},
};

View File

@ -27,7 +27,7 @@
"test:e2e": "jest tests/e2e", "test:e2e": "jest tests/e2e",
"test:real": "jest --config jest.real.config.cjs", "test:real": "jest --config jest.real.config.cjs",
"test:real:debug": "REAL_TEST_DEBUG=true 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" "test:real:p2p": "jest --config jest.real.config.cjs tests/real/peer-discovery.test.ts"
}, },
"keywords": [ "keywords": [

View File

@ -6,11 +6,20 @@ module.exports = async () => {
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
process.env.DEBROS_TEST_MODE = 'real'; process.env.DEBROS_TEST_MODE = 'real';
// Check for required dependencies // Check for required dependencies - skip for ES module packages
try { try {
require('helia'); // Just check if the packages exist without importing them
require('@orbitdb/core'); const fs = require('fs');
console.log('✅ Required dependencies available'); 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) { } catch (error) {
console.error('❌ Missing required dependencies for real tests:', error.message); console.error('❌ Missing required dependencies for real tests:', error.message);
process.exit(1); process.exit(1);

View File

@ -11,8 +11,18 @@ const debugMode = process.env.REAL_TEST_DEBUG === 'true';
if (!debugMode) { if (!debugMode) {
// Silence routine logs but keep errors and important messages // Silence routine logs but keep errors and important messages
console.log = (...args: any[]) => { console.log = (...args: any[]) => {
const message = args.join(' '); try {
if (message.includes('❌') || message.includes('✅') || message.includes('🚀') || message.includes('🧹')) { 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); originalConsole.log(...args);
} }
}; };

View File

@ -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,
};
}
}

View File

@ -1,12 +1,4 @@
import { createHelia } from 'helia'; import { loadModules, loadDatastoreModules } from './helia-wrapper';
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 { join } from 'path'; import { join } from 'path';
import { PrivateSwarmSetup } from './swarm-setup'; import { PrivateSwarmSetup } from './swarm-setup';
import { IPFSInstance } from '../../../src/framework/services/OrbitDBService'; import { IPFSInstance } from '../../../src/framework/services/OrbitDBService';
@ -28,6 +20,11 @@ export class RealIPFSService implements IPFSInstance {
console.log(`🚀 Initializing IPFS node ${this.nodeIndex}...`); console.log(`🚀 Initializing IPFS node ${this.nodeIndex}...`);
try { 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 // Create libp2p instance with private swarm configuration
this.libp2p = await createLibp2p({ this.libp2p = await createLibp2p({
addresses: { addresses: {

View File

@ -1,4 +1,4 @@
import { createOrbitDB } from '@orbitdb/core'; import { loadOrbitDBModules } from './helia-wrapper';
import { RealIPFSService } from './ipfs-setup'; import { RealIPFSService } from './ipfs-setup';
import { OrbitDBInstance } from '../../../src/framework/services/OrbitDBService'; import { OrbitDBInstance } from '../../../src/framework/services/OrbitDBService';
@ -17,21 +17,24 @@ export class RealOrbitDBService implements OrbitDBInstance {
console.log(`🌀 Initializing OrbitDB for node ${this.nodeIndex}...`); console.log(`🌀 Initializing OrbitDB for node ${this.nodeIndex}...`);
try { try {
// Load OrbitDB ES modules dynamically
const { createOrbitDB } = await loadOrbitDBModules();
const ipfs = this.ipfsService.getHelia(); const ipfs = this.ipfsService.getHelia();
if (!ipfs) { if (!ipfs) {
throw new Error('IPFS node must be initialized before OrbitDB'); throw new Error('IPFS node must be initialized before OrbitDB');
} }
// Create OrbitDB instance // Create OrbitDB instance
this.orbitdb = await createOrbitDB({ this.orbitdb = await createOrbitDB({
ipfs, ipfs,
id: `orbitdb-node-${this.nodeIndex}`, 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 initialized for node ${this.nodeIndex}`);
console.log(`📍 OrbitDB ID: ${this.orbitdb.id}`); console.log(`📍 OrbitDB ID: ${this.orbitdb.id}`);
return this.orbitdb; return this.orbitdb;
} catch (error) { } catch (error) {
console.error(`❌ Failed to initialize OrbitDB for node ${this.nodeIndex}:`, 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}`; const dbKey = `${name}-${type}`;
// Check if database is already open // Check if database is already open
if (this.databases.has(dbKey)) { if (this.databases.has(dbKey)) {
return this.databases.get(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}...`); console.log(`📂 Opening ${type} database '${name}' on node ${this.nodeIndex}...`);
let database; let database;
switch (type.toLowerCase()) { switch (type.toLowerCase()) {
case 'documents': case 'documents':
case 'docstore': case 'docstore':
database = await this.orbitdb.open(name, { database = await this.orbitdb.open(name, {
type: 'documents', type: 'documents',
AccessController: 'orbitdb' AccessController: 'orbitdb',
}); });
break; break;
case 'events': case 'events':
case 'eventlog': case 'eventlog':
database = await this.orbitdb.open(name, { database = await this.orbitdb.open(name, {
type: 'events', type: 'events',
AccessController: 'orbitdb' AccessController: 'orbitdb',
}); });
break; break;
case 'keyvalue': case 'keyvalue':
case 'kvstore': case 'kvstore':
database = await this.orbitdb.open(name, { database = await this.orbitdb.open(name, {
type: 'keyvalue', type: 'keyvalue',
AccessController: 'orbitdb' AccessController: 'orbitdb',
}); });
break; break;
default: default:
// Default to documents store // Default to documents store
database = await this.orbitdb.open(name, { database = await this.orbitdb.open(name, {
type: 'documents', type: 'documents',
AccessController: 'orbitdb' AccessController: 'orbitdb',
}); });
} }
this.databases.set(dbKey, database); this.databases.set(dbKey, database);
console.log(`✅ Database '${name}' opened on node ${this.nodeIndex}`); console.log(`✅ Database '${name}' opened on node ${this.nodeIndex}`);
console.log(`🔗 Database address: ${database.address}`); console.log(`🔗 Database address: ${database.address}`);
@ -134,7 +137,7 @@ export class RealOrbitDBService implements OrbitDBInstance {
// Additional utility methods for testing // Additional utility methods for testing
async waitForReplication(database: any, timeout: number = 30000): Promise<boolean> { async waitForReplication(database: any, timeout: number = 30000): Promise<boolean> {
const startTime = Date.now(); const startTime = Date.now();
return new Promise((resolve) => { return new Promise((resolve) => {
const checkReplication = () => { const checkReplication = () => {
if (Date.now() - startTime > timeout) { if (Date.now() - startTime > timeout) {
@ -159,7 +162,7 @@ export class RealOrbitDBService implements OrbitDBInstance {
async getDatabaseInfo(name: string, type: string): Promise<any> { async getDatabaseInfo(name: string, type: string): Promise<any> {
const dbKey = `${name}-${type}`; const dbKey = `${name}-${type}`;
const database = this.databases.get(dbKey); const database = this.databases.get(dbKey);
if (!database) { if (!database) {
return null; return null;
} }
@ -169,13 +172,15 @@ export class RealOrbitDBService implements OrbitDBInstance {
type: database.type, type: database.type,
peers: database.peers || [], peers: database.peers || [],
all: await database.all(), all: await database.all(),
meta: database.meta || {} meta: database.meta || {},
}; };
} }
} }
// Utility function to create OrbitDB network from IPFS network // Utility function to create OrbitDB network from IPFS network
export async function createOrbitDBNetwork(ipfsNodes: RealIPFSService[]): Promise<RealOrbitDBService[]> { export async function createOrbitDBNetwork(
ipfsNodes: RealIPFSService[],
): Promise<RealOrbitDBService[]> {
console.log(`🌀 Creating OrbitDB network with ${ipfsNodes.length} nodes...`); console.log(`🌀 Creating OrbitDB network with ${ipfsNodes.length} nodes...`);
const orbitdbNodes: RealOrbitDBService[] = []; const orbitdbNodes: RealOrbitDBService[] = [];
@ -195,7 +200,7 @@ export async function shutdownOrbitDBNetwork(orbitdbNodes: RealOrbitDBService[])
console.log(`🛑 Shutting down OrbitDB network...`); console.log(`🛑 Shutting down OrbitDB network...`);
// Stop all OrbitDB nodes // 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`); console.log(`✅ OrbitDB network shutdown complete`);
} }
@ -204,7 +209,7 @@ export async function shutdownOrbitDBNetwork(orbitdbNodes: RealOrbitDBService[])
export async function testDatabaseReplication( export async function testDatabaseReplication(
orbitdbNodes: RealOrbitDBService[], orbitdbNodes: RealOrbitDBService[],
dbName: string, dbName: string,
dbType: string = 'documents' dbType: string = 'documents',
): Promise<boolean> { ): Promise<boolean> {
console.log(`🔄 Testing database replication for '${dbName}'...`); console.log(`🔄 Testing database replication for '${dbName}'...`);
@ -220,9 +225,9 @@ export async function testDatabaseReplication(
// Open same database on second node // Open same database on second node
const db2 = await orbitdbNodes[1].openDB(dbName, dbType); const db2 = await orbitdbNodes[1].openDB(dbName, dbType);
// Wait for replication // Wait for replication
await new Promise(resolve => setTimeout(resolve, 2000)); await new Promise((resolve) => setTimeout(resolve, 2000));
// Check if data replicated // Check if data replicated
const db2Data = await db2.all(); const db2Data = await db2.all();
@ -239,4 +244,4 @@ export async function testDatabaseReplication(
console.error(`❌ Error testing database replication:`, error); console.error(`❌ Error testing database replication:`, error);
return false; return false;
} }
} }

View File

@ -13,6 +13,8 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
/* Skip type checking of declaration files. */ /* Skip type checking of declaration files. */
"skipLibCheck": true, "skipLibCheck": true,
/* Perform compilation without referencing other files. */
"isolatedModules": true,
/* Removes comments from the project's output JavaScript code. */ /* Removes comments from the project's output JavaScript code. */
"removeComments": true, "removeComments": true,
/* Enables experimental support for emitting type metadata for decorators which works with the module reflect-metadata. */ /* Enables experimental support for emitting type metadata for decorators which works with the module reflect-metadata. */