difftreelog
tests: thorough event logging + a few more tests refactored
in: master
7 files changed
tests/package.jsondiffbeforeafterboth64 "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts",64 "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts",65 "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts",65 "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts",66 "testAdminTransferAndBurn": "mocha --timeout 9999999 -r ts-node/register ./**/adminTransferAndBurn.test.ts",66 "testAdminTransferAndBurn": "mocha --timeout 9999999 -r ts-node/register ./**/adminTransferAndBurn.test.ts",67 "testMintModes": "mocha --timeout 9999999 -r ts-node/register ./**/mintModes.test.ts",67 "testSetMintPermission": "mocha --timeout 9999999 -r ts-node/register ./**/setMintPermission.test.ts",68 "testSetMintPermission": "mocha --timeout 9999999 -r ts-node/register ./**/setMintPermission.test.ts",68 "testSetPublicAccessMode": "mocha --timeout 9999999 -r ts-node/register ./**/setPublicAccessMode.test.ts",69 "testSetPublicAccessMode": "mocha --timeout 9999999 -r ts-node/register ./**/setPublicAccessMode.test.ts",69 "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.test.ts",70 "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.test.ts",tests/src/fungible.test.tsdiffbeforeafterboth15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.161617import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';18import {U128_MAX} from './util/helpers';19import {itSub, usingPlaygrounds, expect} from './util/playgrounds';18import {itSub, usingPlaygrounds, expect} from './util/playgrounds';201921// todo:playgrounds get rid of globals22let alice: IKeyringPair;20const U128_MAX = (1n << 128n) - 1n;23let bob: IKeyringPair;242125describe('integration test: Fungible functionality:', () => {22describe('integration test: Fungible functionality:', () => {23 let alice: IKeyringPair;24 let bob: IKeyringPair;2526 before(async () => {26 before(async () => {27 await usingPlaygrounds(async (helper, privateKey) => {27 await usingPlaygrounds(async (helper, privateKey) => {28 alice = privateKey('//Alice');28 const donor = privateKey('//Alice');29 bob = privateKey('//Bob');29 [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);30 });30 });31 });31 });323282 expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(60n);82 expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(60n);83 expect(await collection.getBalance(ethAcc)).to.be.equal(140n);83 expect(await collection.getBalance(ethAcc)).to.be.equal(140n);848485 await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejected;85 await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejectedWith(/common\.TokenValueTooLow/);86 });86 });878788 itSub('Tokens multiple creation', async ({helper}) => {88 itSub('Tokens multiple creation', async ({helper}) => {tests/src/inflation.test.tsdiffbeforeafterboth14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.161617import chai from 'chai';18import chaiAsPromised from 'chai-as-promised';17import {IKeyringPair} from '@polkadot/types/types';19import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';18import {expect, itSub, usingPlaygrounds} from './util/playgrounds';201921chai.use(chaiAsPromised);20// todo:playgrounds requires sudo, look into on the later stage22const expect = chai.expect;2324describe('integration test: Inflation', () => {21describe('integration test: Inflation', () => {22 let superuser: IKeyringPair;2324 before(async () => {25 await usingPlaygrounds(async (_, privateKey) => {26 superuser = privateKey('//Alice');27 });28 });29 25 it('First year inflation is 10%', async () => {30 itSub('First year inflation is 10%', async ({helper}) => {26 await usingApi(async (api, privateKeyWrapper) => {2728 // Make sure non-sudo can't start inflation31 // Make sure non-sudo can't start inflation29 const tx = api.tx.inflation.startInflation(1);32 const [bob] = await helper.arrange.createAccounts([10n], superuser);3330 const bob = privateKeyWrapper('//Bob');34 await expect(helper.executeExtrinsic(bob, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);3536 // Make sure superuser can't start inflation without explicit sudo31 await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected;37 await expect(helper.executeExtrinsic(superuser, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);323833 // Start inflation on relay block 1 (Alice is sudo)39 // Start inflation on relay block 1 (Alice is sudo)34 const alice = privateKeyWrapper('//Alice');40 const tx = helper.constructApiCall('api.tx.inflation.startInflation', [1]);35 const sudoTx = api.tx.sudo.sudo(tx as any);41 await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.not.be.rejected;36 await submitTransactionAsync(alice, sudoTx);374238 const blockInterval = (api.consts.inflation.inflationBlockInterval).toBigInt();43 const blockInterval = (helper.api!.consts.inflation.inflationBlockInterval as any).toBigInt();39 const totalIssuanceStart = (await api.query.inflation.startingYearTotalIssuance()).toBigInt();44 const totalIssuanceStart = ((await helper.api!.query.inflation.startingYearTotalIssuance()) as any).toBigInt();40 const blockInflation = (await api.query.inflation.blockInflation()).toBigInt();45 const blockInflation = (await helper.api!.query.inflation.blockInflation() as any).toBigInt();414642 const YEAR = 5259600n; // 6-second block. Blocks in one year47 const YEAR = 5259600n; // 6-second block. Blocks in one year43 // const YEAR = 2629800n; // 12-second block. Blocks in one year48 // const YEAR = 2629800n; // 12-second block. Blocks in one year49 const expectedInflation = totalExpectedInflation / totalActualInflation - 1n;54 const expectedInflation = totalExpectedInflation / totalActualInflation - 1n;505551 expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance);56 expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance);52 });53 });57 });5455});58});tests/src/refungible.test.tsdiffbeforeafterboth17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';18import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from './util/playgrounds';18import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from './util/playgrounds';191920let alice: IKeyringPair;21let bob: IKeyringPair;22const MAX_REFUNGIBLE_PIECES = 1_000_000_000_000_000_000_000n;20const MAX_REFUNGIBLE_PIECES = 1_000_000_000_000_000_000_000n;232124describe('integration test: Refungible functionality:', async () => {22describe('integration test: Refungible functionality:', async () => {23 let alice: IKeyringPair;24 let bob: IKeyringPair;2525 before(async function() {26 before(async function() {26 await usingPlaygrounds(async (helper, privateKey) => {27 await usingPlaygrounds(async (helper, privateKey) => {27 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);28 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);282929 alice = privateKey('//Alice');30 const donor = privateKey('//Alice');30 bob = privateKey('//Bob');31 [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);31 });32 });32 });33 });33 34 209 const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});210 const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});210 const token = await collection.mintToken(alice, 100n);211 const token = await collection.mintToken(alice, 100n);211 await token.repartition(alice, 200n);212 await token.repartition(alice, 200n);212 const chainEvents = helper.chainLog.slice(-1)[0].events.map((x: any) => x.event);213 const chainEvents = helper.chainLog.slice(-1)[0].events;213 expect(chainEvents).to.include.deep.members([{214 expect(chainEvents).to.deep.include({214 method: 'ItemCreated',215 section: 'common',215 section: 'common',216 method: 'ItemCreated',216 index: '0x4202',217 index: [66, 2],217 data: [ 218 data: [218 helper.api!.createType('u32', collection.collectionId).toHuman(), 219 collection.collectionId,219 helper.api!.createType('u32', token.tokenId).toHuman(),220 token.tokenId,220 {Substrate: alice.address}, 221 {substrate: alice.address}, 221 '100',222 100n,222 ],223 ],224 phase: {applyExtrinsic: 2},223 }]);225 });224 });226 });225227226 itSub('Repartition with decreased amount', async ({helper}) => {228 itSub('Repartition with decreased amount', async ({helper}) => {227 const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});229 const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});228 const token = await collection.mintToken(alice, 100n);230 const token = await collection.mintToken(alice, 100n);229 await token.repartition(alice, 50n);231 await token.repartition(alice, 50n);230 const chainEvents = helper.chainLog.slice(-1)[0].events.map((x: any) => x.event);232 const chainEvents = helper.chainLog.slice(-1)[0].events;231 expect(chainEvents).to.include.deep.members([{233 expect(chainEvents).to.deep.include({232 method: 'ItemDestroyed',234 method: 'ItemDestroyed',233 section: 'common',235 section: 'common',234 index: '0x4203',236 index: [66, 3],235 data: [ 237 data: [236 helper.api!.createType('u32', collection.collectionId).toHuman(), 238 collection.collectionId,237 helper.api!.createType('u32', token.tokenId).toHuman(),239 token.tokenId,238 {Substrate: alice.address}, 240 {substrate: alice.address}, 239 '50',241 50n,240 ],242 ],243 phase: {applyExtrinsic: 2},241 }]);244 });242 });245 });243 246 244 itSub('Create new collection with properties', async ({helper}) => {247 itSub('Create new collection with properties', async ({helper}) => {tests/src/tx-version-presence.test.tsdiffbeforeafterbothno syntactic changes
tests/src/util/playgrounds/types.tsdiffbeforeafterboth334import {IKeyringPair} from '@polkadot/types/types';4import {IKeyringPair} from '@polkadot/types/types';556export interface IChainEvent {6export interface IEvent {7 data: any;7 section: string;8 method: string;8 method: string;9 section: string;9 index: [number, number] | string;10 data: any[];11 phase: {applyExtrinsic: number} | 'Initialization',10}12}111312export interface ITransactionResult {14export interface ITransactionResult {13 status: 'Fail' | 'Success';15 status: 'Fail' | 'Success';14 result: {16 result: {15 events: {17 events: {18 phase: any, // {ApplyExtrinsic: number} | 'Initialization',16 event: IChainEvent19 event: IEvent;20 // topics: any[];17 }[];21 }[];18 },22 },19 moduleError?: string;23 moduleError?: string;20}24}2526export interface ISubscribeBlockEventsData {27 number: number;28 hash: string;29 timestamp: number; 30 events: IEvent[];31}213222export interface ILogger {33export interface ILogger {23 log: (msg: any, level?: string) => void;34 log: (msg: any, level?: string) => void;tests/src/util/playgrounds/unique.tsdiffbeforeafterboth9import {ApiInterfaceEvents} from '@polkadot/api/types';9import {ApiInterfaceEvents} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';11import {IKeyringPair} from '@polkadot/types/types';12import {IApiListeners, IBlock, IChainEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types';12import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types';131314export const crossAccountIdFromLower = (lowerAddress: ICrossAccountIdLower): ICrossAccountId => {14export const crossAccountIdFromLower = (lowerAddress: ICrossAccountIdLower): ICrossAccountId => {15 const address = {} as ICrossAccountId;15 const address = {} as ICrossAccountId;149 return {success, tokens};149 return {success, tokens};150 }150 }151151152 static findCollectionInEvents(events: {event: IChainEvent}[], collectionId: number, expectedSection: string, expectedMethod: string) {152 static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string) {153 let eventId = null;153 let eventId = null;154 events.forEach(({event: {data, method, section}}) => {154 events.forEach(({event: {data, method, section}}) => {155 if ((section === expectedSection) && (method === expectedMethod)) {155 if ((section === expectedSection) && (method === expectedMethod)) {163 return eventId === collectionId;163 return eventId === collectionId;164 }164 }165165166 static isTokenTransferSuccess(events: {event: IChainEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {166 static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {167 const normalizeAddress = (address: string | ICrossAccountId) => {167 const normalizeAddress = (address: string | ICrossAccountId) => {168 if(typeof address === 'string') return address;168 if(typeof address === 'string') return address;169 const obj = {} as any;169 const obj = {} as any;195 }195 }196}196}197197198class UniqueEventHelper {199 private static extractIndex(index: any): [number, number] | string {200 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];201 return index.toJSON();202 }203204 private static extractSub(data: any, subTypes: any): {[key: string]: any} {205 let obj: any = {};206 let index = 0;207208 if (data.entries)209 for(const [key, value] of data.entries()) {210 obj[key] = this.extractData(value, subTypes[index]);211 index++;212 }213 else obj = data.toJSON();214215 return obj;216 }217 218 private static extractData(data: any, type: any): any {219 if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();220 if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();221 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);222 return data.toHuman();223 }224225 public static extractEvents(records: ITransactionResult): IEvent[] {226 const parsedEvents: IEvent[] = [];227228 records.result.events.forEach((record) => {229 const {event, phase} = record;230 const types = (event as any).typeDef;231232 const eventData: IEvent = {233 section: event.section.toString(),234 method: event.method.toString(),235 index: this.extractIndex(event.index),236 data: [],237 phase: phase.toJSON(),238 };239240 event.data.forEach((val: any, index: number) => {241 eventData.data.push(this.extractData(val, types[index]));242 });243244 parsedEvents.push(eventData);245 });246247 return parsedEvents;248 }249}198250199class ChainHelperBase {251class ChainHelperBase {200 transactionStatus = UniqueUtil.transactionStatus;252 transactionStatus = UniqueUtil.transactionStatus;201 chainLogType = UniqueUtil.chainLogType;253 chainLogType = UniqueUtil.chainLogType;202 util: typeof UniqueUtil;254 util: typeof UniqueUtil;255 eventHelper: typeof UniqueEventHelper;203 logger: ILogger;256 logger: ILogger;204 api: ApiPromise | null;257 api: ApiPromise | null;205 forcedNetwork: TUniqueNetworks | null;258 forcedNetwork: TUniqueNetworks | null;208261209 constructor(logger?: ILogger) {262 constructor(logger?: ILogger) {210 this.util = UniqueUtil;263 this.util = UniqueUtil;264 this.eventHelper = UniqueEventHelper;211 if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();265 if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();212 this.logger = logger;266 this.logger = logger;213 this.api = null;267 this.api = null;290 return {api, network};344 return {api, network};291 }345 }292346293 getTransactionStatus(data: {events: {event: IChainEvent}[], status: any}) {347 getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {294 const {events, status} = data;348 const {events, status} = data;295 if (status.isReady) {349 if (status.isReady) {296 return this.transactionStatus.NOT_READY;350 return this.transactionStatus.NOT_READY;299 return this.transactionStatus.NOT_READY;353 return this.transactionStatus.NOT_READY;300 }354 }301 if (status.isInBlock || status.isFinalized) {355 if (status.isInBlock || status.isFinalized) {302 const errors = events.filter(e => e.event.data.method === 'ExtrinsicFailed');356 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');303 if (errors.length > 0) {357 if (errors.length > 0) {304 return this.transactionStatus.FAIL;358 return this.transactionStatus.FAIL;305 }359 }306 if (events.filter(e => e.event.data.method === 'ExtrinsicSuccess').length > 0) {360 if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {307 return this.transactionStatus.SUCCESS;361 return this.transactionStatus.SUCCESS;308 }362 }309 }363 }332 if (result.hasOwnProperty('dispatchError')) {386 if (result.hasOwnProperty('dispatchError')) {333 const dispatchError = result['dispatchError'];387 const dispatchError = result['dispatchError'];334388335 if (dispatchError && dispatchError.isModule) {389 if (dispatchError) {390 if (dispatchError.isModule) {336 const modErr = dispatchError.asModule;391 const modErr = dispatchError.asModule;337 const errorMeta = dispatchError.registry.findMetaError(modErr);392 const errorMeta = dispatchError.registry.findMetaError(modErr);338393339 moduleError = `${errorMeta.section}.${errorMeta.name}`;394 moduleError = `${errorMeta.section}.${errorMeta.name}`;340 }395 } else {396 moduleError = dispatchError.toHuman();397 }341 else {398 } else {342 this.logger.log(result, this.logger.level.ERROR);399 this.logger.log(result, this.logger.level.ERROR);343 }400 }344 }401 }364 return call(...params);421 return call(...params);365 }422 }366423367 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=false/*, failureMessage='expected success'*/) {424 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true/*, failureMessage='expected success'*/) {368 if(this.api === null) throw Error('API not initialized');425 if(this.api === null) throw Error('API not initialized');369 if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);426 if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);370427371 const startTime = (new Date()).getTime();428 const startTime = (new Date()).getTime();372 let result: ITransactionResult;429 let result: ITransactionResult;373 let events = [];430 let events: IEvent[] = [];374 try {431 try {375 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), extrinsic) as ITransactionResult;432 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), extrinsic) as ITransactionResult;376 events = result.result.events.map((x: any) => x.toHuman());433 events = this.eventHelper.extractEvents(result);377 }434 }378 catch(e) {435 catch(e) {379 if(!(e as object).hasOwnProperty('status')) throw e;436 if(!(e as object).hasOwnProperty('status')) throw e;