Add tests for querying names and deleteName

This commit is contained in:
2022-04-21 09:44:24 +05:30
committed by Ashwin Phatak
parent 9de3c59c19
commit f207be4838
7 changed files with 406 additions and 10 deletions
+50 -3
View File
@@ -14,7 +14,7 @@ import { createTxMsgCancelBond, createTxMsgCreateBond, createTxMsgRefillBond, cr
import { RegistryClient } from "./registry-client";
import { Account } from "./account";
import { createTransaction } from "./txbuilder";
import { createTxMsgReserveAuthority, createTxMsgSetAuthorityBond, createTxMsgSetName, createTxMsgSetRecord, MessageMsgReserveAuthority, MessageMsgSetAuthorityBond, MessageMsgSetName, MessageMsgSetRecord } from './messages/nameservice';
import { createTxMsgDeleteName, createTxMsgReserveAuthority, createTxMsgSetAuthorityBond, createTxMsgSetName, createTxMsgSetRecord, MessageMsgDeleteName, MessageMsgReserveAuthority, MessageMsgSetAuthorityBond, MessageMsgSetName, MessageMsgSetRecord } from './messages/nameservice';
import { Payload, Record } from './types';
const DEFAULT_WRITE_ERROR = 'Unable to write to chiba-clonk.';
@@ -82,6 +82,20 @@ export class Registry {
return this._client.getAccount(address);
}
/**
* Get records by attributes.
*/
async queryRecords(attributes: {[key: string]: any}, all = false, refs = false) {
return this._client.queryRecords(attributes, all, refs);
}
/**
* Resolve names to records.
*/
async resolveNames(names: string[], refs = false) {
return this._client.resolveNames(names, refs);
}
/**
* Publish record.
* @param transactionPrivateKey - private key in HEX to sign transaction.
@@ -245,7 +259,7 @@ export class Registry {
/**
* Cancel bond.
*/
async cancelBond(params: MessageMsgCancelBond, senderAddress: string, privateKey: string, fee: Fee) {
async cancelBond(params: MessageMsgCancelBond, senderAddress: string, privateKey: string, fee: Fee) {
let result;
try {
@@ -338,7 +352,7 @@ export class Registry {
* @param {string} privateKey
* @param {object} fee
*/
async setName(params: MessageMsgSetName, senderAddress: string, privateKey: string, fee: Fee) {
async setName(params: MessageMsgSetName, senderAddress: string, privateKey: string, fee: Fee) {
let result;
try {
@@ -361,6 +375,39 @@ export class Registry {
return parseTxResponse(result);
}
/**
* Lookup naming information.
*/
async lookupNames(names: string[], history = false) {
return this._client.lookupNames(names, history);
}
/**
* Delete name (WRN) mapping.
*/
async deleteName(params: MessageMsgDeleteName, senderAddress: string, privateKey: string, fee: Fee) {
let result;
try {
const { account: { base_account: accountInfo } } = await this.getAccount(senderAddress);
const sender = {
accountAddress: accountInfo.address,
sequence: accountInfo.sequence,
accountNumber: accountInfo.account_number,
pubkey: accountInfo.pub_key.key,
}
const msg = createTxMsgDeleteName(this._chain, sender, fee, '', params)
result = await this._submitTx(msg, privateKey, sender);
} catch (err: any) {
const error = err[0] || err;
throw new Error(Registry.processWriteError(error));
}
return parseTxResponse(result);
}
/**
* Submit record transaction.
* @param privateKey - private key in HEX to sign message.
+61
View File
@@ -60,6 +60,13 @@ const MSG_SET_AUTHORITY_BOND_TYPES = {
],
}
const MSG_DELETE_NAME_TYPES = {
MsgValue: [
{ name: 'wrn', type: 'string' },
{ name: 'signer', type: 'string' },
],
}
export interface MessageMsgReserveAuthority {
name: string
owner: string
@@ -80,6 +87,10 @@ export interface MessageMsgSetAuthorityBond {
bondId: string
}
export interface MessageMsgDeleteName {
wrn: string
}
export function createTxMsgReserveAuthority(
chain: Chain,
sender: Sender,
@@ -176,6 +187,28 @@ export function createTxMsgSetAuthorityBond(
return createTx(chain, sender, fee, memo, types, msg, msgCosmos)
}
export function createTxMsgDeleteName(
chain: Chain,
sender: Sender,
fee: Fee,
memo: string,
params: MessageMsgDeleteName,
) {
const types = generateTypes(MSG_DELETE_NAME_TYPES)
const msg = createMsgDeleteName(
params.wrn,
sender.accountAddress
)
const msgCosmos = protoCreateMsgDeleteName(
params.wrn,
sender.accountAddress
)
return createTx(chain, sender, fee, memo, types, msg, msgCosmos)
}
function createMsgReserveAuthority(
name: string,
signer: string,
@@ -316,3 +349,31 @@ const protoCreateMsgSetAuthorityBond = (
path: 'vulcanize.nameservice.v1beta1.MsgSetAuthorityBond',
}
}
function createMsgDeleteName(
wrn: string,
signer: string
) {
return {
type: 'nameservice/DeleteAuthority',
value: {
wrn,
signer
},
}
}
const protoCreateMsgDeleteName = (
wrn: string,
signer: string
) => {
const deleteNameAutorityMessage = new nameserviceTx.vulcanize.nameservice.v1beta1.MsgDeleteNameAuthority({
wrn,
signer,
})
return {
message: deleteNameAutorityMessage,
path: 'vulcanize.nameservice.v1beta1.MsgDeleteNameAuthority',
}
}
+148 -4
View File
@@ -1,11 +1,13 @@
import assert from 'assert';
import path from 'path';
import semver from 'semver';
import { Account } from './account';
import { Registry } from './index';
import { ensureUpdatedConfig, getConfig } from './testing/helper';
import { getBaseConfig, getConfig } from './testing/helper';
const WATCHER_ID = 'bafyreibmr47ksukoadck2wigevb2jp5j5oubfadeyzb6zi57ydjsvjmmby'
const WATCHER_1_ID = 'bafyreif7z4lbftuxkj7nu4bl7xihitqvhus3wmoqdkjo7lwv24j6fkfskm'
const WATCHER_2_ID = 'bafyreiaplz7n2bddg3xhkeuiavwnku7xta2s2cfyaza37ytv4vw367exo4'
const WATCHER_YML_PATH = path.join(__dirname, './testing/data/watcher.yml');
jest.setTimeout(120 * 1000);
@@ -14,10 +16,15 @@ const { chainId, restEndpoint, gqlEndpoint, privateKey, accountAddress, fee } =
const namingTests = () => {
let registry: Registry;
let bondId: string;
let watcher: any;
let watcherId: string;
let authorityName: string;
let otherAuthorityName: string;
let otherPrivateKey: string;
let wrn: string;
beforeAll(async () => {
@@ -28,7 +35,8 @@ const namingTests = () => {
await registry.createBond({ denom: 'aphoton', amount: '1000000000' }, accountAddress, privateKey, fee);
// Create bot.
watcher = await ensureUpdatedConfig(WATCHER_YML_PATH);
// TODO: Use ensureUpdatedConfig from helper.
watcher = await getBaseConfig(WATCHER_YML_PATH);
const result = await registry.setRecord(
{
privateKey,
@@ -42,7 +50,7 @@ const namingTests = () => {
// TODO: Get id from setRecord response.
// watcherId = result.data;
watcherId = WATCHER_ID;
watcherId = WATCHER_1_ID;
});
test('Reserve authority.', async () => {
@@ -116,6 +124,142 @@ const namingTests = () => {
test('Set authority bond', async () => {
await registry.setAuthorityBond({ name: authorityName, bondId }, accountAddress, privateKey, fee);
});
test('Set name', async () => {
wrn = `wrn://${authorityName}/app/test`;
await registry.setName({ wrn, cid: watcherId }, accountAddress, privateKey, fee);
// Query records should return it (some WRN points to it).
const records = await registry.queryRecords({ type: 'watcher', version: watcher.record.version });
expect(records).toBeDefined();
// TODO: Fix queryRecords to return filtered results.
// expect(records).toHaveLength(1);
});
test('Lookup name', async () => {
const records = await registry.lookupNames([wrn]);
expect(records).toBeDefined();
expect(records).toHaveLength(1);
const [{ latest, history }] = records;
expect(latest).toBeDefined();
expect(latest.id).toBeDefined();
expect(latest.id).toBe(watcherId);
expect(latest.height).toBeDefined();
expect(history).toBeUndefined();
});
test('Resolve name', async () => {
const records = await registry.resolveNames([wrn]);
expect(records).toBeDefined();
expect(records).toHaveLength(1);
const [{ attributes }] = records;
// TODO: Set name with new record.
// expect(attributes).toEqual(watcher.record);
});
test('Lookup name with history', async () => {
// TODO: Use ensureUpdatedConfig from helper.
const updatedWatcher = await getBaseConfig(WATCHER_YML_PATH);
updatedWatcher.record.version = semver.inc(updatedWatcher.record.version, 'patch');
const result = await registry.setRecord(
{
privateKey,
bondId,
record: updatedWatcher.record
},
accountAddress,
privateKey,
fee
)
// TODO: Get id from setRecord response.
// const updatedWatcherId = result.data;
const updatedWatcherId = WATCHER_2_ID;
await registry.setName({ wrn, cid: updatedWatcherId }, accountAddress, privateKey, fee);
const records = await registry.lookupNames([wrn], true);
expect(records).toHaveLength(1);
const [{ latest, history }] = records;
expect(latest).toBeDefined();
expect(latest.id).toBeDefined();
expect(latest.id).toBe(updatedWatcherId);
expect(latest.height).toBeDefined();
expect(history).toBeDefined();
expect(history).toHaveLength(1);
const [oldRecord] = history;
expect(oldRecord).toBeDefined();
expect(oldRecord.id).toBeDefined();
expect(oldRecord.id).toBe(watcherId);
expect(oldRecord.height).toBeDefined();
});
xtest('Set name without reserving authority', async () => {
await expect(registry.setName({ wrn: 'wrn://not-reserved/app/test', cid: watcherId }, accountAddress, privateKey, fee))
.rejects.toThrow('Name authority not found.');
});
xtest('Set name for non-owned authority', async () => {
// Create another account.
const mnenonic = Account.generateMnemonic();
const otherAccount = await Account.generateFromMnemonic(mnenonic);
await otherAccount.init()
assert(otherAccount.formattedCosmosAddress)
// TODO: Get correct account address from private key.
await registry.sendCoins({ denom: 'aphoton', amount: '1000000000', destinationAddress: otherAccount.formattedCosmosAddress }, accountAddress, privateKey, fee);
// Other account reserves an authority.
otherAuthorityName = `other-${Date.now()}`;
otherPrivateKey = otherAccount.privateKey.toString('hex');
await registry.reserveAuthority({ name: otherAuthorityName, owner: otherAccount.formattedCosmosAddress }, otherAccount.formattedCosmosAddress, otherPrivateKey, fee);
// Try setting name under other authority.
await expect(registry.setName({ wrn: `wrn://${otherAuthorityName}/app/test`, cid: watcherId }, accountAddress, privateKey, fee)).rejects.toThrow('Access denied.');
});
test('Lookup non existing name', async () => {
const records = await registry.lookupNames(['wrn://not-reserved/app/test']);
expect(records).toBeDefined();
expect(records).toHaveLength(1);
const [record] = records;
expect(record).toBeNull();
});
test('Resolve non existing name', async () => {
const records = await registry.resolveNames(['wrn://not-reserved/app/test']);
expect(records).toBeDefined();
expect(records).toHaveLength(1);
const [record] = records;
expect(record).toBeNull();
});
test('Delete name', async () => {
await registry.deleteName({ wrn }, accountAddress, privateKey, fee);
let records = await registry.lookupNames([wrn], true);
expect(records).toBeDefined();
expect(records).toHaveLength(1);
const [{ latest }] = records;
expect(latest).toBeDefined();
expect(latest.id).toBeDefined();
expect(latest.id).toBe('');
expect(latest.height).toBeDefined();
// Query records should NOT return it (no WRN points to it).
records = await registry.queryRecords({ type: 'watcher', version: watcher.record.version });
expect(records).toBeDefined();
// TODO: Fix queryRecords to return filtered results.
// expect(records).toHaveLength(0);
// Query all records should return it (all: true).
records = await registry.queryRecords({ type: 'watcher', version: watcher.record.version }, true);
expect(records).toBeDefined();
// expect(records).toHaveLength(1);
});
};
if (process.env.AUCTIONS_ENABLED) {
+132 -2
View File
@@ -1,10 +1,41 @@
import assert from 'assert';
import axios from 'axios';
import graphqlClient from 'graphql.js'
import { generateEndpointAccount, generateEndpointBroadcast, generatePostBodyBroadcast } from '@tharsis/provider';
import { get, set } from 'lodash'
import { generateEndpointAccount, generateEndpointBroadcast } from '@tharsis/provider';
import { Util } from './util';
const attributeField = `
attributes {
key
value {
null
int
float
string
boolean
json
reference {
id
}
}
}
`;
const refsField = `
references {
id
}
`;
const historyFields = `
history {
id
height
}
`;
const auctionFields = `
id
status
@@ -64,7 +95,7 @@ export class RegistryClient {
/**
* Get query result.
*/
static async getResult(query: any, key: string, modifier?: (rows: any[]) => {}) {
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) {
@@ -75,6 +106,19 @@ export class RegistryClient {
return [];
}
/**
* Prepare response attributes.
*/
static prepareAttributes(path: string) {
return (rows: any[]) => {
const result = rows.map(r => {
set(r, path, Util.fromGQLAttributes(get(r, path)));
return r;
});
return result;
};
}
/**
* New Client.
*/
@@ -100,6 +144,38 @@ export class RegistryClient {
return data
}
/**
* Get records by attributes.
*/
async queryRecords(attributes: {[key: string]: any}, all = false, refs = false) {
if (!attributes) {
attributes = {};
}
const query = `query ($attributes: [KeyValueInput!], $all: Boolean) {
queryRecords(attributes: $attributes, all: $all) {
id
names
owners
bondId
createTime
expiryTime
${attributeField}
${refs ? refsField : ''}
}
}`;
const variables = {
attributes: Util.toGQLAttributes(attributes),
all
};
let result = (await this._graph(query)(variables))['queryRecords'];
result = RegistryClient.prepareAttributes('attributes')(result);
return result;
}
/**
* Lookup authorities by names.
*/
@@ -127,6 +203,60 @@ export class RegistryClient {
return result['lookupAuthorities'];
}
/**
* Lookup names.
*/
async lookupNames(names: string[], history = false) {
assert(names.length);
const query = `query ($names: [String!]) {
lookupNames(names: $names) {
latest {
id
height
}
${history ? historyFields : ''}
}
}`;
const variables = {
names
};
const result = await this._graph(query)(variables);
return result['lookupNames'];
}
/**
* Resolve names to records.
*/
async resolveNames(names: string[], refs = false) {
assert(names.length);
const query = `query ($names: [String!]) {
resolveNames(names: $names) {
id
names
owners
bondId
createTime
expiryTime
${attributeField}
${refs ? refsField : ''}
}
}`;
const variables = {
names
};
const result = (await this._graph(query)(variables))['resolveNames'];
result.records = RegistryClient.prepareAttributes('attributes')(result);
return result;
}
/**
* Get bonds by ids.
*/
+7
View File
@@ -10,6 +10,13 @@ export const ensureUpdatedConfig = async (path: string) => {
return conf;
};
export const getBaseConfig = async (path: string) => {
const conf = await yaml.read(path);
conf.record.version = '0.0.1';
return conf;
};
export const getConfig = () => {
assert(process.env.PRIVATE_KEY);
assert(process.env.ACCOUNT_ADDRESS);