mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
## 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>
119 lines
3.7 KiB
TypeScript
119 lines
3.7 KiB
TypeScript
import invariant from "tiny-invariant";
|
|
import { getServiceFromKurtosis, logger, printHeader } from "utils";
|
|
import type { LaunchedNetwork } from "../../../launcher/types/launchedNetwork";
|
|
import { BASE_SERVICES } from "../../../launcher/utils/constants";
|
|
import type { LaunchOptions } from ".";
|
|
|
|
export const performSummaryOperations = async (
|
|
options: LaunchOptions,
|
|
launchedNetwork: LaunchedNetwork
|
|
) => {
|
|
printHeader("Service Endpoints");
|
|
|
|
const servicesToDisplay = BASE_SERVICES;
|
|
|
|
if (options.blockscout === true) {
|
|
servicesToDisplay.push(...["blockscout", "blockscout-frontend"]);
|
|
}
|
|
|
|
if (launchedNetwork.containers.find((c) => c.name === "datahaven-alice")) {
|
|
servicesToDisplay.push("datahaven-alice");
|
|
}
|
|
|
|
logger.trace("Services to display", servicesToDisplay);
|
|
|
|
const displayData: {
|
|
service: string;
|
|
ports: Record<string, number>;
|
|
url: string;
|
|
}[] = [];
|
|
for (const service of servicesToDisplay) {
|
|
logger.debug(`Checking service: ${service}`);
|
|
|
|
const serviceInfo = service.startsWith("datahaven-")
|
|
? undefined
|
|
: await getServiceFromKurtosis(service, options.kurtosisEnclaveName);
|
|
logger.trace("Service info", serviceInfo);
|
|
switch (true) {
|
|
case service.startsWith("cl-"): {
|
|
invariant(serviceInfo, `❌ Service info for ${service} is not available`);
|
|
const httpPort = serviceInfo.public_ports.http.number;
|
|
displayData.push({
|
|
service,
|
|
ports: { http: httpPort },
|
|
url: `http://127.0.0.1:${httpPort}`
|
|
});
|
|
break;
|
|
}
|
|
|
|
case service.startsWith("el-"): {
|
|
invariant(serviceInfo, `❌ Service info for ${service} is not available`);
|
|
const rpcPort = serviceInfo.public_ports.rpc.number;
|
|
const wsPort = serviceInfo.public_ports.ws.number;
|
|
displayData.push({
|
|
service,
|
|
ports: { rpc: rpcPort, ws: wsPort },
|
|
url: `http://127.0.0.1:${rpcPort}`
|
|
});
|
|
break;
|
|
}
|
|
|
|
case service.startsWith("dora"): {
|
|
invariant(serviceInfo, `❌ Service info for ${service} is not available`);
|
|
const httpPort = serviceInfo.public_ports.http.number;
|
|
displayData.push({
|
|
service,
|
|
ports: { http: httpPort },
|
|
url: `http://127.0.0.1:${httpPort}`
|
|
});
|
|
break;
|
|
}
|
|
|
|
case service === "blockscout": {
|
|
invariant(serviceInfo, `❌ Service info for ${service} is not available`);
|
|
const httpPort = serviceInfo.public_ports.http.number;
|
|
displayData.push({
|
|
service,
|
|
ports: { http: httpPort },
|
|
url: `http://127.0.0.1:${httpPort}`
|
|
});
|
|
break;
|
|
}
|
|
|
|
case service === "blockscout-frontend": {
|
|
invariant(serviceInfo, `❌ Service info for ${service} is not available`);
|
|
const httpPort = serviceInfo.public_ports.http.number;
|
|
displayData.push({
|
|
service,
|
|
ports: { http: httpPort },
|
|
url: `http://127.0.0.1:${httpPort}`
|
|
});
|
|
break;
|
|
}
|
|
|
|
case service === "datahaven-alice": {
|
|
const port = launchedNetwork.getContainerPort(service);
|
|
displayData.push({
|
|
service,
|
|
ports: { ws: port },
|
|
url: `http://127.0.0.1:${port}`
|
|
});
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
logger.error(`Unknown service: ${service}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
const containers = launchedNetwork.containers.filter((c) => !c.name.startsWith("datahaven-"));
|
|
for (const { name, publicPorts } of containers) {
|
|
const url = "ws" in publicPorts ? `ws://127.0.0.1:${publicPorts.ws}` : undefined;
|
|
if (url) {
|
|
displayData.push({ service: name, ports: publicPorts, url });
|
|
}
|
|
}
|
|
|
|
console.table(displayData);
|
|
};
|