diff --git a/test/cli.test.ts b/test/cli.test.ts index 3ec345b..92a502c 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -1,9 +1,21 @@ import fs from 'fs'; -import yaml from 'js-yaml'; 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', () => { test('laconic', async () => { @@ -40,6 +52,9 @@ describe('Test laconic CLI commands', () => { const testAccount2 = 'ethm1vc62ysqu504at932jjq8pwrqgjt67rx6ggn5yu'; assert(testAccount, 'TEST_ACCOUNT not set in env'); + const testAuthorityName = 'laconic'; + let testAuctionId: string; + test('laconic cns status', async () => { const result = spawnSync('laconic', ['cns', 'status']); const outputObj = checkResultAndRetrieveOutput(result); @@ -47,7 +62,7 @@ describe('Test laconic CLI commands', () => { // Expect output object to have CNS status props expect(outputObj).toHaveProperty('version'); expect(outputObj).toHaveProperty('node'); - expect(outputObj).toHaveProperty('node.network', 'laconic_9000-1'); + expect(outputObj).toHaveProperty('node.network', CHAIN_ID); expect(outputObj).toHaveProperty('sync'); expect(Number(outputObj.sync.latest_block_height)).toBeGreaterThan(0); expect(outputObj).toHaveProperty('validator'); @@ -67,7 +82,7 @@ describe('Test laconic CLI commands', () => { const outputObj = checkResultAndRetrieveOutput(result); // Expect output object to have resultant bond id - expect(outputObj).toHaveProperty('bondId'); + expect(outputObj.bondId).toBeDefined(); bondId = outputObj.bondId; }); @@ -130,8 +145,8 @@ describe('Test laconic CLI commands', () => { expect(outputObj.length).toEqual(1); expect(outputObj[0]).toMatchObject(expectedAccount); - expect(outputObj[0]).toHaveProperty('number'); - expect(outputObj[0]).toHaveProperty('sequence'); + expect(outputObj[0].number).toBeDefined(); + expect(outputObj[0].sequence).toBeDefined(); balanceBeforeSend = Number(outputObj[0].balance[0].quantity); expect(balanceBeforeSend).toBeGreaterThan(0); @@ -171,7 +186,7 @@ describe('Test laconic CLI commands', () => { const outputObj = checkResultAndRetrieveOutput(result); // Expect output object to resultant bond id - expect(outputObj).toHaveProperty('id'); + expect(outputObj.id).toBeDefined(); recordId = outputObj.id; }); @@ -185,9 +200,9 @@ describe('Test laconic CLI commands', () => { expect(outputObj.length).toEqual(1); expect(outputObj[0]).toMatchObject(expectedRecord); - expect(outputObj[0]).toHaveProperty('createTime'); - expect(outputObj[0]).toHaveProperty('expiryTime'); - expect(outputObj[0]).toHaveProperty('owners'); + expect(outputObj[0].createTime).toBeDefined(); + expect(outputObj[0].expiryTime).toBeDefined(); + expect(outputObj[0].owners).toBeDefined(); expect(outputObj[0].owners.length).toEqual(1); }); @@ -218,10 +233,8 @@ describe('Test laconic CLI commands', () => { }); describe('Name authority operations (pre auction)', () => { - const authorityName = 'laconic'; - test('laconic cns authority reserve ', async () => { - const result = spawnSync('laconic', ['cns', 'authority', 'reserve', authorityName]); + const result = spawnSync('laconic', ['cns', 'authority', 'reserve', testAuthorityName]); const outputObj = checkResultAndRetrieveOutput(result); // Expect result @@ -229,7 +242,7 @@ describe('Test laconic CLI commands', () => { }); test('laconic cns authority whois ', async () => { - const result = spawnSync('laconic', ['cns', 'authority', 'whois', authorityName]); + const result = spawnSync('laconic', ['cns', 'authority', 'whois', testAuthorityName]); const outputObj = checkResultAndRetrieveOutput(result); // Expected authority (still in auction) @@ -237,23 +250,66 @@ describe('Test laconic CLI commands', () => { expect(outputObj.length).toEqual(1); expect(outputObj[0]).toMatchObject(expectedAuthority); - expect(outputObj[0]).toHaveProperty('expiryTime'); + expect(outputObj[0].expiryTime).toBeDefined(); expect(outputObj[0].height).toBeGreaterThan(0); + + testAuctionId = outputObj[0].auction.id; }); }); describe('Auction operations', () => { + const bidAmount = 25000000; + let bidRevealFilePath: string; + test('laconic cns auction get ', 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 ', 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 ', async () => { - // TODO - }); + test('laconic cns auction bid reveal ', async () => { + // 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)', () => { @@ -289,97 +345,3 @@ describe('Test laconic CLI commands', () => { }); }); }); - -// Helper methods - -function checkResultAndRetrieveOutput(result: SpawnSyncReturns): 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 || [] - }; -} diff --git a/test/helpers.ts b/test/helpers.ts new file mode 100644 index 0000000..0d90eeb --- /dev/null +++ b/test/helpers.ts @@ -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): 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 { + return new Promise(res => setTimeout(res, ms)) +}