Skip to content

Commit 0f1915a

Browse files
committed
chore: added initial setup for walletconnect integration tests
1 parent 5cfee6d commit 0f1915a

13 files changed

+431
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Hathor RPC Handler - Integration Tests
2+
3+
This directory contains integration tests for the `hathor-rpc-handler` package.
4+
These tests interact with real external services (like a WalletConnect relay server and Redis) running in Docker containers.
5+
6+
## Prerequisites
7+
8+
- Docker
9+
- Docker Compose
10+
- Node.js (>=20 recommended)
11+
- Yarn (v4+ recommended)
12+
13+
Ensure Docker Desktop (or Docker Engine with Docker Compose plugin) is running before executing the tests.
14+
15+
## Running the Tests
16+
17+
1. **Install Dependencies:**
18+
If you haven't already, navigate to the root directory of the `hathor-rpc-lib` monorepo and run:
19+
```bash
20+
yarn install
21+
```
22+
This installs dependencies for all packages, including the required WalletConnect libraries.
23+
24+
2. **Execute Tests:**
25+
Navigate to the `packages/hathor-rpc-handler` directory:
26+
```bash
27+
cd packages/hathor-rpc-handler
28+
```
29+
Then, run the integration test script:
30+
```bash
31+
yarn test:integration
32+
# or
33+
# npm run test:integration
34+
```
35+
36+
This command will:
37+
- Start the required Docker containers (Redis, WalletConnect Relay) using `docker-compose up` (handled by `jest.globalSetup.ts`).
38+
- Run the Jest integration tests located in `integration-tests/src` sequentially (`--runInBand`).
39+
- Stop and remove the Docker containers using `docker-compose down` (handled by `jest.globalTeardown.ts`).
40+
41+
## Test Environment
42+
43+
- **Docker:** Uses `integration-tests/docker-compose.yml` to define and manage the test environment services.
44+
- **Jest:** Configured via `integration-tests/jest.config.js`, using global setup/teardown scripts.
45+
- **WalletConnect:** Connects to a local relay server running in Docker at `ws://localhost:5555`.
46+
- **Hathor Wallet:** Uses a pre-defined test seed phrase (`integration-tests/src/config.ts`) to initialize a wallet instance.
47+
48+
## Adding New Tests
49+
50+
- Create new test files (e.g., `myFeature.test.ts`) inside the `integration-tests/src` directory.
51+
- Utilize the helper functions in `walletConnectClient.ts` and `hathorWallet.ts` to interact with the initialized clients/wallets.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
version: '3.8'
2+
3+
services:
4+
redis:
5+
image: redis:latest
6+
ports:
7+
# Expose Redis port mainly for debugging if needed, not strictly necessary for relay
8+
- "6379:6379"
9+
healthcheck:
10+
test: ["CMD", "redis-cli", "ping"]
11+
interval: 1s
12+
timeout: 3s
13+
retries: 30
14+
15+
relay:
16+
image: reown/relay
17+
ports:
18+
- "5555:5000" # Expose relay port
19+
environment:
20+
- REDIS_URL=redis://redis:6379/0 # Use service name 'redis' for internal communication
21+
depends_on:
22+
redis:
23+
condition: service_healthy # Wait for Redis to be healthy
24+
# Add a simple healthcheck if possible (e.g., checking if the port is open)
25+
# Or rely on depends_on and --wait in docker-compose up
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/** @type {import('ts-jest').JestConfigWithTsJest} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
rootDir: '.', // Look for tests in the integration-tests directory
6+
roots: [
7+
'<rootDir>/src' // Source files for tests
8+
],
9+
testMatch: [
10+
'**/__tests__/**/*.+(ts|tsx|js)',
11+
'**/?(*.)+(spec|test).+(ts|tsx|js)'
12+
],
13+
transform: {
14+
'^.+.tsx?$': ['ts-jest', {
15+
// ts-jest configuration options
16+
tsconfig: 'tsconfig.json' // Assuming tsconfig is in hathor-rpc-handler root
17+
}]
18+
},
19+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
20+
// Specify global setup and teardown scripts
21+
globalSetup: '<rootDir>/jest.globalSetup.ts',
22+
globalTeardown: '<rootDir>/jest.globalTeardown.ts',
23+
// Run tests sequentially because they share the Docker environment
24+
maxWorkers: 1,
25+
// Set a longer timeout for integration tests and hooks
26+
testTimeout: 90000, // 90 seconds
27+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { execSync } from 'child_process';
2+
import * as path from 'path';
3+
4+
const integrationTestsDir = __dirname; // Correctly refers to the integration-tests directory
5+
const composeFile = path.join(integrationTestsDir, 'docker-compose.yml');
6+
7+
export default async () => {
8+
console.log('\nSetting up integration test environment...');
9+
try {
10+
// Use --wait to ensure services are healthy/running before proceeding
11+
execSync(`docker compose -f "${composeFile}" up -d --wait`, { stdio: 'inherit' });
12+
console.log('Docker containers started successfully.');
13+
// Add a small delay just in case services need a moment to fully initialize after becoming "healthy"
14+
await new Promise(resolve => setTimeout(resolve, 3000)); // 3 seconds delay
15+
console.log('Integration test environment setup complete.');
16+
} catch (error) {
17+
console.error('Failed to start Docker containers:', error);
18+
// Optionally, try to bring down containers if setup failed partially
19+
try {
20+
execSync(`docker compose -f "${composeFile}" down`, { stdio: 'inherit' });
21+
} catch (downError) {
22+
console.error('Failed to run docker compose down after setup error:', downError);
23+
}
24+
process.exit(1); // Exit if setup fails
25+
}
26+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { execSync } from 'child_process';
2+
import * as path from 'path';
3+
4+
const integrationTestsDir = __dirname; // Correctly refers to the integration-tests directory
5+
const composeFile = path.join(integrationTestsDir, 'docker-compose.yml');
6+
7+
export default async () => {
8+
console.log('\nTearing down integration test environment...');
9+
try {
10+
execSync(`docker compose -f "${composeFile}" down -v --remove-orphans`, { stdio: 'inherit' });
11+
console.log('Docker containers stopped and removed successfully.');
12+
} catch (error) {
13+
console.error('Failed to stop Docker containers:', error);
14+
// Don't exit here, as tests might have finished, but teardown failed.
15+
}
16+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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';
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {
2+
initializeWalletConnectClient,
3+
getWalletConnectClient,
4+
disconnectWalletConnectClient,
5+
} from './walletConnectClient';
6+
7+
jest.setTimeout(120000); // Set longer timeout for the entire test suite
8+
9+
describe('WalletConnect Connection', () => {
10+
// Initialize client and wait for relay connection before tests
11+
beforeAll(async () => {
12+
try {
13+
const client = await initializeWalletConnectClient();
14+
console.log('Test Suite: WalletConnect client object initialized.');
15+
16+
if (client.core.relayer.connected) {
17+
console.log('Test Suite: Relayer already connected.');
18+
return; // Already connected
19+
}
20+
21+
console.log('Test Suite: Waiting for relayer connection...');
22+
await new Promise<void>((resolve, reject) => {
23+
// Timeout if connection takes too long
24+
const timeout = setTimeout(() => {
25+
console.error('Test Suite: Relayer connection attempt timed out.');
26+
reject(new Error('Relayer connection timeout'));
27+
}, 60000); // Increased timeout to 60 seconds
28+
29+
// Use .once() as we only need the first connection event for setup
30+
client.core.relayer.once('relayer_connect', () => {
31+
clearTimeout(timeout);
32+
console.log("Test Suite: 'relayer_connect' event received.");
33+
resolve();
34+
});
35+
36+
client.core.relayer.once('relayer_error', (error: Error) => {
37+
clearTimeout(timeout);
38+
console.error("Test Suite: 'relayer_error' event received.", error);
39+
reject(error);
40+
});
41+
});
42+
43+
} catch (error) {
44+
console.error('Test Suite Error: Failed during WalletConnect client initialization or connection wait', error);
45+
// Optionally throw to fail the suite immediately
46+
throw error;
47+
}
48+
});
49+
50+
// Disconnect client after all tests in this suite have run
51+
afterAll(async () => {
52+
// No need for complex cleanup with done() since we're using --forceExit
53+
await disconnectWalletConnectClient();
54+
console.log('Test Suite: WalletConnect client disconnected.');
55+
});
56+
57+
it('should initialize the WalletConnect client successfully', () => {
58+
// The beforeAll block handles initialization, so we just check if getClient works
59+
expect(() => getWalletConnectClient()).not.toThrow();
60+
const client = getWalletConnectClient();
61+
expect(client).toBeDefined();
62+
// Add more specific checks if needed, e.g., client properties
63+
expect(client.core.relayer.connected).toBe(true); // Check if relay connection is active
64+
});
65+
66+
// Add more tests related to connection status or initial setup here
67+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { HathorWallet } from '@hathor/wallet-lib';
2+
import { HATHOR_TEST_SEED } from './config';
3+
4+
let walletInstance: HathorWallet | null = null;
5+
6+
/**
7+
* Initializes and returns a singleton HathorWallet instance for testing.
8+
*
9+
* Note: This basic initialization might need adjustments depending on
10+
* specific test requirements (e.g., network, server).
11+
*/
12+
export function initializeHathorWallet(): HathorWallet {
13+
if (walletInstance) {
14+
return walletInstance;
15+
}
16+
17+
// Basic configuration, adjust as necessary
18+
const walletConfig = {
19+
seed: HATHOR_TEST_SEED,
20+
network: 'testnet', // Assuming testnet for integration tests
21+
connection: undefined, // Use default connection or configure if needed
22+
password: '123', // Default password for testing
23+
pinCode: '123', // Default pin for testing
24+
};
25+
26+
walletInstance = new HathorWallet(walletConfig);
27+
28+
// Start the wallet (important step)
29+
// Note: Starting might involve network requests, ensure environment allows this.
30+
// Consider if starting should happen here or per-test suite.
31+
// For now, let's assume starting here is fine.
32+
// await walletInstance.start(); // Uncomment and handle async if needed
33+
34+
console.log('Hathor Wallet initialized for testing.');
35+
return walletInstance;
36+
}
37+
38+
/**
39+
* Gets the initialized HathorWallet instance.
40+
* Throws an error if the wallet hasn't been initialized.
41+
*/
42+
export function getHathorWallet(): HathorWallet {
43+
if (!walletInstance) {
44+
throw new Error('Hathor Wallet is not initialized. Call initializeHathorWallet first.');
45+
}
46+
return walletInstance;
47+
}
48+
49+
// Optional: Add a function to stop/clean up the wallet if necessary
50+
export async function stopHathorWallet(): Promise<void> {
51+
if (walletInstance) {
52+
// await walletInstance.stop(); // Uncomment if wallet needs explicit stopping
53+
walletInstance = null;
54+
console.log('Hathor Wallet stopped.');
55+
}
56+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import Client from '@walletconnect/sign-client';
2+
import {
3+
WC_APP_METADATA,
4+
WC_LOGGER_LEVEL,
5+
WC_PROJECT_ID,
6+
WC_RELAY_URL,
7+
} from './walletConnectConfig';
8+
9+
let clientInstance: Client | null = null;
10+
11+
/**
12+
* Initializes and returns a singleton WalletConnect Client instance.
13+
*/
14+
export async function initializeWalletConnectClient(): Promise<Client> {
15+
if (clientInstance) {
16+
console.log('WalletConnect Client already initialized.');
17+
return clientInstance;
18+
}
19+
20+
console.log(`Initializing WalletConnect Client with relay: ${WC_RELAY_URL}`);
21+
try {
22+
clientInstance = await Client.init({
23+
logger: WC_LOGGER_LEVEL,
24+
relayUrl: WC_RELAY_URL,
25+
projectId: WC_PROJECT_ID,
26+
metadata: WC_APP_METADATA,
27+
});
28+
29+
return clientInstance;
30+
} catch (error) {
31+
console.error('Failed to initialize WalletConnect client:', error);
32+
throw error; // Re-throw error to fail tests if connection fails
33+
}
34+
}
35+
36+
/**
37+
* Gets the initialized WalletConnect Client instance.
38+
* Throws an error if the client hasn't been initialized.
39+
*/
40+
export function getWalletConnectClient(): Client {
41+
if (!clientInstance) {
42+
throw new Error('WalletConnect client is not initialized. Call initializeWalletConnectClient first.');
43+
}
44+
return clientInstance;
45+
}
46+
47+
/**
48+
* Disconnects the WalletConnect client if it's initialized.
49+
*/
50+
export async function disconnectWalletConnectClient(): Promise<void> {
51+
if (clientInstance) {
52+
try {
53+
console.log('Disconnecting WalletConnect Client...');
54+
// Just nullify the client and let garbage collection handle cleanup
55+
clientInstance = null;
56+
console.log('WalletConnect Client disconnected.');
57+
} catch (error) {
58+
console.error('Error during WalletConnect client disconnection:', error);
59+
// Ensure client is nullified
60+
clientInstance = null;
61+
}
62+
} else {
63+
console.log('WalletConnect Client was not initialized, no need to disconnect.');
64+
}
65+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { SignClientTypes } from '@walletconnect/types'; // Import Metadata type
2+
3+
// Use the Project ID observed in the working relay log
4+
export const WC_PROJECT_ID = '8264fff563181da658ce64ee80e80458';
5+
6+
export const WC_RELAY_URL = 'ws://localhost:5555'; // Keep as localhost for now
7+
8+
export const WC_LOGGER_LEVEL = 'debug';
9+
10+
export const WC_APP_METADATA: SignClientTypes.Metadata = {
11+
name: 'Hathor RPC Integration Test',
12+
description: 'Integration tests for hathor-rpc-handler',
13+
url: '#', // Placeholder URL
14+
icons: [], // Placeholder Icons
15+
};

0 commit comments

Comments
 (0)