datahaven/test/cli/handlers/launch/datahaven.ts
Facundo Farall 9b311e00ef
test: 🏗️ Setup e2e testing framework (#104)
## Implement E2E Testing Framework with Isolated Networks

### Summary
Refactors the existing E2E testing infrastructure to provide isolated
test environments with parallel execution support. Each test suite now
runs in its own network namespace, preventing resource conflicts.

### Key Changes
- **New Testing Framework** (`test/framework/`): Base classes for test
lifecycle management with automatic setup/teardown
- **Launcher Module** (`test/launcher/`): Extracted network
orchestration logic from CLI handlers for reusability
- **Parallel Execution**: Added `test-parallel.ts` script with
concurrency limits to prevent resource exhaustion
- **Test Isolation**: Each suite gets unique network IDs (format:
`suiteName-timestamp`) and Docker networks
- **Improved Test Organization**: Migrated tests to new framework,
deprecated old test structure

### Test Improvements
- Added 4 new test suites demonstrating framework usage. :
  - `contracts.test.ts` - Smart contract deployment/interaction
  - `datahaven-substrate.test.ts` - Substrate API operations  
  - `cross-chain.test.ts` - Snowbridge cross-chain messaging
  - `ethereum-basic.test.ts` - Ethereum network operations

> [!WARNING]
The test suites themselves are bad and shouldn't be consider examples of
good tests. They were AI generated just to test the concurrency of test
runners

### Documentation
- Added comprehensive framework overview (`E2E_FRAMEWORK_OVERVIEW.md`)
- Updated README with parallel testing commands
- Added test patterns and best practices

### Breaking Changes
- Old test suites moved to `e2e - DEPRECATED/` directory
- Test execution now requires extending `BaseTestSuite` class

### Testing
Run tests with: `bun test:e2e` or `bun test:e2e:parallel` (with
concurrency limits)

### TODO
- [ ] Implement good test examples.
- [ ] Implement useful test utils (like waiting for an event to show up
in DataHaven or Ethereum).
- [ ] Enforce tests with CI (currently cannot be done due to
intermittent error when sending a transaction with PAPI).

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: undercover-cactus <lola@moonsonglabs.com>
2025-07-16 18:51:07 +02:00

111 lines
3.4 KiB
TypeScript

import invariant from "tiny-invariant";
import { confirmWithTimeout, logger, printDivider, printHeader } from "utils";
import {
checkDataHavenRunning,
cleanDataHavenContainers,
launchLocalDataHavenSolochain,
registerNodes
} from "../../../launcher/datahaven";
import type { LaunchedNetwork } from "../../../launcher/types/launchedNetwork";
import { type LaunchOptions, NETWORK_ID } from ".";
// 2 validators (Alice and Bob) are used for local & CI testing
// <repo_root>/operator/runtime/stagenet/src/genesis_config_presets.rs#L98
const CLI_AUTHORITY_IDS = ["alice", "bob"] as const;
/**
* Launches a DataHaven solochain network for testing.
*
* @param options - Configuration options for launching the network.
* @param launchedNetwork - An instance of LaunchedNetwork to track the network's state.
*/
export const launchDataHavenSolochain = async (
options: LaunchOptions,
launchedNetwork: LaunchedNetwork
) => {
printHeader("Starting DataHaven Network");
let shouldLaunchDataHaven = options.datahaven;
if (shouldLaunchDataHaven === undefined) {
shouldLaunchDataHaven = await confirmWithTimeout(
"Do you want to launch the DataHaven network?",
true,
10
);
} else {
logger.info(
`🏳️ Using flag option: ${shouldLaunchDataHaven ? "will launch" : "will not launch"} DataHaven network`
);
}
if (!shouldLaunchDataHaven) {
logger.info("👍 Skipping DataHaven network launch. Done!");
await registerNodes(NETWORK_ID, launchedNetwork);
printDivider();
return;
}
if (await checkDataHavenRunning()) {
// If the user wants to launch the DataHaven network, we ask them if they want
// to clean the existing containers/network or just continue with the existing
// containers/network.
if (shouldLaunchDataHaven) {
let shouldRelaunch = options.cleanNetwork;
if (shouldRelaunch === undefined) {
shouldRelaunch = await confirmWithTimeout(
"Do you want to clean and relaunch the DataHaven containers?",
true,
10
);
}
// Case: User wants to keep existing containers/network
if (!shouldRelaunch) {
logger.info("👍 Keeping existing DataHaven containers/network.");
await registerNodes(NETWORK_ID, launchedNetwork);
printDivider();
return;
}
// Case: User wants to clean and relaunch the DataHaven containers
await cleanDataHavenContainers(NETWORK_ID);
}
}
invariant(options.datahavenImageTag, "❌ DataHaven image tag not defined");
let shouldBuildDataHaven = options.buildDatahaven;
if (shouldBuildDataHaven === undefined) {
shouldBuildDataHaven = await confirmWithTimeout(
"Do you want to build the DataHaven node local Docker image?",
true,
10
);
} else {
logger.info(
`🏳️ Using flag option: ${shouldBuildDataHaven ? "will build" : "will not build"} DataHaven node local Docker image`
);
}
if (!shouldBuildDataHaven) {
logger.info("👍 Skipping DataHaven node local Docker image build. Done!");
}
await launchLocalDataHavenSolochain(
{
networkId: NETWORK_ID,
datahavenImageTag: options.datahavenImageTag,
relayerImageTag: options.relayerImageTag,
authorityIds: CLI_AUTHORITY_IDS,
buildDatahaven: shouldBuildDataHaven,
datahavenBuildExtraArgs: options.datahavenBuildExtraArgs
},
launchedNetwork
);
printDivider();
};