Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
initializeWalletConnectClient,
getWalletConnectClient,
disconnectWalletConnectClient,
} from '../utils/walletConnectClient';

jest.setTimeout(120000); // Set longer timeout for the entire test suite

describe('WalletConnect Connection', () => {
// Initialize client and wait for relay connection before tests
beforeAll(async () => {
try {
const client = await initializeWalletConnectClient();
console.log('Test Suite: WalletConnect client object initialized.');

if (client.core.relayer.connected) {
console.log('Test Suite: Relayer already connected.');
return; // Already connected
}

console.log('Test Suite: Waiting for relayer connection...');
await new Promise<void>((resolve, reject) => {
// Timeout if connection takes too long
const timeout = setTimeout(() => {
console.error('Test Suite: Relayer connection attempt timed out.');
reject(new Error('Relayer connection timeout'));
}, 60000); // Increased timeout to 60 seconds

// Use .once() as we only need the first connection event for setup
client.core.relayer.once('relayer_connect', () => {
clearTimeout(timeout);
console.log("Test Suite: 'relayer_connect' event received.");
resolve();
});

client.core.relayer.once('relayer_error', (error: Error) => {
clearTimeout(timeout);
console.error("Test Suite: 'relayer_error' event received.", error);
reject(error);
});
});

} catch (error) {
console.error('Test Suite Error: Failed during WalletConnect client initialization or connection wait', error);
// Optionally throw to fail the suite immediately
throw error;
}
});

// Disconnect client after all tests in this suite have run
afterAll(async () => {
// No need for complex cleanup with done() since we're using --forceExit
await disconnectWalletConnectClient();
console.log('Test Suite: WalletConnect client disconnected.');
});

it('should initialize the WalletConnect client successfully', () => {
// The beforeAll block handles initialization, so we just check if getClient works
expect(() => getWalletConnectClient()).not.toThrow();
const client = getWalletConnectClient();
expect(client).toBeDefined();
// Add more specific checks if needed, e.g., client properties
expect(client.core.relayer.connected).toBe(true); // Check if relay connection is active
});

// Add more tests related to connection status or initial setup here
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
initializeWalletConnectClient,
getWalletConnectClient,
disconnectWalletConnectClient
} from '../utils/walletConnectClient';
import { createVirtualDappPairing } from '../utils/virtualDapp';

jest.setTimeout(120000); // Set longer timeout for the entire test suite

describe('WalletConnect Pairing', () => {
// Initialize client before tests
beforeAll(async () => {
await initializeWalletConnectClient();
console.log('Test Suite: WalletConnect client initialized for pairing tests');
});

// Disconnect client after tests
afterAll(async () => {
await disconnectWalletConnectClient();
console.log('Test Suite: WalletConnect client disconnected after pairing tests');
});

it('should pair with a virtual dApp successfully', async () => {
const client = getWalletConnectClient();
expect(client).toBeDefined();

// Create a virtual dApp and get the pairing URI
const result = await createVirtualDappPairing();
expect(result).toBeDefined();

const { pairingUri, approvePairing, dappClient } = result;
expect(pairingUri).toBeDefined();

// Add non-null assertion to satisfy TypeScript
expect(pairingUri!.startsWith('wc:')).toBe(true);

console.log('Test Suite: Pairing URI created:', pairingUri);

// Ensure pairingUri is defined before using it
if (!pairingUri) {
throw new Error('Pairing URI is undefined');
}

// Simulate wallet connecting to dApp by pairing with the URI
const pairingPromise = client.pair({ uri: pairingUri });

// Approve the pairing from the dApp side
console.log('Test Suite: Approving pairing from dApp side');
await approvePairing();

// Wait for pairing to complete
const pairingResult = await pairingPromise;
expect(pairingResult).toBeDefined();
expect(pairingResult.topic).toBeDefined();

console.log('Test Suite: Pairing completed successfully with topic:', pairingResult.topic);

// Verify that we have an active session
const sessions = client.session.getAll();
expect(sessions.length).toBeGreaterThan(0);

// Verify the pairing exists
const pairings = client.pairing.getAll();
expect(pairings.some(p => p.topic === pairingResult.topic)).toBe(true);

// Clean up the dApp client
console.log('Test Suite: Cleaning up dApp client');
await dappClient.core.relayer.transportClose();
});
});
22 changes: 22 additions & 0 deletions packages/hathor-rpc-handler/integration-tests/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: '3.8'

services:
redis:
image: redis:latest
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 1s
timeout: 3s
retries: 30

relay:
image: reown/relay
ports:
- "5555:5000"
environment:
- REDIS_URL=redis://redis:6379/0
depends_on:
redis:
condition: service_healthy # Wait for Redis to be healthy
27 changes: 27 additions & 0 deletions packages/hathor-rpc-handler/integration-tests/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
rootDir: '.',
roots: [
'<rootDir>/__tests__',
'<rootDir>/utils'
],
testMatch: [
'**/__tests__/**/*.+(ts|tsx|js)',
'**/?(*.)+(spec|test).+(ts|tsx|js)'
],
transform: {
'^.+.tsx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.json'
}]
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
// Specify global setup and teardown scripts
globalSetup: '<rootDir>/jest.globalSetup.ts',
globalTeardown: '<rootDir>/jest.globalTeardown.ts',
// Run tests sequentially because they share the Docker environment
maxWorkers: 1,
// Set a longer timeout for integration tests and hooks
testTimeout: 90000, // 90 seconds
};
26 changes: 26 additions & 0 deletions packages/hathor-rpc-handler/integration-tests/jest.globalSetup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { execSync } from 'child_process';
import * as path from 'path';

const integrationTestsDir = __dirname; // Correctly refers to the integration-tests directory
const composeFile = path.join(integrationTestsDir, 'docker-compose.yml');

export default async () => {
console.log('\nSetting up integration test environment...');
try {
// Use --wait to ensure services are healthy/running before proceeding
execSync(`docker compose -f "${composeFile}" up -d --wait`, { stdio: 'inherit' });
console.log('Docker containers started successfully.');
// Add a small delay just in case services need a moment to fully initialize after becoming "healthy"
await new Promise(resolve => setTimeout(resolve, 3000)); // 3 seconds delay
console.log('Integration test environment setup complete.');
} catch (error) {
console.error('Failed to start Docker containers:', error);
// Optionally, try to bring down containers if setup failed partially
try {
execSync(`docker compose -f "${composeFile}" down`, { stdio: 'inherit' });
} catch (downError) {
console.error('Failed to run docker compose down after setup error:', downError);
}
process.exit(1); // Exit if setup fails
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { execSync } from 'child_process';
import * as path from 'path';

const integrationTestsDir = __dirname; // Correctly refers to the integration-tests directory
const composeFile = path.join(integrationTestsDir, 'docker-compose.yml');

export default async () => {
console.log('\nTearing down integration test environment...');
try {
execSync(`docker compose -f "${composeFile}" down -v --remove-orphans`, { stdio: 'inherit' });
console.log('Docker containers stopped and removed successfully.');
} catch (error) {
console.error('Failed to stop Docker containers:', error);
// Don't exit here, as tests might have finished, but teardown failed.
}
};
47 changes: 47 additions & 0 deletions packages/hathor-rpc-handler/integration-tests/test-connection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Client from '@walletconnect/sign-client';
import {
initializeWalletConnectClient,
disconnectWalletConnectClient,
} from './utils/walletConnectClient';

let client: Client | null = null;

async function runConnectionTest() {
console.log('Starting standalone connection test...');
try {
// Initialize client
client = await initializeWalletConnectClient();
console.log('Client initialized.');

// Check initial connection state
console.log(`Initial connection state: ${client.core.relayer.connected}`);

// Set up event listener without waiting
client.core.relayer.once('relayer_connect', () => {
console.log('>>> relayer_connect event received! <<<');
console.log(`Connection state after event: ${client!.core.relayer.connected}`);
});

client.core.relayer.once('relayer_error', (error: Error) => {
console.error('>>> relayer_error event received! <<<', error);
});

// Wait a bit to see if we get the connect event
await new Promise(resolve => setTimeout(resolve, 10000));

console.log(`Final connection state: ${client.core.relayer.connected}`);
console.log('Connection test completed.');

} catch (error) {
console.error('Connection test failed:', error);
process.exitCode = 1; // Set exit code to indicate failure
} finally {
// Attempt to disconnect
console.log('Attempting to disconnect...');
await disconnectWalletConnectClient();
console.log('Standalone test finished.');
}
}

// Run the test
runConnectionTest();
30 changes: 30 additions & 0 deletions packages/hathor-rpc-handler/integration-tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"outDir": "dist",
"rootDir": ".",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"lib": ["ES2016", "DOM"],
"types": ["node", "jest"]
},
"include": [
"src/**/*.ts",
"jest.config.js",
"jest.globalSetup.ts",
"jest.globalTeardown.ts"
],
"exclude": [
"node_modules",
"dist"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const HATHOR_TEST_SEED = 'castle item critic hand disorder girl across prevent claw fault vacuum combine food debris arrow member autumn half spirit sick priority evil step genius';
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { HathorWallet } from '@hathor/wallet-lib';
import { HATHOR_TEST_SEED } from './config';

let walletInstance: HathorWallet | null = null;

/**
* Initializes and returns a singleton HathorWallet instance for testing.
*
* Note: This basic initialization might need adjustments depending on
* specific test requirements (e.g., network, server).
*/
export function initializeHathorWallet(): HathorWallet {
if (walletInstance) {
return walletInstance;
}

// Basic configuration, adjust as necessary
const walletConfig = {
seed: HATHOR_TEST_SEED,
network: 'testnet', // Assuming testnet for integration tests
connection: undefined, // Use default connection or configure if needed
password: '123', // Default password for testing
pinCode: '123', // Default pin for testing
};

walletInstance = new HathorWallet(walletConfig);

// Start the wallet (important step)
// Note: Starting might involve network requests, ensure environment allows this.
// Consider if starting should happen here or per-test suite.
// For now, let's assume starting here is fine.
// await walletInstance.start(); // Uncomment and handle async if needed

console.log('Hathor Wallet initialized for testing.');
return walletInstance;
}

/**
* Gets the initialized HathorWallet instance.
* Throws an error if the wallet hasn't been initialized.
*/
export function getHathorWallet(): HathorWallet {
if (!walletInstance) {
throw new Error('Hathor Wallet is not initialized. Call initializeHathorWallet first.');
}
return walletInstance;
}

// Optional: Add a function to stop/clean up the wallet if necessary
export async function stopHathorWallet(): Promise<void> {
if (walletInstance) {
// await walletInstance.stop(); // Uncomment if wallet needs explicit stopping
walletInstance = null;
console.log('Hathor Wallet stopped.');
}
}
Loading