From 2390e1641b991aa604ec05c565ab59d230ca3fbd Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 19 Jan 2021 16:15:35 +0000 Subject: [PATCH] Merge pull request #67 from usetech-llc/feature/NFTPAR-257 Feature/nftpar 257 --- --- a/tests/package.json +++ b/tests/package.json @@ -28,7 +28,8 @@ "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts", "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts", "testToggleContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/toggleContractWhiteList.test.ts", - "testAddToContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/addToContractWhiteList.test.ts" + "testAddToContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/addToContractWhiteList.test.ts", + "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts" }, "author": "", "license": "Apache 2.0", --- a/tests/src/burnItem.test.ts +++ b/tests/src/burnItem.test.ts @@ -35,16 +35,16 @@ const tx = api.tx.nft.burnItem(collectionId, tokenId, 0); const events = await submitTransactionAsync(alice, tx); const result = getGenericResult(events); - - // Get the item + // Get the item const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON(); - // What to expect + // tslint:disable-next-line:no-unused-expression expect(result.success).to.be.true; + // tslint:disable-next-line:no-unused-expression expect(item).to.be.not.null; expect(item.Owner).to.be.equal(nullPublicKey); }); - + }); it('Burn item in Fungible collection', async () => { const createMode = 'Fungible'; --- a/tests/src/substrate/get-balance.ts +++ b/tests/src/substrate/get-balance.ts @@ -3,12 +3,12 @@ // file 'LICENSE', which is part of this source code package. // -import { ApiPromise } from "@polkadot/api"; -import promisifySubstrate from "./promisify-substrate"; -import {AccountInfo} from "@polkadot/types/interfaces/system"; +import { ApiPromise } from '@polkadot/api'; +import {AccountInfo} from '@polkadot/types/interfaces/system'; +import promisifySubstrate from './promisify-substrate'; -export default async function getBalance(api: ApiPromise, accounts: string[]): Promise { - const balance = promisifySubstrate(api, (accounts: string[]) => api.query.system.account.multi(accounts)); +export default async function getBalance(api: ApiPromise, accounts: string[]): Promise> { + const balance = promisifySubstrate(api, (acc: string[]) => api.query.system.account.multi(acc)); const responce = await balance(accounts) as unknown as AccountInfo[]; - return responce.map(r => r.data.free.toBigInt().valueOf()); + return responce.map((r) => r.data.free.toBigInt().valueOf()); } --- a/tests/src/substrate/substrate-api.ts +++ b/tests/src/substrate/substrate-api.ts @@ -58,21 +58,22 @@ return TransactionStatus.Fail; } -export function submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic): Promise { - return new Promise(async function(resolve, reject) { +export function +submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic): Promise { + return new Promise(async (resolve, reject) => { try { await transaction.signAndSend(sender, ({ events = [], status }) => { const transactionStatus = getTransactionStatus(events, status); - if (transactionStatus == TransactionStatus.Success) { + if (transactionStatus === TransactionStatus.Success) { resolve(events); - } else if (transactionStatus == TransactionStatus.Fail) { + } else if (transactionStatus === TransactionStatus.Fail) { console.log(`Something went wrong with transaction. Status: ${status}`); reject(events); } }); } catch (e) { - console.log("Error: ", e); + console.log('Error: ', e); reject(e); } }); --- a/tests/src/transfer.test.ts +++ b/tests/src/transfer.test.ts @@ -3,49 +3,174 @@ // file 'LICENSE', which is part of this source code package. // -import { expect, assert } from "chai"; -import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api"; -import { alicesPublicKey, bobsPublicKey, ferdiesPublicKey } from "./accounts"; -import privateKey from "./substrate/privateKey"; -import getBalance from "./substrate/get-balance"; -import { BigNumber } from 'bignumber.js'; -import { findUnusedAddress } from './util/helpers' +import { ApiPromise } from '@polkadot/api'; +import { IKeyringPair } from '@polkadot/types/types'; +import { expect } from 'chai'; +import { alicesPublicKey, bobsPublicKey } from './accounts'; +import getBalance from './substrate/get-balance'; +import privateKey from './substrate/privateKey'; +import { default as usingApi, submitTransactionAsync } from './substrate/substrate-api'; +import { + burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, + destroyCollectionExpectSuccess, + findUnusedAddress, + getCreateCollectionResult, + getCreateItemResult, + transferExpectFail, + transferExpectSuccess, +} from './util/helpers'; -describe('Transfer', () => { - it('Balance transfers', async () => { - await usingApi(async api => { +let Alice: IKeyringPair; +let Bob: IKeyringPair; +let Charlie: IKeyringPair; + +describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => { + it('Balance transfers and check balance', async () => { + await usingApi(async (api: ApiPromise) => { const [alicesBalanceBefore, bobsBalanceBefore] = await getBalance(api, [alicesPublicKey, bobsPublicKey]); const alicePrivateKey = privateKey('//Alice'); - + const transfer = api.tx.balances.transfer(bobsPublicKey, 1n); - const result = await submitTransactionAsync(alicePrivateKey, transfer); + const events = await submitTransactionAsync(alicePrivateKey, transfer); + const result = getCreateItemResult(events); + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.true; const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]); + // tslint:disable-next-line:no-unused-expression expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true; + // tslint:disable-next-line:no-unused-expression expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true; }); }); it('Inability to pay fees error message is correct', async () => { - await usingApi(async api => { + await usingApi(async (api) => { // Find unused address const pk = await findUnusedAddress(api); - const error = console.error; - const log = console.log; - console.log = function () {}; - console.error = function () {}; - const badTransfer = api.tx.balances.transfer(bobsPublicKey, 1n); - const badTransaction = async function () { - const result = await submitTransactionAsync(pk, badTransfer); + // const events = await submitTransactionAsync(pk, badTransfer); + const badTransaction = async () => { + const events = await submitTransactionAsync(pk, badTransfer); + const result = getCreateCollectionResult(events); + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.false; }; - await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees"); + expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low'); + }); + }); - console.log = log; - console.error = error; + it('Create collection, balance transfers and check balance', async () => { + await usingApi(async (api) => { + const Alice = privateKey('//Alice'); + const Bob = privateKey('//Bob'); + // nft + const nftCollectionId = await createCollectionExpectSuccess(); + const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT'); + await transferExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT'); + // fungible + const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible'); + await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible'); + // reFungible + const reFungibleCollectionId = await + createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); + await transferExpectSuccess(reFungibleCollectionId, + newReFungibleTokenId, Alice, Bob, 1, 'ReFungible'); }); }); }); + +describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => { + before(async () => { + await usingApi(async (api: ApiPromise) => { + Alice = privateKey('//Alice'); + Bob = privateKey('//Bob'); + Charlie = privateKey('//Charlie'); + }); + }); + it('Transfer with not existed collection_id', async () => { + await usingApi(async (api) => { + // nft + const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number; + await transferExpectFail(nftCollectionCount + 1, 1, Alice, Bob, 1); + // fungible + const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number; + await transferExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob, 1); + // reFungible + const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number; + await transferExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob, 1); + }); + }); + it('Transfer with deleted collection_id', async () => { + // nft + const nftCollectionId = await createCollectionExpectSuccess(); + const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT'); + await destroyCollectionExpectSuccess(nftCollectionId); + await transferExpectFail(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT'); + // fungible + const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible'); + await destroyCollectionExpectSuccess(fungibleCollectionId); + await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible'); + // reFungible + const reFungibleCollectionId = await + createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); + await destroyCollectionExpectSuccess(reFungibleCollectionId); + await transferExpectFail(reFungibleCollectionId, + newReFungibleTokenId, Alice, Bob, 1, 'ReFungible'); + }); + it('Transfer with not existed item_id', async () => { + // nft + const nftCollectionId = await createCollectionExpectSuccess(); + await transferExpectFail(nftCollectionId, 2, Alice, Bob, 1, 'NFT'); + // fungible + const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + await transferExpectFail(fungibleCollectionId, 2, Alice, Bob, 1, 'Fungible'); + // reFungible + const reFungibleCollectionId = await + createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + await transferExpectFail(reFungibleCollectionId, + 2, Alice, Bob, 1, 'ReFungible'); + }); + it('Transfer with deleted item_id', async () => { + // nft + const nftCollectionId = await createCollectionExpectSuccess(); + const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT'); + await burnItemExpectSuccess(Alice, nftCollectionId, newNftTokenId, 1); + await transferExpectFail(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT'); + // fungible + const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible'); + await burnItemExpectSuccess(Alice, fungibleCollectionId, newFungibleTokenId, 10); + await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1, 'Fungible'); + // reFungible + const reFungibleCollectionId = await + createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); + await burnItemExpectSuccess(Alice, reFungibleCollectionId, newReFungibleTokenId, 1); + await transferExpectFail(reFungibleCollectionId, + newReFungibleTokenId, Alice, Bob, 1, 'ReFungible'); + }); + it('Transfer with recipient that is not owner', async () => { + // nft + const nftCollectionId = await createCollectionExpectSuccess(); + const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT'); + await transferExpectFail(nftCollectionId, newNftTokenId, Charlie, Bob, 1, 'NFT'); + // fungible + const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible'); + await transferExpectFail(fungibleCollectionId, newFungibleTokenId, Charlie, Bob, 1, 'Fungible'); + // reFungible + const reFungibleCollectionId = await + createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); + const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible'); + await transferExpectFail(reFungibleCollectionId, + newReFungibleTokenId, Charlie, Bob, 1, 'ReFungible'); + }); +}); --- a/tests/src/util/helpers.ts +++ b/tests/src/util/helpers.ts @@ -391,6 +391,22 @@ ReFungible: CreateReFungibleData; } +export async function burnItemExpectSuccess(owner: IKeyringPair, collectionId: number, tokenId: number, value = 0) { + await usingApi(async (api) => { + const tx = api.tx.nft.burnItem(collectionId, tokenId, value); + const events = await submitTransactionAsync(owner, tx); + const result = getGenericResult(events); + // Get the item + const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON(); + // What to expect + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.true; + // tslint:disable-next-line:no-unused-expression + expect(item).to.be.not.null; + expect(item.Owner).to.be.equal(nullPublicKey); + }); +} + export async function approveExpectSuccess(collectionId: number, tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number = 1) { @@ -462,6 +478,58 @@ } export async function +transferExpectSuccess(collectionId: number, + tokenId: number, + sender: IKeyringPair, + recipient: IKeyringPair, + value: number = 1, + type: string = 'NFT') { + await usingApi(async (api: ApiPromise) => { + let balanceBefore = new BN(0); + if (type === 'Fungible') { + balanceBefore = await api.query.nft.balance(collectionId, recipient.address) as unknown as BN; + } + const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value); + const events = await submitTransactionAsync(sender, transferTx); + const result = getCreateItemResult(events); + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.true; + if (type === 'NFT') { + const nftItemData = await api.query.nft.nftItemList(collectionId, tokenId) as unknown as ITokenDataType; + expect(nftItemData.Owner.toString()).to.be.equal(recipient.address); + } + if (type === 'Fungible') { + const balanceAfter = await api.query.nft.balance(collectionId, recipient.address) as unknown as BN; + expect(balanceAfter.sub(balanceBefore).toNumber()).to.be.equal(value); + } + if (type === 'ReFungible') { + const nftItemData = + await api.query.nft.reFungibleItemList(collectionId, tokenId) as unknown as IReFungibleTokenDataType; + expect(nftItemData.Owner[0].Owner.toString()).to.be.equal(recipient.address); + expect(nftItemData.Owner[0].Fraction.toNumber()).to.be.equal(value); + } + }); +} + +export async function +transferExpectFail(collectionId: number, + tokenId: number, + sender: IKeyringPair, + recipient: IKeyringPair, + value: number = 1, + type: string = 'NFT') { + await usingApi(async (api: ApiPromise) => { + const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value); + const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected; + if (events && Array.isArray(events)) { + const result = getCreateCollectionResult(events); + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.false; + } + }); +} + +export async function approveExpectFail(collectionId: number, tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number = 1) { await usingApi(async (api: ApiPromise) => { @@ -473,7 +541,8 @@ }); } -export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') { +export async function createItemExpectSuccess( + sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') { let newItemId: number = 0; await usingApi(async (api) => { const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10); @@ -525,6 +594,7 @@ const collection: any = (await api.query.nft.collection(collectionId)).toJSON(); // What to expect + // tslint:disable-next-line:no-unused-expression expect(result.success).to.be.true; expect(collection.Access).to.be.equal('WhiteList'); }); @@ -559,6 +629,7 @@ const collection: any = (await api.query.nft.collection(collectionId)).toJSON(); // What to expect + // tslint:disable-next-line:no-unused-expression expect(result.success).to.be.true; expect(collection.MintMode).to.be.equal(true); }); -- gitstuff