mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 01:38:32 +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>
143 lines
4.6 KiB
TypeScript
143 lines
4.6 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { $ } from "bun";
|
|
import { logger } from "utils";
|
|
|
|
const LOG_LEVEL = Bun.env.LOG_LEVEL || "info";
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
export const cargoCrossbuild = async (options: {
|
|
datahavenBuildExtraArgs?: string;
|
|
networkId?: string;
|
|
}) => {
|
|
logger.info("🔀 Cross-building DataHaven node for Linux AMD64");
|
|
|
|
const ARCH = (await $`uname -m`.text()).trim();
|
|
const OS = (await $`uname -s`.text()).trim();
|
|
|
|
// Case: Apple Silicon
|
|
if (ARCH === "arm64" && OS === "Darwin") {
|
|
logger.info("🍎 Apple Silicon detected. Proceeding with cross-building...");
|
|
|
|
if (!isCommandAvailable("zig")) {
|
|
logger.error("Zig is not installed. Please install Zig to proceed.");
|
|
logger.info(
|
|
"Instructions to install can be found here: https://ziglang.org/learn/getting-started/"
|
|
);
|
|
throw new Error("Zig is not installed");
|
|
}
|
|
|
|
await installCargoZigbuild();
|
|
|
|
const target = "x86_64-unknown-linux-gnu";
|
|
await addRustupTarget(target);
|
|
|
|
// Build and copy libpq.so before cargo zigbuild
|
|
await buildAndCopyLibpq(target, options.networkId);
|
|
|
|
// Get additional arguments from command line
|
|
const additionalArgs = options.datahavenBuildExtraArgs ?? "";
|
|
|
|
const command = `cargo zigbuild --target ${target} --release ${additionalArgs}`;
|
|
logger.debug(`Running build command: ${command}`);
|
|
|
|
if (LOG_LEVEL === "debug") {
|
|
await $`sh -c "${command}"`.cwd(`${process.cwd()}/../operator`);
|
|
} else {
|
|
await $`sh -c "${command}"`.cwd(`${process.cwd()}/../operator`).quiet();
|
|
}
|
|
|
|
// Case: Linux x86
|
|
} else if (ARCH === "x86_64" && OS === "Linux") {
|
|
logger.info("🖥️ Linux AMD64 detected. Proceeding with cross-building...");
|
|
|
|
const target = "x86_64-unknown-linux-gnu";
|
|
await addRustupTarget(target);
|
|
|
|
// Get additional arguments from command line
|
|
const additionalArgs = options.datahavenBuildExtraArgs ?? "";
|
|
|
|
const command = `cargo build --target ${target} --release ${additionalArgs}`;
|
|
logger.debug(`Running build command: ${command}`);
|
|
|
|
if (LOG_LEVEL === "debug") {
|
|
await $`sh -c "${command}"`.cwd(`${process.cwd()}/../operator`);
|
|
} else {
|
|
await $`sh -c "${command}"`.cwd(`${process.cwd()}/../operator`).quiet();
|
|
}
|
|
|
|
// Case: Unsupported architecture or OS
|
|
} else {
|
|
logger.error("🚨 Unsupported architecture or OS. Please use Apple Silicon or Linux AMD64.");
|
|
logger.info(`Architecture: ${ARCH}; OS: ${OS}`);
|
|
throw new Error("Unsupported architecture or OS");
|
|
}
|
|
};
|
|
|
|
const isCommandAvailable = async (command: string): Promise<boolean> => {
|
|
try {
|
|
await $`command -v ${command}`.text();
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const installCargoZigbuild = async (): Promise<void> => {
|
|
if (!(await $`cargo install --list`.text()).includes("cargo-zigbuild")) {
|
|
await $`cargo install cargo-zigbuild --locked`.text();
|
|
}
|
|
};
|
|
|
|
const addRustupTarget = async (target: string): Promise<void> => {
|
|
if (!(await $`rustup target list --installed`.text()).includes(target)) {
|
|
logger.debug(await $`rustup target add ${target}`.text());
|
|
}
|
|
};
|
|
|
|
// Updated function to build and copy libpq.so
|
|
const buildAndCopyLibpq = async (target: string, networkId?: string): Promise<void> => {
|
|
logger.info("🏗️ Building and copying libpq.so...");
|
|
|
|
// Set Docker platform
|
|
process.env.DOCKER_DEFAULT_PLATFORM = "linux/amd64";
|
|
|
|
// Build Docker image
|
|
const dockerfilePath = path.join(__dirname, "../docker/crossbuild-mac-libpq.dockerfile");
|
|
logger.debug(
|
|
await $`docker build -f ${dockerfilePath} -t crossbuild-libpq ${path.join(__dirname, "..", "..")}`.text()
|
|
);
|
|
|
|
// Create container with unique name
|
|
const containerName = networkId ? `linux-libpq-container-${networkId}` : "linux-libpq-container";
|
|
logger.debug(await $`docker create --name ${containerName} crossbuild-libpq`.text());
|
|
|
|
const destPath = path.join(
|
|
__dirname,
|
|
"..",
|
|
"..",
|
|
"operator",
|
|
"target",
|
|
target,
|
|
"release",
|
|
"deps"
|
|
);
|
|
|
|
// Ensure the destination directory exists
|
|
fs.mkdirSync(destPath, { recursive: true });
|
|
|
|
logger.debug(
|
|
await $`docker cp ${containerName}:/artifacts/libpq.so ${path.join(destPath, "libpq.so")}`.text()
|
|
);
|
|
|
|
// Remove container
|
|
logger.debug(await $`docker rm ${containerName}`.text());
|
|
|
|
// Set RUSTFLAGS with the correct library path
|
|
process.env.RUSTFLAGS = `-C link-arg=-Wl,-rpath,$ORIGIN/../release/deps -L ${destPath}`;
|
|
logger.trace(`RUSTFLAGS set to: ${process.env.RUSTFLAGS}`);
|
|
|
|
logger.success(`libpq.so has been copied to ${destPath}`);
|
|
};
|