git.delta.rocks / unique-network / refs/commits / ed322340add8

difftreelog

Merge pull request #60 from usetech-llc/feature/NFTPAR-241

Greg Zaitsev2021-02-03parents: #3f6d973 #b21f673.patch.diff
in: master
Feature/nftpar 241

4 files changed

modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -27,6 +27,7 @@
     "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts",
     "testConnection": "mocha --timeout 9999999 -r ts-node/register ./**/connection.test.ts",
     "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
+    "testCreateMultipleItems": "mocha --timeout 9999999 -r ts-node/register ./**/createMultipleItems.test.ts",
     "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts",
     "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts",
     "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
modifiedtests/src/createMultipleItems.test.tsdiffbeforeafterboth
--- a/tests/src/createMultipleItems.test.ts
+++ b/tests/src/createMultipleItems.test.ts
@@ -2,68 +2,177 @@
 // This file is subject to the terms and conditions defined in
 // file 'LICENSE', which is part of this source code package.
 //
+import { ApiPromise } from '@polkadot/api';
+import BN from 'bn.js';
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import privateKey from './substrate/privateKey';
+import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api';
+import {
+  createCollectionExpectSuccess,
+  destroyCollectionExpectSuccess,
+  IReFungibleTokenDataType,
+} from './util/helpers';
 
-import { assert } from 'chai';
-import { alicesPublicKey } from './accounts';
-import privateKey from './substrate/privateKey';
-import usingApi from './substrate/substrate-api';
-import waitNewBlocks from './substrate/wait-new-blocks';
+chai.use(chaiAsPromised);
+const expect = chai.expect;
 
-const idCollection = 12;
+interface ITokenDataType {
+  Owner: number[];
+  ConstData: number[];
+  VariableData: number[];
+}
 
-describe.skip('integration test: ext. createMultipleItems():', () => {
-  it('Create two NFT tokens in active NFT collection', async () => {
-    await usingApi(async (api) => {
-      const AitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (before): ${AitemListIndex}`);
-      const args = ['NFT', 'NFT'];
-      const alicePrivateKey = privateKey('//Alice');
-      const createMultipleItems = await api.tx.nft
-      .createMultipleItems(idCollection, alicesPublicKey, args)
-      .signAndSend(alicePrivateKey);
-      // tslint:disable-next-line: no-unused-expression
-      assert.exists(createMultipleItems, 'createMultipleItems is neither `null` or `undefined`');
-      console.log(`Ext. createMultipleItems submitted with hash: ${createMultipleItems}`);
-      await waitNewBlocks(api);
-      const BitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (after): ${BitemListIndex}`);
-      if (BitemListIndex === AitemListIndex) { assert.fail('Corret token not added in collection!'); }
+describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
+  it('Create  0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN;
+      expect(itemsListIndexBefore.toNumber()).to.be.equal(0);
+      const Alice = privateKey('//Alice');
+      const args = [{ nft: ['0x31', '0x31'] }, { nft: ['0x32', '0x32'] }, { nft: ['0x33', '0x33'] }];
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, args);
+      await submitTransactionAsync(Alice, createMultipleItemsTx);
+      const itemsListIndexAfter = await api.query.nft.itemListIndex(collectionId) as unknown as BN;
+      expect(itemsListIndexAfter.toNumber()).to.be.equal(3);
+      const token1Data = await api.query.nft.nftItemList(collectionId, 1) as unknown as ITokenDataType;
+      const token2Data = await api.query.nft.nftItemList(collectionId, 2) as unknown as ITokenDataType;
+      const token3Data = await api.query.nft.nftItemList(collectionId, 3) as unknown as ITokenDataType;
+
+      expect(token1Data.Owner.toString()).to.be.equal(Alice.address);
+      expect(token2Data.Owner.toString()).to.be.equal(Alice.address);
+      expect(token3Data.Owner.toString()).to.be.equal(Alice.address);
+
+      expect(token1Data.ConstData.toString()).to.be.equal('0x31');
+      expect(token2Data.ConstData.toString()).to.be.equal('0x32');
+      expect(token3Data.ConstData.toString()).to.be.equal('0x33');
+
+      expect(token1Data.VariableData.toString()).to.be.equal('0x31');
+      expect(token2Data.VariableData.toString()).to.be.equal('0x32');
+      expect(token3Data.VariableData.toString()).to.be.equal('0x33');
     });
   });
-  it('(!negative test!) Create two Fungible tokens in active NFT collection', async () => {
-    await usingApi(async (api) => {
-      const AitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (before): ${AitemListIndex}`);
-      const args = ['Fungible', 'Fungible'];
-      const alicePrivateKey = privateKey('//Alice');
-      const createMultipleItems = await api.tx.nft
-      .createMultipleItems(idCollection, alicesPublicKey, args)
-      .signAndSend(alicePrivateKey);
-      // tslint:disable-next-line: no-unused-expression
-      assert.exists(createMultipleItems, 'createMultipleItems is neither `null` or `undefined`');
-      console.log(`Ext. createMultipleItems submitted with hash: ${createMultipleItems}`);
-      await waitNewBlocks(api);
-      const BitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (after): ${BitemListIndex}`);
-      if (BitemListIndex > AitemListIndex) { assert.fail('Incorrect token added in collection!'); }
+
+  it('Create  0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN;
+      expect(itemsListIndexBefore.toNumber()).to.be.equal(0);
+      const Alice = privateKey('//Alice');
+      const args = [
+        { Refungible: ['0x31', '0x31'] },
+        { Refungible: ['0x32', '0x32'] },
+        { Refungible: ['0x33', '0x33'] },
+      ];
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, args);
+      await submitTransactionAsync(Alice, createMultipleItemsTx);
+      const itemsListIndexAfter = await api.query.nft.itemListIndex(collectionId) as unknown as BN;
+      expect(itemsListIndexAfter.toNumber()).to.be.equal(3);
+      const token1Data = await api.query.nft.reFungibleItemList(collectionId, 1) as unknown as IReFungibleTokenDataType;
+      const token2Data = await api.query.nft.reFungibleItemList(collectionId, 2) as unknown as IReFungibleTokenDataType;
+      const token3Data = await api.query.nft.reFungibleItemList(collectionId, 3) as unknown as IReFungibleTokenDataType;
+
+      expect(token1Data.Owner[0].Owner.toString()).to.be.equal(Alice.address);
+      expect(token1Data.Owner[0].Fraction.toNumber()).to.be.equal(1);
+
+      expect(token2Data.Owner[0].Owner.toString()).to.be.equal(Alice.address);
+      expect(token2Data.Owner[0].Fraction.toNumber()).to.be.equal(1);
+
+      expect(token3Data.Owner[0].Owner.toString()).to.be.equal(Alice.address);
+      expect(token3Data.Owner[0].Fraction.toNumber()).to.be.equal(1);
+
+      expect(token1Data.ConstData.toString()).to.be.equal('0x31');
+      expect(token2Data.ConstData.toString()).to.be.equal('0x32');
+      expect(token3Data.ConstData.toString()).to.be.equal('0x33');
+
+      expect(token1Data.VariableData.toString()).to.be.equal('0x31');
+      expect(token2Data.VariableData.toString()).to.be.equal('0x32');
+      expect(token3Data.VariableData.toString()).to.be.equal('0x33');
     });
   });
-  it('(!negative test!) Create two ReFungible tokens in active NFT collection', async () => {
-    await usingApi(async (api) => {
-      const AitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (before): ${AitemListIndex}`);
-      const args = ['ReFungible', 'ReFungible'];
-      const alicePrivateKey = privateKey('//Alice');
-      const createMultipleItems = await api.tx.nft
-      .createMultipleItems(idCollection, alicesPublicKey, args)
-      .signAndSend(alicePrivateKey);
-      // tslint:disable-next-line: no-unused-expression
-      assert.exists(createMultipleItems, 'createMultipleItems is neither `null` or `undefined`');
-      console.log(`Ext. createMultipleItems submitted with hash: ${createMultipleItems}`);
-      await waitNewBlocks(api);
-      const BitemListIndex = await api.query.nft.itemListIndex(idCollection);
-      console.log(`itemListIndex count (after): ${BitemListIndex}`);
-      if (BitemListIndex > AitemListIndex) { assert.fail('Incorrect token added in collection!'); }
+});
+
+describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
+  it('Create token with not existing type', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      try {
+        const args = [{ invalid: null }, { invalid: null }, { invalid: null }];
+        const createMultipleItemsTx = await api.tx.nft
+          .createMultipleItems(collectionId, Alice.address, args);
+        await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
+      } catch (e) {
+        // tslint:disable-next-line:no-unused-expression
+        expect(e).to.be.exist;
+      }
+    });
+  });
+
+  it('Create token in not existing collection', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = parseInt((await api.query.nft.createdCollectionCount()).toString()) + 1;
+      const Alice = privateKey('//Alice');
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, ['NFT', 'NFT', 'NFT']);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
+    });
+  });
+
+  it('Create NFT and Re-fungible tokens that has reached the maximum data limit', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      // NFT
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const args = [
+        { nft: ['A'.repeat(2049), 'A'.repeat(2049)] },
+        { nft: ['B'.repeat(2049), 'B'.repeat(2049)] },
+        { nft: ['C'.repeat(2049), 'C'.repeat(2049)] },
+      ];
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, args);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
+
+      // ReFungible
+      const collectionIdReFungible =
+        await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}});
+      const argsReFungible = [
+        { ReFungible: ['1'.repeat(2049), '1'.repeat(2049)] },
+        { ReFungible: ['2'.repeat(2049), '2'.repeat(2049)] },
+        { ReFungible: ['3'.repeat(2049), '3'.repeat(2049)] },
+      ];
+      const createMultipleItemsTxFungible = await api.tx.nft
+        .createMultipleItems(collectionIdReFungible, Alice.address, argsReFungible);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTxFungible)).to.be.rejected;
+    });
+  });
+
+  it('Create tokens with different types', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, ['NFT', 'Fungible', 'ReFungible']);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
+      // garbage collection :-D
+      await destroyCollectionExpectSuccess(collectionId);
+    });
+  });
+
+  it('Create tokens with different data limits <> maximum data limit', async () => {
+    await usingApi(async (api: ApiPromise) => {
+      const collectionId = await createCollectionExpectSuccess();
+      const Alice = privateKey('//Alice');
+      const args = [
+        { nft: ['A', 'A'] },
+        { nft: ['B', 'B'.repeat(2049)] },
+        { nft: ['C'.repeat(2049), 'C'] },
+      ];
+      const createMultipleItemsTx = await api.tx.nft
+        .createMultipleItems(collectionId, Alice.address, args);
+      await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected;
     });
   });
 });
modifiedtests/src/substrate/substrate-api.tsdiffbeforeafterboth
before · tests/src/substrate/substrate-api.ts
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56import { WsProvider, ApiPromise } from "@polkadot/api";7import { EventRecord } from '@polkadot/types/interfaces/system/types';8import { ExtrinsicStatus } from '@polkadot/types/interfaces/author/types';9import { IKeyringPair } from "@polkadot/types/types";1011import config from "../config";12import promisifySubstrate from "./promisify-substrate";13import { ApiOptions, SubmittableExtrinsic, ApiTypes } from "@polkadot/api/types";14import rtt from "../../../runtime_types.json";1516function defaultApiOptions(): ApiOptions {17  const wsProvider = new WsProvider(config.substrateUrl);18  return { provider: wsProvider, types: rtt };19}2021export default async function usingApi(action: (api: ApiPromise) => Promise<void>, settings: ApiOptions | undefined = undefined): Promise<void> {22  settings = settings || defaultApiOptions();23  let api: ApiPromise = new ApiPromise(settings);2425  // TODO: Remove, this is temporary: Filter unneeded API output 26  // (Jaco promised it will be removed in the next version)27  const consoleLog = console.log;28  console.log = (message: string) => {29    if (message.includes("API/INIT: Capabilities detected") || message.includes("2021-")) {}30    else if (message.includes("StorageChangeSet:: WebSocket is not connected") || message.includes("2021-")) {}31    else consoleLog(message);32  };33  const consoleErr = console.error;34  console.error = (message: string) => {35    if (message.includes("StorageChangeSet:: WebSocket is not connected") || message.includes("2021-")) {}36    else consoleErr(message);37  };3839  try {40    await promisifySubstrate(api, async () => {41      if(api) {42        await api.isReadyOrError;43        await action(api);44      }45    })();46  } finally {47    await api.disconnect();48    console.log = consoleLog;49    console.error = consoleErr;50  }51}5253enum TransactionStatus {54  Success,55  Fail,56  NotReady57}5859function getTransactionStatus(events: EventRecord[], status: ExtrinsicStatus): TransactionStatus {60  if (status.isReady) {61    return TransactionStatus.NotReady;62  }63  if (status.isBroadcast) {64    return TransactionStatus.NotReady;65  } 66  if (status.isInBlock || status.isFinalized) {67    if(events.filter(e => e.event.data.method === 'ExtrinsicFailed').length > 0) {68      return TransactionStatus.Fail;69    }70    if(events.filter(e => e.event.data.method === 'ExtrinsicSuccess').length > 0) {71      return TransactionStatus.Success;72    }73  }7475  return TransactionStatus.Fail;76}7778export function79submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {80  return new Promise(async (resolve, reject) => {81    try {82      await transaction.signAndSend(sender, ({ events = [], status }) => {83        const transactionStatus = getTransactionStatus(events, status);8485        if (transactionStatus === TransactionStatus.Success) {86          resolve(events);87        } else if (transactionStatus === TransactionStatus.Fail) {88          console.log(`Something went wrong with transaction. Status: ${status}`);89          reject(events);90        }91      });92    } catch (e) {93      console.log('Error: ', e);94      reject(e);95    }96  });97}9899export function submitTransactionExpectFailAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {100  const consoleError = console.error;101  const consoleLog = console.log;102  console.error = () => {};103  console.log = () => {};104105  return new Promise<EventRecord[]>(async function(res, rej) {106    const resolve = (rec: EventRecord[]) => {107      setTimeout(() => {108        res(rec);109        console.error = consoleError;110        console.log = consoleLog;111      });112    };113    const reject = (errror: any) => {114      setTimeout(() => {115        rej(errror);116        console.error = consoleError;117        console.log = consoleLog;118      });119    };120    try {121      await transaction.signAndSend(sender, ({ events = [], status }) => {122        const transactionStatus = getTransactionStatus(events, status);123124        // console.log('transactionStatus', transactionStatus, 'events', events);125126        if (transactionStatus == TransactionStatus.Success) {127          resolve(events);128        } else if (transactionStatus == TransactionStatus.Fail) {129          reject(events);130        }131      });132    } catch (e) {133      reject(e);134    }135  });136}
after · tests/src/substrate/substrate-api.ts
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56import { WsProvider, ApiPromise } from "@polkadot/api";7import { EventRecord } from '@polkadot/types/interfaces/system/types';8import { ExtrinsicStatus } from '@polkadot/types/interfaces/author/types';9import { IKeyringPair } from "@polkadot/types/types";1011import config from "../config";12import promisifySubstrate from "./promisify-substrate";13import { ApiOptions, SubmittableExtrinsic, ApiTypes } from "@polkadot/api/types";14import rtt from "../../../runtime_types.json";1516function defaultApiOptions(): ApiOptions {17  const wsProvider = new WsProvider(config.substrateUrl);18  return { provider: wsProvider, types: rtt };19}2021export default async function usingApi(action: (api: ApiPromise) => Promise<void>, settings: ApiOptions | undefined = undefined): Promise<void> {22  settings = settings || defaultApiOptions();23  let api: ApiPromise = new ApiPromise(settings);2425  // TODO: Remove, this is temporary: Filter unneeded API output 26  // (Jaco promised it will be removed in the next version)27  const consoleLog = console.log;28  console.log = (message: string) => {29    if (message.includes("API/INIT: Capabilities detected") || message.includes("2021-")) {}30    else if (message.includes("StorageChangeSet:: WebSocket is not connected") || message.includes("2021-")) {}31    else consoleLog(message);32  };33  const consoleErr = console.error;34  console.error = (message: string) => {35    if (message.includes("StorageChangeSet:: WebSocket is not connected") || message.includes("2021-")) {}36    else consoleErr(message);37  };3839  try {40    await promisifySubstrate(api, async () => {41      if(api) {42        await api.isReadyOrError;43        await action(api);44      }45    })();46  } finally {47    await api.disconnect();48    console.log = consoleLog;49    console.error = consoleErr;50  }51}5253enum TransactionStatus {54  Success,55  Fail,56  NotReady57}5859function getTransactionStatus(events: EventRecord[], status: ExtrinsicStatus): TransactionStatus {60  if (status.isReady) {61    return TransactionStatus.NotReady;62  }63  if (status.isBroadcast) {64    return TransactionStatus.NotReady;65  } 66  if (status.isInBlock || status.isFinalized) {67    if(events.filter(e => e.event.data.method === 'ExtrinsicFailed').length > 0) {68      return TransactionStatus.Fail;69    }70    if(events.filter(e => e.event.data.method === 'ExtrinsicSuccess').length > 0) {71      return TransactionStatus.Success;72    }73  }7475  return TransactionStatus.Fail;76}7778export function79submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {80  return new Promise(async (resolve, reject) => {81    try {82      await transaction.signAndSend(sender, ({ events = [], status }) => {83        const transactionStatus = getTransactionStatus(events, status);8485        if (transactionStatus === TransactionStatus.Success) {86          resolve(events);87        } else if (transactionStatus === TransactionStatus.Fail) {88          console.log(`Something went wrong with transaction. Status: ${status}`);89          reject(events);90        }91      });92    } catch (e) {93      console.log('Error: ', e);94      reject(e);95    }96  });97}9899export function submitTransactionExpectFailAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {100  const consoleError = console.error;101  const consoleLog = console.log;102  console.error = () => {};103  console.log = () => {};104105  return new Promise<EventRecord[]>(async function(res, rej) {106    const resolve = (rec: EventRecord[]) => {107      setTimeout(() => {108        res(rec);109        console.error = consoleError;110        console.log = consoleLog;111      });112    };113    const reject = (errror: any) => {114      setTimeout(() => {115        rej(errror);116        console.error = consoleError;117        console.log = consoleLog;118      });119    };120    try {121      await transaction.signAndSend(sender, ({ events = [], status }) => {122        const transactionStatus = getTransactionStatus(events, status);123124        // console.log('transactionStatus', transactionStatus, 'events', events);125126        if (transactionStatus === TransactionStatus.Success) {127          resolve(events);128        } else if (transactionStatus === TransactionStatus.Fail) {129          reject(events);130        }131      });132    } catch (e) {133      reject(e);134    }135  });136}
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -51,7 +51,7 @@
   Value: BN;
 }
 
-interface IReFungibleTokenDataType {
+export interface IReFungibleTokenDataType {
   Owner: IReFungibleOwner[];
   ConstData: number[];
   VariableData: number[];