Add CLI tests and setup CI #53

Merged
ashwin merged 23 commits from deep-stack/laconic-registry-cli:pm-add-tests into main 2024-01-29 04:46:32 +00:00
2 changed files with 197 additions and 115 deletions
Showing only changes of commit d8a5b93a23 - Show all commits

View File

@ -1,9 +1,21 @@
import fs from 'fs'; import fs from 'fs';
import yaml from 'js-yaml';
import assert from 'assert'; import assert from 'assert';
import { SpawnSyncReturns, spawnSync } from 'child_process'; import { spawnSync } from 'child_process';
const TOKEN_TYPE = 'aphoton'; import {
CHAIN_ID,
TOKEN_TYPE,
COMMIT_DURATION,
delay,
checkResultAndRetrieveOutput,
createBond,
getBondObj,
getAccountObj,
getRecordObj,
getAuthorityObj,
getAuctionObj,
getBidObject
} from './helpers';
describe('Test laconic CLI commands', () => { describe('Test laconic CLI commands', () => {
test('laconic', async () => { test('laconic', async () => {
@ -40,6 +52,9 @@ describe('Test laconic CLI commands', () => {
const testAccount2 = 'ethm1vc62ysqu504at932jjq8pwrqgjt67rx6ggn5yu'; const testAccount2 = 'ethm1vc62ysqu504at932jjq8pwrqgjt67rx6ggn5yu';
assert(testAccount, 'TEST_ACCOUNT not set in env'); assert(testAccount, 'TEST_ACCOUNT not set in env');
const testAuthorityName = 'laconic';
let testAuctionId: string;
test('laconic cns status', async () => { test('laconic cns status', async () => {
const result = spawnSync('laconic', ['cns', 'status']); const result = spawnSync('laconic', ['cns', 'status']);
const outputObj = checkResultAndRetrieveOutput(result); const outputObj = checkResultAndRetrieveOutput(result);
@ -47,7 +62,7 @@ describe('Test laconic CLI commands', () => {
// Expect output object to have CNS status props // Expect output object to have CNS status props
expect(outputObj).toHaveProperty('version'); expect(outputObj).toHaveProperty('version');
expect(outputObj).toHaveProperty('node'); expect(outputObj).toHaveProperty('node');
expect(outputObj).toHaveProperty('node.network', 'laconic_9000-1'); expect(outputObj).toHaveProperty('node.network', CHAIN_ID);
expect(outputObj).toHaveProperty('sync'); expect(outputObj).toHaveProperty('sync');
expect(Number(outputObj.sync.latest_block_height)).toBeGreaterThan(0); expect(Number(outputObj.sync.latest_block_height)).toBeGreaterThan(0);
expect(outputObj).toHaveProperty('validator'); expect(outputObj).toHaveProperty('validator');
@ -67,7 +82,7 @@ describe('Test laconic CLI commands', () => {
const outputObj = checkResultAndRetrieveOutput(result); const outputObj = checkResultAndRetrieveOutput(result);
// Expect output object to have resultant bond id // Expect output object to have resultant bond id
expect(outputObj).toHaveProperty('bondId'); expect(outputObj.bondId).toBeDefined();
bondId = outputObj.bondId; bondId = outputObj.bondId;
}); });
@ -130,8 +145,8 @@ describe('Test laconic CLI commands', () => {
expect(outputObj.length).toEqual(1); expect(outputObj.length).toEqual(1);
expect(outputObj[0]).toMatchObject(expectedAccount); expect(outputObj[0]).toMatchObject(expectedAccount);
expect(outputObj[0]).toHaveProperty('number'); expect(outputObj[0].number).toBeDefined();
expect(outputObj[0]).toHaveProperty('sequence'); expect(outputObj[0].sequence).toBeDefined();
balanceBeforeSend = Number(outputObj[0].balance[0].quantity); balanceBeforeSend = Number(outputObj[0].balance[0].quantity);
expect(balanceBeforeSend).toBeGreaterThan(0); expect(balanceBeforeSend).toBeGreaterThan(0);
@ -171,7 +186,7 @@ describe('Test laconic CLI commands', () => {
const outputObj = checkResultAndRetrieveOutput(result); const outputObj = checkResultAndRetrieveOutput(result);
// Expect output object to resultant bond id // Expect output object to resultant bond id
expect(outputObj).toHaveProperty('id'); expect(outputObj.id).toBeDefined();
recordId = outputObj.id; recordId = outputObj.id;
}); });
@ -185,9 +200,9 @@ describe('Test laconic CLI commands', () => {
expect(outputObj.length).toEqual(1); expect(outputObj.length).toEqual(1);
expect(outputObj[0]).toMatchObject(expectedRecord); expect(outputObj[0]).toMatchObject(expectedRecord);
expect(outputObj[0]).toHaveProperty('createTime'); expect(outputObj[0].createTime).toBeDefined();
expect(outputObj[0]).toHaveProperty('expiryTime'); expect(outputObj[0].expiryTime).toBeDefined();
expect(outputObj[0]).toHaveProperty('owners'); expect(outputObj[0].owners).toBeDefined();
expect(outputObj[0].owners.length).toEqual(1); expect(outputObj[0].owners.length).toEqual(1);
}); });
@ -218,10 +233,8 @@ describe('Test laconic CLI commands', () => {
}); });
describe('Name authority operations (pre auction)', () => { describe('Name authority operations (pre auction)', () => {
const authorityName = 'laconic';
test('laconic cns authority reserve <authority_name>', async () => { test('laconic cns authority reserve <authority_name>', async () => {
const result = spawnSync('laconic', ['cns', 'authority', 'reserve', authorityName]); const result = spawnSync('laconic', ['cns', 'authority', 'reserve', testAuthorityName]);
const outputObj = checkResultAndRetrieveOutput(result); const outputObj = checkResultAndRetrieveOutput(result);
// Expect result // Expect result
@ -229,7 +242,7 @@ describe('Test laconic CLI commands', () => {
}); });
test('laconic cns authority whois <authority_name>', async () => { test('laconic cns authority whois <authority_name>', async () => {
const result = spawnSync('laconic', ['cns', 'authority', 'whois', authorityName]); const result = spawnSync('laconic', ['cns', 'authority', 'whois', testAuthorityName]);
const outputObj = checkResultAndRetrieveOutput(result); const outputObj = checkResultAndRetrieveOutput(result);
// Expected authority (still in auction) // Expected authority (still in auction)
@ -237,23 +250,66 @@ describe('Test laconic CLI commands', () => {
expect(outputObj.length).toEqual(1); expect(outputObj.length).toEqual(1);
expect(outputObj[0]).toMatchObject(expectedAuthority); expect(outputObj[0]).toMatchObject(expectedAuthority);
expect(outputObj[0]).toHaveProperty('expiryTime'); expect(outputObj[0].expiryTime).toBeDefined();
expect(outputObj[0].height).toBeGreaterThan(0); expect(outputObj[0].height).toBeGreaterThan(0);
testAuctionId = outputObj[0].auction.id;
}); });
}); });
describe('Auction operations', () => { describe('Auction operations', () => {
const bidAmount = 25000000;
let bidRevealFilePath: string;
test('laconic cns auction get <auction_id>', async () => { test('laconic cns auction get <auction_id>', async () => {
// TODO const result = spawnSync('laconic', ['cns', 'auction', 'get', testAuctionId]);
const outputObj = checkResultAndRetrieveOutput(result);
// Expected auction (still in commit stage)
const expectedAuction = getAuctionObj({ owner: testAccount, status: 'commit' });
expect(outputObj.length).toEqual(1);
expect(outputObj[0]).toMatchObject(expectedAuction);
}); });
test('laconic cns auction bid commit <auction_id> <quantity> <type>', async () => { test('laconic cns auction bid commit <auction_id> <quantity> <type>', async () => {
// TODO const result = spawnSync('laconic', ['cns', 'auction', 'bid', 'commit', testAuctionId, bidAmount.toString(), TOKEN_TYPE]);
const outputObj = checkResultAndRetrieveOutput(result);
// Expected output
expect(outputObj.reveal_file).toBeDefined();
bidRevealFilePath = outputObj.reveal_file;
}); });
test('laconic cns auction bid reveal <auction_id> <file-path>', async () => { test('laconic cns auction bid reveal <auction_id> <file_path>', async () => {
// TODO // Wait for commits duration (60s)
await delay(COMMIT_DURATION * 1000);
const auctionResult = spawnSync('laconic', ['cns', 'auction', 'get', testAuctionId]);
const auctionOutputObj = checkResultAndRetrieveOutput(auctionResult);
const expectedAuction = getAuctionObj({ owner: testAccount, status: 'reveal' });
const expectedBid = getBidObject({ bidder: testAccount });
expect(auctionOutputObj[0]).toMatchObject(expectedAuction);
expect(auctionOutputObj[0].bids[0]).toMatchObject(expectedBid);
// Reveal bid
const result = spawnSync('laconic', ['cns', 'auction', 'bid', 'reveal', testAuctionId, bidRevealFilePath]);
const outputObj = checkResultAndRetrieveOutput(result);
// Expected output
expect(outputObj).toEqual({ success: true });
const revealObject = JSON.parse(fs.readFileSync(bidRevealFilePath, 'utf8'));
expect(revealObject).toMatchObject({
chainId: CHAIN_ID,
auctionId: testAuctionId,
bidderAddress: testAccount,
bidAmount: `${bidAmount}aphoton`
}); });
}, (COMMIT_DURATION + 5) * 1000);
}); });
describe('Name authority operations (post auction)', () => { describe('Name authority operations (post auction)', () => {
@ -289,97 +345,3 @@ describe('Test laconic CLI commands', () => {
}); });
}); });
}); });
// Helper methods
function checkResultAndRetrieveOutput(result: SpawnSyncReturns<Buffer>): any {
expect(result.status).toBe(0);
const errorOutput = result.stderr.toString().trim();
expect(errorOutput).toBe('');
const output = result.stdout.toString().trim();
expect(output.length).toBeGreaterThan(0);
return JSON.parse(output);
}
function createBond(quantity: number): { bondId: string } {
const result = spawnSync('laconic', ['cns', 'bond', 'create', '--type', TOKEN_TYPE, '--quantity', quantity.toString(), '--gas', '200000', '--fees', `200000${TOKEN_TYPE}`]);
const output = result.stdout.toString().trim();
return JSON.parse(output);
}
function getBondObj(params: { id: string, owner: string, balance: number}): any {
return {
id: params.id,
owner: params.owner,
balance: [
{
type: TOKEN_TYPE,
quantity: params.balance
}
]
};
}
function getAccountObj(params: { address: string, balance?: number }): any {
const balanceObj: any = { type: TOKEN_TYPE };
if (params.balance) {
balanceObj.quantity = params.balance;
}
return {
address: params.address,
balance: [balanceObj]
};
}
function getRecordObj(recordFilePath: string, params: { bondId: string, recordId: string }): any {
const recordContent = yaml.load(fs.readFileSync(recordFilePath, 'utf8')) as any;
return {
id: params.recordId,
names: null,
bondId: params.bondId,
attributes: recordContent.record
};
}
function getAuthorityObj(params: { status: string, auction: any }): any {
return {
ownerAddress: '',
ownerPublicKey: '',
status: params.status,
bondId: '',
auction: params.auction || null
};
}
function getAuctionObj(params: { owner: string, status?: string, bids?: any[] }): any {
const auctionFees = {
commit: 1000000,
reveal: 1000000,
minimumBid: 5000000
};
return {
status: params.status || 'commit',
ownerAddress: params.owner,
commitFee: {
type: TOKEN_TYPE,
quantity: auctionFees.commit
},
revealFee: {
type: TOKEN_TYPE,
quantity: auctionFees.reveal
},
minimumBid: {
type: TOKEN_TYPE,
quantity: auctionFees.minimumBid
},
winnerAddress: '',
bids: params.bids || []
};
}

120
test/helpers.ts Normal file
View File

@ -0,0 +1,120 @@
import fs from 'fs';
import yaml from 'js-yaml';
import { SpawnSyncReturns, spawnSync } from 'child_process';
export const CHAIN_ID = 'laconic_9000-1';
export const TOKEN_TYPE = 'aphoton';
export const AUCTION_FEES = {
commit: 1000000,
reveal: 1000000,
minimumBid: 5000000
};
export const COMMIT_DURATION = 60; // 60s
export function checkResultAndRetrieveOutput(result: SpawnSyncReturns<Buffer>): any {
expect(result.status).toBe(0);
const errorOutput = result.stderr.toString().trim();
expect(errorOutput).toBe('');
const output = result.stdout.toString().trim();
expect(output.length).toBeGreaterThan(0);
return JSON.parse(output);
}
export function createBond(quantity: number): { bondId: string } {
const result = spawnSync('laconic', ['cns', 'bond', 'create', '--type', TOKEN_TYPE, '--quantity', quantity.toString(), '--gas', '200000', '--fees', `200000${TOKEN_TYPE}`]);
const output = result.stdout.toString().trim();
return JSON.parse(output);
}
export function getBondObj(params: { id: string, owner: string, balance: number}): any {
return {
id: params.id,
owner: params.owner,
balance: [
{
type: TOKEN_TYPE,
quantity: params.balance
}
]
};
}
export function getAccountObj(params: { address: string, balance?: number }): any {
const balanceObj: any = { type: TOKEN_TYPE };
if (params.balance) {
balanceObj.quantity = params.balance;
}
return {
address: params.address,
balance: [balanceObj]
};
}
export function getRecordObj(recordFilePath: string, params: { bondId: string, recordId: string }): any {
const recordContent = yaml.load(fs.readFileSync(recordFilePath, 'utf8')) as any;
return {
id: params.recordId,
names: null,
bondId: params.bondId,
attributes: recordContent.record
};
}
export function getAuthorityObj(params: { status: string, auction: any }): any {
return {
ownerAddress: '',
ownerPublicKey: '',
status: params.status,
bondId: '',
auction: params.auction || null
};
}
export function getAuctionObj(params: { owner: string, status?: string }): any {
return {
status: params.status || 'commit',
ownerAddress: params.owner,
commitFee: {
type: TOKEN_TYPE,
quantity: AUCTION_FEES.commit
},
revealFee: {
type: TOKEN_TYPE,
quantity: AUCTION_FEES.reveal
},
minimumBid: {
type: TOKEN_TYPE,
quantity: AUCTION_FEES.minimumBid
},
winnerAddress: '',
};
}
export function getBidObject(params: { bidder: string, status?: string }): any {
return {
bidderAddress: params.bidder,
status: params.status || 'commit',
commitFee: {
type: TOKEN_TYPE,
quantity: AUCTION_FEES.commit
},
revealFee: {
type: TOKEN_TYPE,
quantity: AUCTION_FEES.reveal
},
bidAmount: {
type: '',
quantity: 0
}
};
}
export async function delay(ms: number): Promise<any> {
return new Promise(res => setTimeout(res, ms))
}