Files
monero_c/tests/utils.ts
Mateusz Franik 85770ea6f1 tests: run integration and regression tests on other platforms (#93)
* tests: add script to download test dependencies from fallback mirrors

* tests: use the new download_deps script, run tests on macos

* ci: download proper artifact for macos

* chore: make download_deps script download everything to dir named `monero_c` when ran directly

* tests: await downloading deps

* tests download  proper monero_c version in prepareMoneroC

* tests: fix typos

* tests: add file data for more targets

* tests: print why retrieving tags failed

* chore: change mirror url endpoint from `monero_c` to `download_mirror`

* tests: use cached releases endpoint to prevent ratelimits

* ci: remove brew@1.76 dependency

* tests: fix macos dylib path

* feat!(monero.ts): make `createTransactionMultDest` optionally return `null`

* feat(monero.ts): make `Wallet_reconnectDevice` symbol optional

* tests: don't try to extract file if out already exists

* tests: remove unnecesary directory rm calls

* ci: set regression tests to use canary
2024-12-30 10:39:28 +01:00

206 lines
6.0 KiB
TypeScript
Executable File

import { build$, CommandBuilder } from "jsr:@david/dax";
import { dirname, join } from "jsr:@std/path";
import {
downloadDependencies,
dylibInfos,
getFileInfo,
moneroCliInfo,
Target,
wowneroCliInfo,
} from "./download_deps.ts";
import { loadMoneroDylib, loadWowneroDylib, moneroSymbols, wowneroSymbols } from "../impls/monero.ts/mod.ts";
export type Coin = "monero" | "wownero";
const target = `${Deno.build.os}_${Deno.build.arch}` as const;
export const $ = build$({
commandBuilder: new CommandBuilder()
.printCommand(true)
.stdin("inherit")
.stdout("inherit")
.stderr("inherit"),
});
export const dylibNames = (coin: Coin, version: MoneroCVersion) => ({
linux_x86_64: `${coin}_x86_64-linux-gnu_libwallet2_api_c.so`,
darwin_aarch64: version === "next"
? `${coin}_aarch64-apple-darwin_libwallet2_api_c.dylib`
: `${coin}_aarch64-apple-darwin11_libwallet2_api_c.dylib`,
windows_x86_64: `${coin}_x86_64-w64-mingw32_libwallet2_api_c.dll`,
} as Partial<Record<Target, string>>);
export const moneroTsDylibNames = (coin: Coin) => ({
linux_x86_64: `${coin}_libwallet2_api_c.so`,
darwin_aarch64: `${coin}_aarch64-apple-darwin11_libwallet2_api_c.dylib`,
windows_x86_64: `${coin}_libwallet2_api_c.dll`,
} as Partial<Record<Target, string>>);
export function loadDylib(coin: Coin, version: MoneroCVersion) {
const dylibName = moneroTsDylibNames(coin)[target]!;
if (coin === "monero") {
const dylib = Deno.dlopen(`tests/dependencies/libs/${version}/${dylibName}`, moneroSymbols);
loadMoneroDylib(dylib);
return dylib;
} else {
const dylib = Deno.dlopen(`tests/dependencies/libs/${version}/${dylibName}`, wowneroSymbols);
loadWowneroDylib(dylib);
return dylib;
}
}
async function exists(path: string): Promise<boolean> {
try {
await Deno.stat(path);
return true;
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
return false;
}
throw error;
}
}
export async function extract(path: string, out: string) {
const outDir = out.endsWith("/") ? out : dirname(out);
await Deno.mkdir(outDir, { recursive: true });
if (path.endsWith(".tar.bz2")) {
let args = `-C ${dirname(out)}`;
if (outDir === out) {
args = `-C ${out} --strip-components=1`;
}
await $.raw`tar -xf ${path} ${args}`;
} else if (path.endsWith(".zip")) {
await $.raw`unzip ${path} -nu -d ${outDir}`;
} else if (path.endsWith(".xz")) {
await $.raw`xz -kd ${path}`;
await Deno.rename(path.slice(0, -3), out);
} else {
throw new Error("Unsupported archive file for:" + path);
}
}
export async function prepareMoneroCli() {
await downloadDependencies(moneroCliInfo);
const path = join("./tests/dependencies", moneroCliInfo.outDir ?? "", getFileInfo(moneroCliInfo).name);
await extract(path, "./tests/dependencies/monero-cli/");
}
export async function prepareWowneroCli() {
await downloadDependencies(wowneroCliInfo);
const path = join("./tests/dependencies", wowneroCliInfo.outDir ?? "", getFileInfo(wowneroCliInfo).name);
await extract(path, "./tests/dependencies/wownero-cli/");
}
export function prepareCli(coin: Coin) {
if (coin === "wownero") {
return prepareWowneroCli();
}
return prepareMoneroCli();
}
interface WalletInfo {
path: string;
password: string;
address: string;
secretSpendKey: string;
publicSpendKey: string;
secretViewKey: string;
publicViewKey: string;
}
export async function createWalletViaCli(
coin: Coin,
name: string,
password: string,
): Promise<WalletInfo> {
const path = `./tests/wallets/${name}`;
const cliPath = `./tests/dependencies/${coin}-cli/${coin}-wallet-cli`;
await $
.raw`${cliPath} --generate-new-wallet ${path} --password ${password} --mnemonic-language English --command exit`
.stdout("null");
const address = (await $.raw`${cliPath} --wallet-file ${path} --password ${password} --command address`
.stdinText(`${password}\n`)
.lines())
.at(-1)!
.split(/\s+/)[1];
const retrieveKeys = (lines: string[]) =>
lines.slice(-2)
.map((line) => line.split(": ")[1]);
const [secretSpendKey, publicSpendKey] = retrieveKeys(
await $.raw`${cliPath} --wallet-file ${path} --password ${password} --command spendkey`
.stdinText(`${password}\n`)
.lines(),
);
const [secretViewKey, publicViewKey] = retrieveKeys(
await $.raw`${cliPath} --wallet-file ${path} --password ${password} --command viewkey`
.stdinText(`${password}\n`)
.lines(),
);
return {
path,
password,
address,
secretSpendKey,
publicSpendKey,
secretViewKey,
publicViewKey,
};
}
// deno-lint-ignore ban-types
export type MoneroCVersion = "next" | (string & {});
export async function getMoneroCTags(): Promise<string[]> {
const response = await fetch("https://static.mrcyjanek.net/monero_c/release.php");
if (!response.ok) {
throw new Error(`Could not receive monero_c release tags: ${await response.text()}`);
}
const json = await response.json() as { tag_name: string }[];
return json.map(({ tag_name }) => tag_name);
}
export async function prepareMoneroC(coin: Coin, version: MoneroCVersion) {
const dylibName = dylibNames(coin, version)[target];
const moneroTsDylibName = moneroTsDylibNames(coin)[target];
if (!dylibName || !moneroTsDylibName) {
throw new Error(`Missing dylib name value for target: ${target}`);
}
const releaseDylibName = dylibName.slice(`${coin}_`.length);
if (version === "next") {
const outFileDir = `./tests/dependencies/libs/${version}/${moneroTsDylibName}`;
if (await exists(outFileDir)) {
return;
}
await extract(`./release/${coin}/${releaseDylibName}.xz`, outFileDir);
} else {
const outFileDir = `./tests/dependencies/libs/${version}/${moneroTsDylibName}`;
if (await exists(outFileDir)) {
return;
}
const downloadInfo = dylibInfos[coin].find((info) => info.outDir?.endsWith(version));
if (downloadInfo) {
await downloadDependencies(downloadInfo);
}
await extract(`./tests/dependencies/libs/${version}/${dylibName}.xz`, outFileDir);
}
}