@pact-toolbox/test
Version:
158 lines (154 loc) • 5.71 kB
JavaScript
import { getNetworkConfig, getSerializableNetworkConfig, isDevNetworkConfig, isPactServerNetworkConfig, resolveConfig } from "@pact-toolbox/config";
import { PactToolboxNetwork } from "@pact-toolbox/network";
import { PactToolboxClient } from "@pact-toolbox/runtime";
import { execAsync, getRandomNetworkPorts, logger } from "@pact-toolbox/utils";
import { join } from "pathe";
import readdir from "tiny-readdir-glob";
import chalk from "chalk";
import ora from "ora";
import { cpus } from "node:os";
//#region src/utils.ts
function getConfigOverrides(configOverrides) {
if (typeof configOverrides === "string") return { defaultNetwork: configOverrides };
return configOverrides || {};
}
function injectNetworkConfig(config) {
const network = getSerializableNetworkConfig(config);
globalThis.__PACT_TOOLBOX_NETWORK_CONFIG__ = network;
}
async function updatePorts(config) {
const ports = await getRandomNetworkPorts();
const network = getNetworkConfig(config);
if (isPactServerNetworkConfig(network)) {
if (network.serverConfig) network.serverConfig.port = ports.service;
}
if (isDevNetworkConfig(network)) {
if (network.containerConfig) network.containerConfig.port = ports.service;
}
return network;
}
//#endregion
//#region src/createTestEnv.ts
async function createPactTestEnv({ network, client, config, configOverrides } = {}) {
logger.pauseLogs();
if (!config) config = await resolveConfig(configOverrides);
if (network) config.defaultNetwork = network;
await updatePorts(config);
injectNetworkConfig(config);
if (!client) client = new PactToolboxClient(config);
const localNetwork = new PactToolboxNetwork(config, {
client,
isDetached: true,
logAccounts: false,
isStateless: true
});
return {
start: async () => localNetwork.start(),
stop: async () => localNetwork.stop(),
restart: async () => localNetwork.restart(),
client,
config
};
}
//#endregion
//#region src/repl.ts
async function runWithConcurrency(items, iteratorFn, concurrency) {
const results = [];
const running = [];
let index = 0;
async function run() {
if (index >= items.length) return;
const i = index++;
const item = items[i];
if (!item) return;
const promise = iteratorFn(item).then((result) => {
results[i] = result;
});
running.push(promise);
if (running.length >= concurrency) await Promise.race(running);
await promise.finally(() => {
const promiseIndex = running.indexOf(promise);
if (promiseIndex > -1) running.splice(promiseIndex, 1);
});
await run();
}
const initialRuns = Array.from({ length: Math.min(concurrency, items.length) }, run);
await Promise.all(initialRuns);
await Promise.all(running);
return results;
}
async function runReplTests(config) {
const startTime = Date.now();
if (!config) config = await resolveConfig();
const spinner = ora("Finding REPL tests").start();
const cwd = join(process.cwd(), config.contractsDir);
const aborter = new AbortController();
const result = await readdir(`**/*.repl`, {
cwd,
depth: 20,
limit: 1e6,
followSymlinks: true,
ignore: ["prelude/**"],
signal: aborter.signal
});
const testFiles = result.files;
if (testFiles.length === 0) {
spinner.succeed("No REPL tests found.");
return;
}
spinner.info(`${testFiles.length} tests found.`);
spinner.start(`Running REPL tests...`);
let completed = 0;
const runTest = async (file) => {
const testStartTime = Date.now();
const cleanedFile = file.replace(cwd, "").substring(1);
try {
await execAsync(`pact -t ${file}`);
const duration = Date.now() - testStartTime;
completed++;
spinner.text = `[${completed}/${testFiles.length}] Running REPL tests... (${cleanedFile})`;
return {
filePath: cleanedFile,
status: "passed",
duration
};
} catch (error) {
completed++;
spinner.text = `[${completed}/${testFiles.length}] Running REPL tests... (${cleanedFile})`;
const duration = Date.now() - testStartTime;
return {
filePath: cleanedFile,
status: "failed",
error,
duration
};
}
};
const concurrency = Math.max(1, cpus().length - 1);
const allResults = await runWithConcurrency(testFiles, runTest, concurrency);
spinner.stop();
const totalDuration = Date.now() - startTime;
const passedTests = allResults.filter((r) => r.status === "passed");
const failedTests = allResults.filter((r) => r.status === "failed");
console.log("\n");
for (const result$1 of allResults) if (result$1.status === "passed") console.log(`${chalk.green.bold("PASS")} ${result$1.filePath} ${chalk.gray(`(${result$1.duration}ms)`)}`);
else console.log(`${chalk.red.bold("FAIL")} ${result$1.filePath} ${chalk.gray(`(${result$1.duration}ms)`)}`);
if (failedTests.length > 0) {
console.log(`\n${chalk.red.bold("Failed Tests:")}`);
for (const test of failedTests) {
console.log(`\n${chalk.red.bold("●")} ${test.filePath}`);
const errorMessage = test.error?.message ?? "Unknown error";
const cleanedError = errorMessage.includes("Command failed:") ? errorMessage.split("Command failed:")[1]?.trim() ?? "" : errorMessage;
console.log(`${chalk.red(cleanedError)}`);
}
}
console.log("\n");
console.log(chalk.bold("REPL Test-runner Summary"));
console.log(` ${chalk.bold("Test Suites:")} ${failedTests.length > 0 ? `${chalk.red.bold(`${failedTests.length} failed`)}, ` : ""}${chalk.green.bold(`${passedTests.length} passed`)}, ${allResults.length} total`);
console.log(` ${chalk.bold("Time:")} ${(totalDuration / 1e3).toFixed(2)}s`);
console.log("\n");
if (failedTests.length > 0) process.exit(1);
}
//#endregion
export { createPactTestEnv, getConfigOverrides, injectNetworkConfig, runReplTests, updatePorts };
//# sourceMappingURL=index.node.mjs.map