Compare commits

...

1 Commits

Author SHA1 Message Date
Tommy Verrall f95e9a7d37 Implement base line for tests on the validator ts client
- This will need to be separated and configured accordingly 
- This was a quick spin up, using jest as a library to implement some coverage
- Further things to be refined - mocks, more coverage, better configuration, clean up methods, improve env vars
2022-01-06 10:01:08 +00:00
15 changed files with 3994 additions and 2989 deletions
+6
View File
@@ -0,0 +1,6 @@
NYMD_URL=https://sandbox-validator.nymtech.net
VALIDATOR_API=https://sandbox-validator.nymtech.net/api
MIXNET_CONTRACT=nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j
VESTING_CONTRACT=nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s
CURRENCY_PREFIX=nymt
CHAIN_ID=nym-sandbox
+6 -12
View File
@@ -3,26 +3,20 @@ Nym Validator Client
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
Running examples
-----------------
With the code checked out, `cd examples`. This folder contains runnable example code that will set up a blockchain and allow you to interact with it through the client.
Running tests
-------------
The tests will be separated into three categories: unit, integration and mock.
Currently the command to run all tests:
```
npm test
```
You can also trigger test execution with a test watcher. I don't have the centuries of life left to me that are needed to fight through the arcana of wiring up a working TypeScript mocha triggered execution setup, so for now my Cargo-based hack is:
The tests require `.env.example` being renamed to `.env`. The variables and their values for these tests are currently pointing to the `nym-sandbox` environment.
```
cargo watch -s "cd clients/validator && npm test"
```
It's ugly but works fine if you have Cargo installed. TypeScript setup help happily accepted here.
`Tests are still in development` - the test libary is `jest` and the test script will execute currently with: `--coverage --verbosity false`
Generating Documentation
------------------------
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFiles: ["dotenv/config"]
};
+7 -14
View File
@@ -6,9 +6,7 @@
"main": "./dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "ts-mocha tests/**/*.test.ts",
"coverage": "nyc npm test",
"test": "jest --coverage --verbose false",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"docs": "typedoc --out docs src/index.ts"
@@ -21,27 +19,21 @@
],
"license": "Apache-2.0",
"devDependencies": {
"@types/chai": "^4.2.15",
"@types/expect": "^24.3.0",
"@types/mocha": "^8.2.1",
"@types/jest": "27.4.0",
"@typescript-eslint/eslint-plugin": "^5.7.0",
"@typescript-eslint/parser": "^5.7.0",
"chai": "^4.2.0",
"eslint": "^7.18.0",
"eslint-config-airbnb": "^19.0.2",
"eslint-config-airbnb-typescript": "^16.1.0",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-root-import": "^1.0.4",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-mocha": "^10.0.3",
"eslint-plugin-prettier": "^4.0.0",
"mocha": "^8.2.1",
"moq.ts": "^7.2.0",
"nyc": "^15.1.0",
"jest": "^27.4.5",
"prettier": "^2.5.1",
"ts-mocha": "^8.0.0",
"ts-jest": "^27.1.2",
"typedoc": "^0.20.27",
"typescript": "^4.1.3"
"typescript": "^4.5.4"
},
"dependencies": {
"@cosmjs/cosmwasm-stargate": "^0.27.0-rc2",
@@ -51,6 +43,7 @@
"@cosmjs/stargate": "^0.27.0-rc2",
"@cosmjs/tendermint-rpc": "^0.27.0-rc2",
"axios": "^0.21.1",
"cosmjs-types": "^0.4.0"
"cosmjs-types": "^0.4.0",
"dotenv": "^10.0.0"
}
}
+5
View File
@@ -2,7 +2,12 @@ import axios from 'axios';
import { GasPrice } from '@cosmjs/stargate';
export function nymGasPrice(prefix: string): GasPrice {
if (typeof prefix === 'string') {
return GasPrice.fromString(`0.025u${prefix}`); // TODO: ideally this ugly conversion shouldn't be hardcoded here.
}
else {
throw new Error(`${prefix} is not of type string`);
}
}
export const downloadWasm = async (url: string): Promise<Uint8Array> => {
@@ -0,0 +1,92 @@
import validatorClient from "../../src/index";
import { config } from '../test-utils/config';
let client: validatorClient;
let mnemonic: string;
beforeEach(async () => {
mnemonic = validatorClient.randomMnemonic();
client = await validatorClient.connect(
mnemonic,
config.NYMD_URL as string,
config.VALIDATOR_API as string,
config.CURRENCY_PREFIX as string,
config.MIXNET_CONTRACT as string,
config.VESTING_CONTRACT as string
);
});
//todos
//we want to mock the majority of these tests
//and keep a few integration tests in place
describe("connect to the nym validator client and perform integration tests against the current testnet", () => {
test("get cached mixnodes", async () => {
try {
const response = await client.getCachedMixnodes();
//expect all mixnodes to have their owner address
response.forEach(mixnodeDetails => {
expect(mixnodeDetails.owner).toHaveLength(43)
});
}
catch (e) {
console.log(e);
}
});
test("get balance of address and denom of the network", async () => {
try {
//provide a users address and get their balance
//we expect their balance to be zero, as it's a new account
const address = await validatorClient.mnemonicToAddress(mnemonic, config.CURRENCY_PREFIX as string);
const response = await client.getBalance(address);
expect(response.amount).toStrictEqual("0");
expect(response.denom).toBe("unymt");
}
catch (e) {
console.log(e);
}
});
test("get minimium pledge amount for a mixnode", async () => {
try {
const response = await client.minimumMixnodePledge();
expect(response.amount).toBe("100000000");
expect(response.denom).toBe(config.CURRENCY_PREFIX as string);
}
catch (e) {
console.log(e);
}
});
test("get minimium gateway pledge amount", async () => {
try {
const response = await client.minimumGatewayPledge();
expect(response.amount).toBe("100000000");
expect(response.denom).toBe(config.CURRENCY_PREFIX as string);
}
catch (e) {
console.log(e);
}
});
test("get current mixnet contract address", () => {
try {
const response = client.mixnetContract;
expect(response).toStrictEqual(config.MIXNET_CONTRACT as string)
}
catch (e) {
console.log(e);
}
});
test("get current vesting contract address", () => {
try {
const response = client.vestingContract;
expect(response).toStrictEqual(config.VESTING_CONTRACT as string)
}
catch (e) {
console.log(e);
}
});
});
@@ -0,0 +1,66 @@
import SigningClient from '../../src/signing-client';
import validator from "../../src/index";
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate';
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';
import { config } from '../test-utils/config';
let cosmWasmClient: CosmWasmClient;
let signingClient: SigningClient;
let mnemonic: string;
beforeEach(async () => {
cosmWasmClient = await SigningClient.connect(config.NYMD_URL as string);
mnemonic = validator.randomMnemonic();
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic)
signingClient = await SigningClient.connectWithNymSigner(
wallet,
config.NYMD_URL as string,
config.VALIDATOR_API as string,
config.CURRENCY_PREFIX as string);
});
describe("alternate between the signing clients of nym and perform basic requests", () => {
test("retrieve balance using the cosmwasm client", async () => {
try {
const randomAddress = await validator.mnemonicToAddress(mnemonic, config.CURRENCY_PREFIX as string);
const balance = await cosmWasmClient.getBalance(randomAddress, config.CURRENCY_PREFIX as string);
expect(balance.denom).toStrictEqual(config.CURRENCY_PREFIX as string);
expect(balance.amount).toStrictEqual("0");
}
catch (e) {
console.log(e);
}
});
test("get the chain id of the network", async () => {
try {
//pass these values in environment variables in the future
const chainId = await cosmWasmClient.getChainId();
expect(chainId).toStrictEqual(config.CHAIN_ID as string);
}
catch (e) {
console.log(e);
}
});
test("get height then search its block", async () => {
try {
const height = await cosmWasmClient.getHeight()
const block = await cosmWasmClient.getBlock(height);
expect(block.header.chainId).toStrictEqual(config.CHAIN_ID as string);
expect(block.header.height).toStrictEqual(height);
}
catch (e) {
console.log(e);
}
});
test("get current reward pool", async () => {
try {
//this is due to change due to when rewards get distributed
const rewards = await signingClient.getRewardPool(config.MIXNET_CONTRACT as string);
expect(rewards).toStrictEqual("250000000000000");
}
catch (e) {
console.log(e);
}
});
});
@@ -0,0 +1,70 @@
import ValidatorApiQuerier from '../../src/validator-api-querier';
import { config } from '../test-utils/config';
import { now, elapsed} from '../test-utils/utils';
let client: ValidatorApiQuerier;
beforeEach(() => {
client = new ValidatorApiQuerier(config.VALIDATOR_API as string);
});
//todos
//we want to mock the majority of these tests
//and keep a few integration tests in place
describe("call out to the validator api and run queries", () => {
test.skip("build client and get all mixnodes", async () => {
try {
//this test was currently ran against a set of prefix data
//this will change
const ownerAddress = "nymt1ydqkmz0ddpvkd3l0vyf8k5xjrqtcnxxvhlpdsr";
let response = await client.getActiveMixnodes();
expect(response[0].owner).toStrictEqual(ownerAddress);
}
catch (e) {
console.log(e);
}
});
test("get rewarded mixnodes", async () => {
try {
// we assume that all mixnodes will have their owners address
// also active sets will determine rewarded mixnodes
let response = await client.getRewardedMixnodes();
response.forEach(rNode => {
expect(rNode.owner.length).toStrictEqual(43);
})
}
catch (e) {
console.log(e);
}
});
test("get cached gateways and it should be six", async () => {
try {
//current gateways running in sandbox-testnet 6
let response = await client.getCachedGateways();
expect(response.length).toStrictEqual(6);
}
catch (e) {
console.log(e);
}
});
test("get cached mixnodes", async () => {
try {
const start = now();
let response = await client.getCachedMixnodes();
response.forEach(mixnode => {
expect(mixnode.owner.length).toStrictEqual(43);
})
console.log(elapsed(start, true));
}
catch (e) {
console.log(e);
}
});
});
@@ -0,0 +1,9 @@
export const config = {
NYMD_URL: process.env.NYMD_URL,
VALIDATOR_API: process.env.VALIDATOR_API,
MIXNET_CONTRACT: process.env.MIXNET_CONTRACT,
VESTING_CONTRACT: process.env.MIXNET_CONTRACT,
USER_MNEMONIC: process.env.MNEMONIC,
CURRENCY_PREFIX: process.env.CURRENCY_PREFIX,
CHAIN_ID: process.env.CHAIN_ID
}
@@ -0,0 +1,17 @@
// timer actions
// Store current time as `start`
export const now = (eventName = null) => {
if (eventName) {
console.log(`Started ${eventName}..`);
}
return new Date().getTime();
}
//takes arg of start time
export const elapsed = (beginning: number, log = false) => {
const duration = new Date().getTime() - beginning;
if (log) {
console.log(`${duration / 1000}s`);
}
return duration;
}
@@ -0,0 +1,10 @@
const currency = require('../../src/currency');
describe("provide unit tests around the the currency module", () => {
test.skip("convert to native balance", () => {
const decimal = "12.0346";
const value = currency.printableBalanceToNative(decimal);
expect(value).toStrictEqual("12034600");
});
});
@@ -0,0 +1,27 @@
const stargate = require("../../src/stargate-helper");
import { config } from '../test-utils/config';
describe("test the stargate functions within the client", () => {
test("test that the gas price is returned correctly", () => {
const nymCurrency = config.CURRENCY_PREFIX as string;
const getGasPrice = stargate.nymGasPrice(nymCurrency);
expect(getGasPrice.denom).toBe(`u${nymCurrency}`);
});
test("provide invalid type returns an error message", () => {
//pass invalid type
expect(() => {
const nymCurrency = 13;
stargate.nymGasPrice(nymCurrency);
}).toThrow("13 is not of type string");
});
//provide test for downloading wasm
//mock this test
// test.skip("providing nothing returns", async () => {
// //pass invalid type
// const downloadWasm = stargate.downloadWasm("http://localhost");
// console.log(downloadWasm);
// })
});
@@ -0,0 +1,19 @@
import validator from "../../src/index";
import { config } from '../test-utils/config';
const NETWORK_DENOM = config.CURRENCY_PREFIX as string;
describe("perform basic interactions with the validator", () => {
test("build client and get all mixnodes", async () => {
const mnemonic = validator.randomMnemonic();
const mnemonicCount = mnemonic.split(" ").length;
expect(mnemonicCount).toStrictEqual(24);
});
test("build client and get all mixnodes", async () => {
const buildMnemonic = validator.randomMnemonic();
const mnemonic = await validator.mnemonicToAddress(buildMnemonic, NETWORK_DENOM);
expect(mnemonic).toHaveLength(43);
expect(mnemonic).toContain(NETWORK_DENOM);
});
});
+10 -2
View File
@@ -5,7 +5,11 @@
"esModuleInterop": true,
"strict": true,
"declaration": true,
"outDir": "./dist"
"outDir": "./dist",
"types": [
"jest",
"node",
]
},
"typedocOptions": {
"entryPoints": [
@@ -16,7 +20,11 @@
"exclude": [
"dist",
"examples",
"tests",
"node_modules"
],
"include": [
"tests",
"./tests/*/*.tsx",
"./tests/*/*.ts"
]
}
+3643 -2960
View File
File diff suppressed because it is too large Load Diff