Add tests for querying bond

This commit is contained in:
nabarun 2022-04-04 12:35:16 +05:30 committed by Ashwin Phatak
parent 19d5405087
commit 7fe5bcda83
8 changed files with 221 additions and 20 deletions

View File

@ -25,6 +25,7 @@
"axios": "^0.26.1",
"ethers": "^5.6.1",
"evmosjs": "^0.2.2",
"graphql.js": "^0.6.8",
"is-url": "^1.2.4",
"js-sha256": "^0.9.0",
"ripemd160": "^2.0.2",

View File

@ -1,7 +1,7 @@
import { Registry } from './index';
import { getConfig } from './testing/helper';
import { getConfig, wait } from './testing/helper';
const { mockServer, chibaClonk: { chainId, endpoint, privateKey, accountAddress, fee } } = getConfig();
const { mockServer, chibaClonk: { chainId, restEndpoint, gqlEndpoint, privateKey, accountAddress, fee } } = getConfig();
jest.setTimeout(90 * 1000);
@ -9,15 +9,41 @@ const bondTests = () => {
let registry: Registry;
let bondId1: string;
let bondOwner: string;
beforeAll(async () => {
registry = new Registry(endpoint, chainId);
registry = new Registry(restEndpoint, gqlEndpoint, chainId);
});
test('Create bond.', async () => {
bondId1 = await registry.getNextBondId(accountAddress);
expect(bondId1).toBeDefined();
await registry.createBond({ denom: 'aphoton', amount: '100' }, accountAddress, privateKey, fee);
await wait(5000)
})
test('Get bond by ID.', async () => {
const [bond] = await registry.getBondsByIds([bondId1]);
expect(bond).toBeDefined();
expect(bond.id).toBe(bondId1);
expect(bond.balance).toHaveLength(1);
expect(bond.balance[0]).toEqual({ type: 'aphoton', quantity: '100' });
bondOwner = bond.owner;
});
test('Query bonds.', async () => {
const bonds = await registry.queryBonds();
expect(bonds).toBeDefined();
const bond = bonds.filter((bond: any) => bond.id === bondId1);
expect(bond).toBeDefined();
});
test('Query bonds by owner.', async () => {
const bonds = await registry.queryBonds({ owner: bondOwner });
expect(bonds).toBeDefined();
const bond = bonds.filter((bond: any) => bond.id === bondId1);
expect(bond).toBeDefined();
});
};
if (mockServer) {

1
src/graphql.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module 'graphql.js'

View File

@ -53,13 +53,17 @@ export class Registry {
return message.log || DEFAULT_WRITE_ERROR;
}
constructor(url: string, cosmosChainId = DEFAULT_CHAIN_ID) {
if (!isUrl(url)) {
throw new Error('Path to a registry GQL endpoint should be provided.');
constructor(restUrl: string, gqlUrl: string, cosmosChainId = DEFAULT_CHAIN_ID) {
if (!isUrl(restUrl)) {
throw new Error('Path to a REST endpoint should be provided.');
}
this._endpoint = url;
this._client = new RegistryClient(url);
if (!isUrl(gqlUrl)) {
throw new Error('Path to a GQL endpoint should be provided.');
}
this._endpoint = restUrl;
this._client = new RegistryClient(restUrl, gqlUrl);
this._chain = {
chainId: 9000,
@ -85,7 +89,7 @@ export class Registry {
const accountObj = account.base_account;
const nextSeq = parseInt(accountObj.sequence, 10) + 1;
result = sha256(`${accountObj.address}:${accountObj.number}:${nextSeq}`);
result = sha256(`${accountObj.address}:${accountObj.account_number}:${nextSeq}`);
} catch (err: any) {
const error = err[0] || err;
throw new Error(Registry.processWriteError(error));
@ -94,6 +98,20 @@ export class Registry {
return result;
}
/**
* Get bonds by ids.
*/
async getBondsByIds(ids: string[]) {
return this._client.getBondsByIds(ids);
}
/**
* Query bonds by attributes.
*/
async queryBonds(attributes = {}) {
return this._client.queryBonds(attributes);
}
/**
* Create bond.
*/

View File

@ -1,22 +1,43 @@
import assert from 'assert';
import axios from 'axios';
import graphqlClient from 'graphql.js'
import { generateEndpointAccount, generateEndpointBroadcast, generatePostBodyBroadcast } from '@tharsis/provider';
import { Util } from './util';
/**
* Registry
*/
export class RegistryClient {
_endpoint: string
_restEndpoint: string
_graph: any
/**
* Get query result.
*/
static async getResult(query: any, key: string, modifier?: (rows: any[]) => {}) {
const result = await query;
if (result && result[key] && result[key].length && result[key][0] !== null) {
if (modifier) {
return modifier(result[key]);
}
return result[key];
}
return [];
}
/**
* New Client.
* @param {string} endpoint
* @param {object} options
*/
constructor(endpoint: string) {
assert(endpoint);
constructor(restEndpoint: string, gqlEndpoint: string) {
assert(restEndpoint);
this._endpoint = endpoint;
this._restEndpoint = restEndpoint;
this._graph = graphqlClient(gqlEndpoint, {
method: 'POST',
asJSON: true
});
}
/**
@ -25,11 +46,58 @@ export class RegistryClient {
async getAccount(address: string) {
assert(address);
let { data } = await axios.get(`${this._endpoint}${generateEndpointAccount(address)}`)
let { data } = await axios.get(`${this._restEndpoint}${generateEndpointAccount(address)}`)
return data
}
/**
* Get bonds by ids.
*/
async getBondsByIds(ids: string[]) {
assert(ids);
assert(ids.length);
const query = `query ($ids: [String!]) {
getBondsByIds(ids: $ids) {
id
owner
balance {
type
quantity
}
}
}`;
const variables = {
ids
};
return RegistryClient.getResult(this._graph(query)(variables), 'getBondsByIds');
}
/**
* Get records by attributes.
*/
async queryBonds(attributes = {}) {
const query = `query ($attributes: [KeyValueInput!]) {
queryBonds(attributes: $attributes) {
id
owner
balance {
type
quantity
}
}
}`;
const variables = {
attributes: Util.toGQLAttributes(attributes)
};
return RegistryClient.getResult(this._graph(query)(variables), 'queryBonds');
}
/**
* Submit transaction.
*/
@ -38,7 +106,7 @@ export class RegistryClient {
// Broadcast transaction.
const { data } = await axios.post(
`${this._endpoint}${generateEndpointBroadcast()}`,
`${this._restEndpoint}${generateEndpointBroadcast()}`,
tx
)

View File

@ -1,5 +1,7 @@
const DEFAULT_PRIVATE_KEY = '0451f0bd95c855d52e76cdc8dd06f29097b944bfef26d3455725157f9133f4e0';
const DEFAULT_ADDRESS = 'ethm19n3je0lhuk0w9kmkftsuw4etn8lmpu3jjfayeh'
const DEFAULT_PRIVATE_KEY = '794ce0bf3c75571416001c3415e69059aeba54038bcac8ce5b9792259e6d193b';
const DEFAULT_ADDRESS = 'ethm10atmndy7sm46829rc3yr7cxqucgrz5e9jg58xp'
export const wait = (time: number) => new Promise(resolve => setTimeout(resolve, time))
export const getConfig = () => ({
mockServer: process.env.MOCK_SERVER || false,
@ -7,7 +9,8 @@ export const getConfig = () => ({
chainId: process.env.CHIBA_CLONK_CHAIN_ID || 'ethermint_9000-1',
privateKey: DEFAULT_PRIVATE_KEY,
accountAddress: DEFAULT_ADDRESS,
endpoint: process.env.CHIBA_CLONK_ENDPOINT || 'http://localhost:1317',
restEndpoint: process.env.CHIBA_CLONK_REST_ENDPOINT || 'http://localhost:1317',
gqlEndpoint: process.env.CHIBA_CLONK_GQL_ENDPOINT || 'http://localhost:9473/api',
fee: {
amount: '20',
denom: 'aphoton',

79
src/util.ts Normal file
View File

@ -0,0 +1,79 @@
/**
* Utils
*/
export class Util {
/**
* Sorts JSON object.
*/
static sortJSON(object: any) {
if (object instanceof Array) {
for (let i = 0; i < object.length; i++) {
object[i] = Util.sortJSON(object[i]);
}
return object;
}
if (typeof object !== 'object' || object === null) return object;
let keys = Object.keys(object);
keys = keys.sort();
const newObject: {[key: string]: any} = {};
for (let i = 0; i < keys.length; i++) {
newObject[keys[i]] = Util.sortJSON(object[keys[i]]);
}
return newObject;
}
/**
* Marshal object into gql 'attributes' variable.
*/
static toGQLAttributes(object: any) {
const vars: any[] = [];
Object.keys(object).forEach(key => {
let type: string = typeof object[key];
if (object[key] === null) {
vars.push({ key, value: { 'null': true } });
} else if (type === 'number') {
type = (object[key] % 1 === 0) ? 'int' : 'float';
vars.push({ key, value: { [type]: object[key] } });
} else if (type === 'string') {
vars.push({ key, value: { 'string': object[key] } });
} else if (type === 'boolean') {
vars.push({ key, value: { 'boolean': object[key] } });
} else if (type === 'object') {
const nestedObject = object[key];
if (nestedObject['/'] !== undefined) {
vars.push({ key, value: { 'reference': { id: nestedObject['/'] } } });
}
}
});
return vars;
}
/**
* Unmarshal attributes array to object.
*/
static fromGQLAttributes(attributes: any[] = []) {
const res: {[key: string]: any} = {};
attributes.forEach(attr => {
if (attr.value.null) {
res[attr.key] = null;
} else if (attr.value.json) {
res[attr.key] = JSON.parse(attr.value.json);
} else if (attr.value.reference) {
// Convert GQL reference to IPLD style link.
const ref = attr.value.reference;
res[attr.key] = { '/': ref.id };
} else {
const { values, null: n, ...types } = attr.value;
const value = Object.values(types).find(v => v !== null);
res[attr.key] = value;
}
});
return res;
}
}

View File

@ -2220,6 +2220,11 @@ graceful-fs@^4.2.9:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
graphql.js@^0.6.8:
version "0.6.8"
resolved "https://registry.yarnpkg.com/graphql.js/-/graphql.js-0.6.8.tgz#5c2e57311b5e74c6665ff9394394bc76f273542f"
integrity sha512-y1OxsvPCfBell00yb2T1E+JQjFXzbmqDT3hsf7Ckof80DlRuQ3SrmLL7KC04Up81vlBj+l9opYJjDLf9OgMH3w==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"