Skip to content

Commit

Permalink
fix: make each test run concurrently
Browse files Browse the repository at this point in the history
  • Loading branch information
VGau committed Oct 10, 2024
1 parent edd381a commit c091179
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 286 deletions.
1 change: 1 addition & 0 deletions e2e/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const config: Config = {
globalSetup: "./config/jest/global-setup.ts",
maxWorkers: "50%",
testTimeout: 3 * 60 * 1000,
workerThreads: true,
};

export default config;
123 changes: 55 additions & 68 deletions e2e/src/config/tests-config/accounts/account-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ interface IAccountManager {
whaleAccount(accIndex?: number): Wallet;
generateAccount(initialBalanceWei?: bigint): Promise<Wallet>;
generateAccounts(numberOfAccounts: number, initialBalanceWei?: bigint): Promise<Wallet[]>;
getTransactionManager(account: Account): Wallet;
getWallet(account: Account): Wallet;
}

function getTransactionManager(provider: Provider, privateKey: string): Wallet {
function getWallet(provider: Provider, privateKey: string): Wallet {
if (!privateKey.startsWith("0x")) {
privateKey = "0x" + privateKey;
}
Expand All @@ -26,55 +26,43 @@ abstract class AccountManager implements IAccountManager {
protected readonly chainId: number;
protected readonly whaleAccounts: Account[];
protected provider: Provider;
protected txManagers: Wallet[];
private whaleAccountsInUse: Set<string>;
protected accountWallets: Wallet[];

constructor(provider: Provider, whaleAccounts: Account[], chainId: number) {
this.provider = provider;
this.whaleAccounts = whaleAccounts;
this.chainId = chainId;
this.txManagers = this.whaleAccounts.map((account) => getTransactionManager(this.provider, account.privateKey));
this.whaleAccountsInUse = new Set();
this.accountWallets = this.whaleAccounts.map((account) => getWallet(this.provider, account.privateKey));
}

selectWhaleAccount(accIndex?: number): { account: Account; txManager: Wallet } {
selectWhaleAccount(accIndex?: number): { account: Account; accountWallet: Wallet } {
if (accIndex) {
return { account: this.whaleAccounts[accIndex], txManager: this.txManagers[accIndex] };
return { account: this.whaleAccounts[accIndex], accountWallet: this.accountWallets[accIndex] };
}
const workerIdEnv = process.env.JEST_WORKER_ID || "1";
const workerId = parseInt(workerIdEnv, 10) - 1;

const accountIndex = workerId % this.whaleAccounts.length;
let whaleAccount = this.whaleAccounts[accountIndex];

if (this.whaleAccountsInUse.has(whaleAccount.address)) {
const availableWhaleAccounts = this.whaleAccounts.filter(
(account) => !this.whaleAccountsInUse.has(account.address),
);

if (availableWhaleAccounts.length === 0) {
throw new Error("No available whale accounts");
}

whaleAccount = availableWhaleAccounts[0];
}

const whaleTxManager = this.txManagers[this.whaleAccounts.indexOf(whaleAccount)];
this.whaleAccountsInUse.add(whaleAccount.address);
return { account: whaleAccount, txManager: whaleTxManager };
const accountIndex = workerId;
const whaleAccount = this.whaleAccounts[accountIndex];
const whaleTxManager = this.accountWallets[this.whaleAccounts.indexOf(whaleAccount)];
return { account: whaleAccount, accountWallet: whaleTxManager };
}

whaleAccount(accIndex?: number): Wallet {
return this.selectWhaleAccount(accIndex).txManager;
return this.selectWhaleAccount(accIndex).accountWallet;
}

async generateAccount(initialBalanceWei = etherToWei("10")): Promise<Wallet> {
const accounts = await this.generateAccounts(1, initialBalanceWei);
return this.getTransactionManager(accounts[0]);
return this.getWallet(accounts[0]);
}

async generateAccounts(numberOfAccounts: number, initialBalanceWei = etherToWei("10")): Promise<Wallet[]> {
const { account: whaleAccount, txManager: whaleTxManager } = this.selectWhaleAccount();
async generateAccounts(
numberOfAccounts: number,
initialBalanceWei = etherToWei("10"),
retryDelayMs = 3_000,
): Promise<Wallet[]> {
const { account: whaleAccount, accountWallet: whaleAccountWallet } = this.selectWhaleAccount();

console.log(
`Generating accounts: chainId=${this.chainId} numberOfAccounts=${numberOfAccounts} whaleAccount=${whaleAccount.address}`,
Expand All @@ -88,49 +76,48 @@ abstract class AccountManager implements IAccountManager {
const newAccount = new Account(randomPrivKey, ethers.computeAddress(randomPrivKey));
accounts.push(newAccount);

try {
const tx = {
to: newAccount.address,
value: initialBalanceWei,
gasPrice: ethers.parseUnits("300", "gwei"),
gasLimit: 21000n,
};
const transactionResponse = await whaleTxManager.sendTransaction(tx);
console.log(
`Waiting for account funding: newAccount=${newAccount.address} txHash=${transactionResponse.hash} whaleAccount=${whaleAccount.address}`,
);
const receipt = await transactionResponse.wait();

if (!receipt) {
throw new Error(`Transaction failed to be mined`);
}

if (receipt.status !== 1) {
throw new Error(`Transaction failed with status ${receipt.status}`);
let success = false;
while (!success) {
try {
const tx = {
to: newAccount.address,
value: initialBalanceWei,
gasPrice: ethers.parseUnits("300", "gwei"),
gasLimit: 21000n,
};
const transactionResponse = await whaleAccountWallet.sendTransaction(tx);
console.log(
`Waiting for account funding: newAccount=${newAccount.address} txHash=${transactionResponse.hash} whaleAccount=${whaleAccount.address}`,
);
const receipt = await transactionResponse.wait();

if (!receipt) {
throw new Error(`Transaction failed to be mined`);
}

if (receipt.status !== 1) {
throw new Error(`Transaction failed with status ${receipt.status}`);
}
console.log(
`Account funded: newAccount=${newAccount.address} balance=${(
await this.provider.getBalance(newAccount.address)
).toString()} wei`,
);
success = true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
console.log(
`Failed to send funds from accAddress=${whaleAccount.address}. Retrying funding in ${retryDelayMs}ms...`,
);
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
}
console.log(
`Account funded: newAccount=${newAccount.address} balance=${(
await this.provider.getBalance(newAccount.address)
).toString()} wei`,
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
const whaleBalance = await this.provider.getBalance(whaleAccount.address);
throw new Error(
`Failed to send funds from accAddress=${whaleAccount.address}, accBalance=${whaleBalance.toString()}, accPrivKey=0x...${whaleAccount.privateKey.slice(
-8,
)}, error: ${error.message}`,
);
} finally {
this.whaleAccountsInUse.delete(whaleAccount.address);
}
}

return accounts.map((account) => getTransactionManager(this.provider, account.privateKey));
return accounts.map((account) => getWallet(this.provider, account.privateKey));
}

getTransactionManager(account: Account): Wallet {
return getTransactionManager(this.provider, account.privateKey);
getWallet(account: Account): Wallet {
return getWallet(this.provider, account.privateKey);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { Provider } from "ethers";
import Account from "./account";
import { AccountManager } from "./account-manager";

class TestnetAccountManager extends AccountManager {
class EnvironmentBasedAccountManager extends AccountManager {
constructor(provider: Provider, whaleAccounts: Account[], chainId: number) {
super(provider, whaleAccounts, chainId);
}
}

export { TestnetAccountManager };
export { EnvironmentBasedAccountManager };
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function readGenesisFileAccounts(genesisJson: GenesisJson): Account[] {
return accounts;
}

class LocalAccountManager extends AccountManager {
class GenesisBasedAccountManager extends AccountManager {
constructor(provider: Provider, genesisFilePath: string) {
const genesisJson = readJsonFile(genesisFilePath);
const genesis = genesisJson as GenesisJson;
Expand All @@ -38,4 +38,4 @@ class LocalAccountManager extends AccountManager {
}
}

export { LocalAccountManager };
export { GenesisBasedAccountManager };
6 changes: 3 additions & 3 deletions e2e/src/config/tests-config/environments/dev.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ethers } from "ethers";
import { TestnetAccountManager } from "../accounts/testnet-account-manager";
import { EnvironmentBasedAccountManager } from "../accounts/environment-based-account-manager";
import { Config } from "../types";
import Account from "../accounts/account";

Expand All @@ -26,7 +26,7 @@ const config: Config = {
rpcUrl: L1_RPC_URL,
chainId: L1_CHAIN_ID,
lineaRollupAddress: "0x2A5CDCfc38856e2590E9Bd32F54Fa348e5De5f48",
accountManager: new TestnetAccountManager(
accountManager: new EnvironmentBasedAccountManager(
new ethers.JsonRpcProvider(L1_RPC_URL.toString()),
L1_WHALE_ACCOUNTS,
L1_CHAIN_ID,
Expand All @@ -37,7 +37,7 @@ const config: Config = {
rpcUrl: L2_RPC_URL,
chainId: L2_CHAIN_ID,
l2MessageServiceAddress: "0x33bf916373159A8c1b54b025202517BfDbB7863D",
accountManager: new TestnetAccountManager(
accountManager: new EnvironmentBasedAccountManager(
new ethers.JsonRpcProvider(L2_RPC_URL.toString()),
L2_WHALE_ACCOUNTS,
L2_CHAIN_ID,
Expand Down
6 changes: 3 additions & 3 deletions e2e/src/config/tests-config/environments/local.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ethers } from "ethers";
import path from "path";
import { LocalAccountManager } from "../accounts/local-account-manager";
import { GenesisBasedAccountManager } from "../accounts/genesis-based-account-manager";
import { Config } from "../types";

const L1_RPC_URL = new URL("http://localhost:8445");
Expand All @@ -15,7 +15,7 @@ const config: Config = {
chainId: 31648428,
lineaRollupAddress: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
dummyContractAddress: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9",
accountManager: new LocalAccountManager(
accountManager: new GenesisBasedAccountManager(
new ethers.JsonRpcProvider(L1_RPC_URL.toString()),
path.resolve(
process.env.LOCAL_L1_GENESIS ||
Expand All @@ -28,7 +28,7 @@ const config: Config = {
chainId: 1337,
l2MessageServiceAddress: "0xe537D669CA013d86EBeF1D64e40fC74CADC91987",
dummyContractAddress: "0x2f6dAaF8A81AB675fbD37Ca6Ed5b72cf86237453",
accountManager: new LocalAccountManager(
accountManager: new GenesisBasedAccountManager(
new ethers.JsonRpcProvider(L2_RPC_URL.toString()),
path.resolve(
process.env.LOCAL_L2_GENESIS ||
Expand Down
6 changes: 3 additions & 3 deletions e2e/src/config/tests-config/environments/sepolia.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ethers } from "ethers";
import { TestnetAccountManager } from "../accounts/testnet-account-manager";
import { EnvironmentBasedAccountManager } from "../accounts/environment-based-account-manager";
import Account from "../accounts/account";
import { Config } from "../types";

Expand All @@ -25,7 +25,7 @@ const config: Config = {
rpcUrl: L1_RPC_URL,
chainId: L1_CHAIN_ID,
lineaRollupAddress: "0xB218f8A4Bc926cF1cA7b3423c154a0D627Bdb7E5",
accountManager: new TestnetAccountManager(
accountManager: new EnvironmentBasedAccountManager(
new ethers.JsonRpcProvider(L1_RPC_URL.toString()),
L1_WHALE_ACCOUNTS,
L1_CHAIN_ID,
Expand All @@ -36,7 +36,7 @@ const config: Config = {
rpcUrl: L2_RPC_URL,
chainId: L2_CHAIN_ID,
l2MessageServiceAddress: "0x971e727e956690b9957be6d51Ec16E73AcAC83A7",
accountManager: new TestnetAccountManager(
accountManager: new EnvironmentBasedAccountManager(
new ethers.JsonRpcProvider(L2_RPC_URL.toString()),
L2_WHALE_ACCOUNTS,
L2_CHAIN_ID,
Expand Down
Loading

0 comments on commit c091179

Please sign in to comment.