summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorMateusz Franik <47059999+Im-Beast@users.noreply.github.com>2024-10-16 07:55:11 +0200
committerGitHub <noreply@github.com>2024-10-16 07:55:11 +0200
commitfd7bb6ae1c27ffe5d41f3a818ee9034d9bb76138 (patch)
tree01e57a1c483370a3f023ed27d401069502694396 /tests
parent44fd5e17bbce52caf681850ac79f463d9ce6bb31 (diff)
feat: wownero typescript bindings, regression tests (#71)
* regression tests * ci: move regression_check to full_check workflow, reuse artifact build * feat: support wownero in monero.ts bindings * ci: test wownero regressions as well * extract wownero-cli as wownero * actually load wownero when specified * fix: commitUR not being a symbol in wownero
Diffstat (limited to 'tests')
-rwxr-xr-xtests/compare.ts80
-rwxr-xr-xtests/deno.jsonc5
-rwxr-xr-xtests/deno.lock90
-rwxr-xr-xtests/regression.test.ts39
-rwxr-xr-xtests/utils.ts131
5 files changed, 345 insertions, 0 deletions
diff --git a/tests/compare.ts b/tests/compare.ts
new file mode 100755
index 0000000..d09bdd9
--- /dev/null
+++ b/tests/compare.ts
@@ -0,0 +1,80 @@
+import { moneroSymbols as symbols, type MoneroTsDylib, type WowneroTsDylib } from "../impls/monero.ts/src/symbols.ts";
+import { loadMoneroDylib, loadWowneroDylib } from "../impls/monero.ts/src/bindings.ts";
+import { Wallet, WalletManager } from "../impls/monero.ts/mod.ts";
+import { readCString } from "../impls/monero.ts/src/utils.ts";
+import { assertEquals } from "jsr:@std/assert";
+
+const coin = Deno.args[0] as "monero" | "wownero";
+const version = Deno.args[1];
+const walletInfo = JSON.parse(Deno.args[2]);
+
+const moneroSymbols = {
+ ...symbols,
+
+ "MONERO_Wallet_secretViewKey": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_publicViewKey": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+
+ "MONERO_Wallet_secretSpendKey": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_publicSpendKey": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+} as const;
+
+type ReplaceMonero<T extends string> = T extends `MONERO${infer Y}` ? `WOWNERO${Y}` : never;
+type WowneroSymbols = { [Key in keyof typeof moneroSymbols as ReplaceMonero<Key>]: (typeof moneroSymbols)[Key] };
+const wowneroSymbols = Object.fromEntries(
+ Object.entries(moneroSymbols).map(([key, value]) => [key.replace("MONERO", "WOWNERO"), value]),
+) as WowneroSymbols;
+
+let getKey: (wallet: Wallet, type: `${"secret" | "public"}${"Spend" | "View"}Key`) => Promise<string | null>;
+
+if (coin === "monero") {
+ const dylib = Deno.dlopen(`tests/libs/${version}/monero_libwallet2_api_c.so`, moneroSymbols);
+ loadMoneroDylib(dylib as MoneroTsDylib);
+
+ getKey = async (wallet, type) =>
+ await readCString(await dylib.symbols[`MONERO_Wallet_${type}` as const](wallet.getPointer()));
+} else {
+ const dylib = Deno.dlopen(`tests/libs/${version}/wownero_libwallet2_api_c.so`, wowneroSymbols);
+ loadWowneroDylib(dylib as WowneroTsDylib);
+
+ getKey = async (wallet, type) =>
+ await readCString(
+ await dylib.symbols[`WOWNERO_Wallet_${type}` as const](wallet.getPointer()),
+ );
+}
+
+const walletManager = await WalletManager.new();
+const wallet = await Wallet.open(walletManager, walletInfo.path, walletInfo.password);
+
+assertEquals(await wallet.address(), walletInfo.address);
+
+assertEquals(await getKey(wallet, "publicSpendKey"), walletInfo.publicSpendKey);
+assertEquals(await getKey(wallet, "secretSpendKey"), walletInfo.secretSpendKey);
+
+assertEquals(await getKey(wallet, "publicViewKey"), walletInfo.publicViewKey);
+assertEquals(await getKey(wallet, "secretViewKey"), walletInfo.secretViewKey);
+
+await wallet.store(walletInfo.path);
diff --git a/tests/deno.jsonc b/tests/deno.jsonc
new file mode 100755
index 0000000..a7b75ec
--- /dev/null
+++ b/tests/deno.jsonc
@@ -0,0 +1,5 @@
+{
+ "fmt": {
+ "lineWidth": 120
+ }
+}
diff --git a/tests/deno.lock b/tests/deno.lock
new file mode 100755
index 0000000..02d189f
--- /dev/null
+++ b/tests/deno.lock
@@ -0,0 +1,90 @@
+{
+ "version": "4",
+ "specifiers": {
+ "jsr:@david/dax@*": "0.42.0",
+ "jsr:@david/path@0.2": "0.2.0",
+ "jsr:@david/which@~0.4.1": "0.4.1",
+ "jsr:@std/assert@*": "0.221.0",
+ "jsr:@std/assert@0.221": "0.221.0",
+ "jsr:@std/bytes@0.221": "0.221.0",
+ "jsr:@std/fmt@0.221": "0.221.0",
+ "jsr:@std/fmt@1": "1.0.2",
+ "jsr:@std/fs@1": "1.0.4",
+ "jsr:@std/io@0.221": "0.221.0",
+ "jsr:@std/path@1": "1.0.6",
+ "jsr:@std/path@^1.0.6": "1.0.6",
+ "jsr:@std/streams@0.221": "0.221.0",
+ "jsr:@std/streams@^1.0.7": "1.0.7",
+ "jsr:@std/tar@*": "0.1.2"
+ },
+ "jsr": {
+ "@david/dax@0.42.0": {
+ "integrity": "0c547c9a20577a6072b90def194c159c9ddab82280285ebfd8268a4ebefbd80b",
+ "dependencies": [
+ "jsr:@david/path",
+ "jsr:@david/which",
+ "jsr:@std/fmt@1",
+ "jsr:@std/fs",
+ "jsr:@std/io",
+ "jsr:@std/path@1",
+ "jsr:@std/streams@0.221"
+ ]
+ },
+ "@david/path@0.2.0": {
+ "integrity": "f2d7aa7f02ce5a55e27c09f9f1381794acb09d328f8d3c8a2e3ab3ffc294dccd",
+ "dependencies": [
+ "jsr:@std/fs",
+ "jsr:@std/path@1"
+ ]
+ },
+ "@david/which@0.4.1": {
+ "integrity": "896a682b111f92ab866cc70c5b4afab2f5899d2f9bde31ed00203b9c250f225e"
+ },
+ "@std/assert@0.221.0": {
+ "integrity": "a5f1aa6e7909dbea271754fd4ab3f4e687aeff4873b4cef9a320af813adb489a",
+ "dependencies": [
+ "jsr:@std/fmt@0.221"
+ ]
+ },
+ "@std/bytes@0.221.0": {
+ "integrity": "64a047011cf833890a4a2ab7293ac55a1b4f5a050624ebc6a0159c357de91966"
+ },
+ "@std/fmt@0.221.0": {
+ "integrity": "379fed69bdd9731110f26b9085aeb740606b20428ce6af31ef6bd45ef8efa62a"
+ },
+ "@std/fmt@1.0.2": {
+ "integrity": "87e9dfcdd3ca7c066e0c3c657c1f987c82888eb8103a3a3baa62684ffeb0f7a7"
+ },
+ "@std/fs@1.0.4": {
+ "integrity": "2907d32d8d1d9e540588fd5fe0ec21ee638134bd51df327ad4e443aaef07123c",
+ "dependencies": [
+ "jsr:@std/path@^1.0.6"
+ ]
+ },
+ "@std/io@0.221.0": {
+ "integrity": "faf7f8700d46ab527fa05cc6167f4b97701a06c413024431c6b4d207caa010da",
+ "dependencies": [
+ "jsr:@std/assert@0.221",
+ "jsr:@std/bytes"
+ ]
+ },
+ "@std/path@1.0.6": {
+ "integrity": "ab2c55f902b380cf28e0eec501b4906e4c1960d13f00e11cfbcd21de15f18fed"
+ },
+ "@std/streams@0.221.0": {
+ "integrity": "47f2f74634b47449277c0ee79fe878da4424b66bd8975c032e3afdca88986e61",
+ "dependencies": [
+ "jsr:@std/io"
+ ]
+ },
+ "@std/streams@1.0.7": {
+ "integrity": "1a93917ca0c58c01b2bfb93647189229b1702677f169b6fb61ad6241cd2e499b"
+ },
+ "@std/tar@0.1.2": {
+ "integrity": "98183102395decd6268253996177804f818580ef547a25b81da0e7cc334db708",
+ "dependencies": [
+ "jsr:@std/streams@^1.0.7"
+ ]
+ }
+ }
+}
diff --git a/tests/regression.test.ts b/tests/regression.test.ts
new file mode 100755
index 0000000..82a9f95
--- /dev/null
+++ b/tests/regression.test.ts
@@ -0,0 +1,39 @@
+import { $, createWalletViaCli, downloadCli, getMoneroC, getMoneroCTags } from "./utils.ts";
+
+const coin = Deno.env.get("COIN");
+if (coin !== "monero" && coin !== "wownero") {
+ throw new Error("COIN env var invalid or missing");
+}
+
+Deno.test(`Regression tests (${coin})`, async (t) => {
+ await Deno.remove("./tests/wallets", { recursive: true }).catch(() => {});
+ await Deno.mkdir("./tests/wallets", { recursive: true });
+
+ const tags = await getMoneroCTags();
+ const latestTag = tags[0];
+ await Promise.all([getMoneroC(coin, "next"), await getMoneroC(coin, latestTag), downloadCli(coin)]);
+
+ await t.step("Simple (next, latest, next)", async () => {
+ const walletInfo = await createWalletViaCli(coin, "dog", "sobaka");
+
+ for (const version of ["next", latestTag, "next"]) {
+ await $`deno run -A ./tests/compare.ts ${coin} ${version} ${JSON.stringify(walletInfo)}`;
+ }
+ });
+
+ await t.step("All releases sequentially (all tags in the release order, next)", async () => {
+ tags.unshift("next");
+
+ const walletInfo = await createWalletViaCli(coin, "cat", "koshka");
+
+ for (const version of tags.toReversed()) {
+ if (version !== "next" && version !== tags[0]) await getMoneroC(coin, version);
+ await $`deno run -A ./tests/compare.ts ${coin} ${version} ${JSON.stringify(walletInfo)}`;
+ }
+
+ await Deno.remove("./tests/wallets", { recursive: true }).catch(() => {});
+ });
+
+ await Deno.remove("./tests/wallets", { recursive: true }).catch(() => {});
+ await Deno.remove("./tests/libs", { recursive: true }).catch(() => {});
+});
diff --git a/tests/utils.ts b/tests/utils.ts
new file mode 100755
index 0000000..028e0ff
--- /dev/null
+++ b/tests/utils.ts
@@ -0,0 +1,131 @@
+import { build$, CommandBuilder } from "jsr:@david/dax";
+
+export const $ = build$({
+ commandBuilder: new CommandBuilder()
+ .printCommand(true)
+ .stdin("inherit")
+ .stdout("inherit")
+ .stderr("inherit"),
+});
+
+type Coin = "monero" | "wownero";
+
+export async function downloadMoneroCli() {
+ const MONERO_CLI_FILE_NAME = "monero-linux-x64-v0.18.3.4";
+ const MONERO_WALLET_CLI_URL = `https://downloads.getmonero.org/cli/${MONERO_CLI_FILE_NAME}.tar.bz2`;
+
+ await $`wget ${MONERO_WALLET_CLI_URL}`;
+ await $
+ .raw`tar -xvf ${MONERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=monero-cli --strip-components=1 -C tests`;
+ await $.raw`rm ${MONERO_CLI_FILE_NAME}.tar.bz2`;
+}
+
+export async function downloadWowneroCli() {
+ const WOWNERO_CLI_FILE_NAME = "wownero-x86_64-linux-gnu-59db3fe8d";
+ const WOWNERO_WALLET_CLI_URL =
+ `https://codeberg.org/wownero/wownero/releases/download/v0.11.2.0/wownero-x86_64-linux-gnu-59db3fe8d.tar.bz2`;
+
+ await $`wget ${WOWNERO_WALLET_CLI_URL}`;
+ await $
+ .raw`tar -xvf ${WOWNERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=wownero-cli --strip-components=1 -C tests`;
+ await $.raw`rm ${WOWNERO_CLI_FILE_NAME}.tar.bz2`;
+}
+
+export function downloadCli(coin: Coin) {
+ if (coin === "wownero") {
+ return downloadWowneroCli();
+ }
+ return downloadMoneroCli();
+}
+
+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/${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[]> {
+ return ((
+ await (await fetch(
+ "https://api.github.com/repos/MrCyjanek/monero_c/releases",
+ )).json()
+ ) as { tag_name: string }[])
+ .map(({ tag_name }) => tag_name);
+}
+export async function getMoneroC(coin: Coin, version: MoneroCVersion) {
+ const dylibName = `${coin}_x86_64-linux-gnu_libwallet2_api_c.so`;
+ const endpointDylibName = `${coin}_libwallet2_api_c.so`;
+ const releaseDylibName = dylibName.slice(`${coin}_`.length);
+
+ if (version === "next") {
+ await $.raw`xz -kd release/${coin}/${releaseDylibName}.xz`;
+ await $`mkdir -p tests/libs/next`;
+ await $`mv release/${coin}/${releaseDylibName} tests/libs/next/${endpointDylibName}`;
+ } else {
+ const downloadUrl = `https://github.com/MrCyjaneK/monero_c/releases/download/${version}/${dylibName}.xz`;
+
+ const file = await Deno.open(`./tests/${dylibName}.xz`, {
+ create: true,
+ write: true,
+ });
+ file.write(await (await fetch(downloadUrl)).bytes());
+ file.close();
+
+ await $.raw`xz -d ./tests/${dylibName}.xz`;
+ await $.raw`mkdir -p ./tests/libs/${version}`;
+ await $
+ .raw`mv ./tests/${dylibName} ./tests/libs/${version}/${endpointDylibName}`;
+ }
+}