feat: init tests with clarinet sdk and vitest

This commit is contained in:
Hugo Caillard
2023-08-17 16:05:26 +02:00
parent 629613ad92
commit dfb3898866
11 changed files with 1193 additions and 539 deletions

View File

@@ -1,5 +1,4 @@
{
"deno.enable": true,
"files.eol": "\n"
"deno.enable": false,
"files.eol": "\n"
}

1412
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,18 +7,15 @@
"test": "unit-tests"
},
"scripts": {
"test": "node --test",
"coverage": "rimraf ./lcov.info; node --test "
"test": "vitest run -c ./node_modules/obscurity-sdk/vitest.config.js",
"test:coverage": "vitest run -c ./node_modules/obscurity-sdk/vitest.config.js -- --clarity-coverage true"
},
"author": "",
"license": "ISC",
"type": "module",
"dependencies": {
"@stacks/transactions": "^6.5.4",
"obscurity-sdk": "^0.0.5"
},
"devDependencies": {
"@types/node": "^20.4.4",
"rimraf": "^5.0.1"
"obscurity-sdk": "^0.4.0-rc6",
"vitest": "^0.34.1"
}
}

View File

@@ -1,69 +1,56 @@
import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v1.6.0/index.ts';
import { assertEquals } from 'https://deno.land/std@0.170.0/testing/asserts.ts';
import { Clarinet, Tx, Chain, Account, types } from "https://deno.land/x/clarinet@v1.6.0/index.ts";
import { assertEquals } from "https://deno.land/std@0.170.0/testing/asserts.ts";
import { hexToBuffer } from "https://deno.land/x/hextools@v1.0.0/mod.ts";
import { mainnet_valid_guardians_set_upgrades } from "./constants.ts";
export const executeGuardiansRotations = (chain: Chain, account: Account) => {
const vaaRotation1 = hexToBuffer(mainnet_valid_guardians_set_upgrades[0].vaa);
let publicKeysRotation1 = [];
for (let key of mainnet_valid_guardians_set_upgrades[0].keys) {
publicKeysRotation1.push(types.buff(hexToBuffer(key)));
}
const vaaRotation1 = hexToBuffer(mainnet_valid_guardians_set_upgrades[0].vaa);
let publicKeysRotation1 = [];
for (let key of mainnet_valid_guardians_set_upgrades[0].keys) {
publicKeysRotation1.push(types.buff(hexToBuffer(key)));
}
const vaaRotation2 = hexToBuffer(mainnet_valid_guardians_set_upgrades[1].vaa);
let publicKeysRotation2 = [];
for (let key of mainnet_valid_guardians_set_upgrades[1].keys) {
publicKeysRotation2.push(types.buff(hexToBuffer(key)));
}
const vaaRotation2 = hexToBuffer(mainnet_valid_guardians_set_upgrades[1].vaa);
let publicKeysRotation2 = [];
for (let key of mainnet_valid_guardians_set_upgrades[1].keys) {
publicKeysRotation2.push(types.buff(hexToBuffer(key)));
}
const vaaRotation3 = hexToBuffer(mainnet_valid_guardians_set_upgrades[2].vaa);
let publicKeysRotation3 = [];
for (let key of mainnet_valid_guardians_set_upgrades[2].keys) {
publicKeysRotation3.push(types.buff(hexToBuffer(key)));
}
const vaaRotation3 = hexToBuffer(mainnet_valid_guardians_set_upgrades[2].vaa);
let publicKeysRotation3 = [];
for (let key of mainnet_valid_guardians_set_upgrades[2].keys) {
publicKeysRotation3.push(types.buff(hexToBuffer(key)));
}
let block = chain.mineBlock([
Tx.contractCall(
"wormhole-core-v1",
"update-guardians-set",
[
types.buff(vaaRotation1),
types.list(
publicKeysRotation1
)
],
account.address),
Tx.contractCall(
"wormhole-core-v1",
"update-guardians-set",
[
types.buff(vaaRotation2),
types.list(
publicKeysRotation2
)
],
account.address),
Tx.contractCall(
"wormhole-core-v1",
"update-guardians-set",
[
types.buff(vaaRotation3),
types.list(
publicKeysRotation3
)
],
account.address),
]);
let block = chain.mineBlock([
Tx.contractCall(
"wormhole-core-dev-preview-1",
"update-guardians-set",
[types.buff(vaaRotation1), types.list(publicKeysRotation1)],
account.address
),
Tx.contractCall(
"wormhole-core-dev-preview-1",
"update-guardians-set",
[types.buff(vaaRotation2), types.list(publicKeysRotation2)],
account.address
),
Tx.contractCall(
"wormhole-core-dev-preview-1",
"update-guardians-set",
[types.buff(vaaRotation3), types.list(publicKeysRotation3)],
account.address
),
]);
// assert: review returned data, contract state, and other requirements
assertEquals(block.receipts.length, 3);
const rotation1 = block.receipts[0].result;
rotation1.expectOk()
// assert: review returned data, contract state, and other requirements
assertEquals(block.receipts.length, 3);
const rotation1 = block.receipts[0].result;
rotation1.expectOk();
const rotation2 = block.receipts[1].result;
rotation2.expectOk()
const rotation2 = block.receipts[1].result;
rotation2.expectOk();
const rotation3 = block.receipts[2].result;
rotation3.expectOk()
const rotation3 = block.receipts[2].result;
rotation3.expectOk();
};

View File

@@ -1,57 +1,61 @@
import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v1.6.0/index.ts';
import { assertEquals, assertObjectMatch, assertArrayIncludes } from 'https://deno.land/std@0.170.0/testing/asserts.ts';
import { Clarinet, Tx, Chain, Account, types } from "https://deno.land/x/clarinet@v1.6.0/index.ts";
import {
assertEquals,
assertObjectMatch,
assertArrayIncludes,
} from "https://deno.land/std@0.170.0/testing/asserts.ts";
import { hexToBuffer } from "https://deno.land/x/hextools@v1.0.0/mod.ts";
import { mainnet_valid_pfs } from "./constants.ts";
import { executeGuardiansRotations } from "./helpers.ts";
Clarinet.test({
name: "Ensure that valid price attestations can be ingested and recorded",
fn(chain: Chain, accounts: Map<string, Account>) {
// arrange: set up the chain, state, and other required elements
const wallet_1 = accounts.get("wallet_1")!;
executeGuardiansRotations(chain, wallet_1);
name: "Ensure that valid price attestations can be ingested and recorded",
fn(chain: Chain, accounts: Map<string, Account>) {
// arrange: set up the chain, state, and other required elements
const wallet_1 = accounts.get("wallet_1")!;
executeGuardiansRotations(chain, wallet_1);
const vaaBytes = hexToBuffer(mainnet_valid_pfs[0]);
const vaaBytes = hexToBuffer(mainnet_valid_pfs[0]);
const block = chain.mineBlock([
Tx.contractCall(
"pyth-oracle-v1",
"update-prices-feeds",
[
types.list([types.buff(vaaBytes)])
],
wallet_1.address)
]);
const block = chain.mineBlock([
Tx.contractCall(
"pyth-oracle-dev-preview-1",
"update-prices-feeds",
[types.list([types.buff(vaaBytes)])],
wallet_1.address
),
]);
assertEquals(block.receipts.length, 1);
assertEquals(block.height, 3);
const priceUpdate = block.receipts[0].result;
// Ensure that the BTC_USD price feed id was the only updated feed
assertArrayIncludes(priceUpdate.expectOk().expectList(),
["0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"]
);
assertEquals(block.receipts.length, 1);
assertEquals(block.height, 3);
const priceUpdate = block.receipts[0].result;
// Ensure that the BTC_USD price feed id was the only updated feed
assertArrayIncludes(priceUpdate.expectOk().expectList(), [
"0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43",
]);
const feedIdBtcUsd = hexToBuffer("0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43");
const oracleResult = chain.callReadOnlyFn(
"pyth-oracle-v1",
"read-price-feed",
[types.buff(feedIdBtcUsd)],
wallet_1.address
);
assertObjectMatch(oracleResult.result.expectOk().expectTuple(), {
"attestation-time": "u1686854317",
"conf": "u2064471426",
"ema-conf": "u1891952230",
"ema-price": "2507095440000",
"expo": "4294967288",
"prev-conf": "u2064471426",
"prev-price": "2515455528574",
"prev-publish-time": "u1686854316",
"price": "2515455528574",
"publish-time": "u1686854317",
"status": "u1"
});
},
const feedIdBtcUsd = hexToBuffer(
"0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"
);
const oracleResult = chain.callReadOnlyFn(
"pyth-oracle-dev-preview-1",
"read-price-feed",
[types.buff(feedIdBtcUsd)],
wallet_1.address
);
assertObjectMatch(oracleResult.result.expectOk().expectTuple(), {
"attestation-time": "u1686854317",
conf: "u2064471426",
"ema-conf": "u1891952230",
"ema-price": "2507095440000",
expo: "4294967288",
"prev-conf": "u2064471426",
"prev-price": "2515455528574",
"prev-publish-time": "u1686854316",
price: "2515455528574",
"publish-time": "u1686854317",
status: "u1",
});
},
});

24
tsconfig.json Normal file
View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["unit-tests"],
"exclude": ["node_modules"]
}

View File

@@ -1,27 +0,0 @@
// @ts-check
import fs from "node:fs";
import path from "node:path";
import initVM from "obscurity-sdk";
let deployer;
let sender;
/**
*
* @param {string} name
* @param {(vm: import("obscurity-sdk").ClarityVM) => any} fn
*/
export async function clarinetTest(name, fn) {
const vm = await initVM();
await vm.initSession(process.cwd(), "./Clarinet.toml");
const accounts = vm.getAccounts();
deployer = accounts.get("deployer");
sender = accounts.get("wallet_1");
fn(vm);
const lcov = vm.terminate();
fs.appendFileSync(path.join(process.cwd(), "lcov.info"), lcov);
}

View File

@@ -1,36 +1,31 @@
// @ts-check
import { it } from "node:test";
import { Cl } from "@stacks/transactions";
import { mainnet_valid_guardians_set_upgrades, mainnet_valid_pfs } from "./constants.js";
import { clarinetTest } from "./clarinetTest.js";
import { Cl, ClarityValue } from "@stacks/transactions";
import { describe, it } from "vitest";
import { mainnet_valid_guardians_set_upgrades, mainnet_valid_pfs } from "./constants";
const pyth_oracle_v1_contract_name = "pyth-oracle-v1";
const wormhole_core_v1_contract_name = "wormhole-core-v1";
const pyth_oracle_v1_contract_name = "pyth-oracle-dev-preview-1";
const wormhole_core_v1_contract_name = "wormhole-core-dev-preview-1";
clarinetTest("Pyth testsuite", (vm) => {
describe("Pyth testsuite", () => {
const accounts = vm.getAccounts();
const deployer = accounts.get("deployer");
const pyth_oracle_v1_contract_addr = `${deployer}.${pyth_oracle_v1_contract_name}`;
const wormhole_core_v1_contract_addr = `${deployer}.${wormhole_core_v1_contract_name}`;
const sender = accounts.get("wallet_1");
if (!sender) throw new Error("invalid account");
const sender = accounts.get("wallet_1")!;
it("ensure that legitimate price attestations are validated", () => {
const vaaRotation1 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[0].vaa);
let publicKeysRotation1 = [];
let publicKeysRotation1: ClarityValue[] = [];
for (let key of mainnet_valid_guardians_set_upgrades[0].keys) {
publicKeysRotation1.push(Cl.bufferFromHex(key));
}
const vaaRotation2 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[1].vaa);
let publicKeysRotation2 = [];
let publicKeysRotation2: ClarityValue[] = [];
for (let key of mainnet_valid_guardians_set_upgrades[1].keys) {
publicKeysRotation2.push(Cl.bufferFromHex(key));
}
const vaaRotation3 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[2].vaa);
let publicKeysRotation3 = [];
let publicKeysRotation3: ClarityValue[] = [];
for (let key of mainnet_valid_guardians_set_upgrades[2].keys) {
publicKeysRotation3.push(Cl.bufferFromHex(key));
}

5
unit-tests/types/global.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
import type { ClarityVM } from "obscurity-sdk";
declare global {
var vm: ClarityVM;
}
export {};

View File

@@ -1,37 +1,31 @@
// @ts-check
import { it } from "node:test";
import { Cl, ClarityValue } from "@stacks/transactions";
import { describe, it } from "vitest";
import { Cl } from "@stacks/transactions";
import { mainnet_valid_guardians_set_upgrades } from "./constants.js";
import { clarinetTest } from "./clarinetTest.js";
import { mainnet_valid_guardians_set_upgrades } from "./constants";
const pyth_oracle_v1_contract_name = "pyth-oracle-v1";
const wormhole_core_v1_contract_name = "wormhole-core-v1";
const wormhole_core_v1_contract_name = "wormhole-core-dev-preview-1";
clarinetTest("Wormhole testsuite", (vm) => {
describe("Wormhole testsuite", () => {
const accounts = vm.getAccounts();
const deployer = accounts.get("deployer");
const pyth_oracle_v1_contract_addr = `${deployer}.${pyth_oracle_v1_contract_name}`;
const wormhole_core_v1_contract_addr = `${deployer}.${wormhole_core_v1_contract_name}`;
const sender = accounts.get("wallet_1");
if (!sender) throw new Error("invalid account");
const sender = accounts.get("wallet_1")!;
it("ensure that guardians set can be rotated", () => {
const vaaRotation1 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[0].vaa);
let publicKeysRotation1 = [];
let publicKeysRotation1: ClarityValue[] = [];
for (let key of mainnet_valid_guardians_set_upgrades[0].keys) {
publicKeysRotation1.push(Cl.bufferFromHex(key));
}
const vaaRotation2 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[1].vaa);
let publicKeysRotation2 = [];
let publicKeysRotation2: ClarityValue[] = [];
for (let key of mainnet_valid_guardians_set_upgrades[1].keys) {
publicKeysRotation2.push(Cl.bufferFromHex(key));
}
const vaaRotation3 = Cl.bufferFromHex(mainnet_valid_guardians_set_upgrades[2].vaa);
let publicKeysRotation3 = [];
let publicKeysRotation3: ClarityValue[] = [];
for (let key of mainnet_valid_guardians_set_upgrades[2].keys) {
publicKeysRotation3.push(Cl.bufferFromHex(key));
}