--- a/tests/src/addToWhiteList.test.ts +++ b/tests/src/addToWhiteList.test.ts @@ -16,6 +16,7 @@ enablePublicMintingExpectSuccess, enableWhiteListExpectSuccess, normalizeAccountId, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); @@ -23,6 +24,7 @@ let Alice: IKeyringPair; let Bob: IKeyringPair; +let Charlie: IKeyringPair; describe('Integration Test ext. addToWhiteList()', () => { @@ -85,3 +87,32 @@ }); }); + +describe('Integration Test ext. addToWhiteList() with collection admin permissions:', () => { + + before(async () => { + await usingApi(async () => { + Alice = privateKey('//Alice'); + Bob = privateKey('//Bob'); + Charlie = privateKey('//Charlie'); + }); + }); + + it('Execute the extrinsic with parameters: Collection ID and address to add to the white list', async () => { + const collectionId = await createCollectionExpectSuccess(); + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + await addToWhiteListExpectSuccess(Bob, collectionId, Charlie.address); + }); + + it('Whitelisted minting: list restrictions', async () => { + const collectionId = await createCollectionExpectSuccess(); + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + await addToWhiteListExpectSuccess(Bob, collectionId, Charlie.address); + + // allowed only for collection owner + await enableWhiteListExpectSuccess(Alice, collectionId); + await enablePublicMintingExpectSuccess(Alice, collectionId); + + await createItemExpectSuccess(Charlie, collectionId, 'NFT', Charlie.address); + }); +}); \ No newline at end of file --- a/tests/src/approve.test.ts +++ b/tests/src/approve.test.ts @@ -16,6 +16,7 @@ destroyCollectionExpectSuccess, setCollectionLimitsExpectSuccess, transferExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); @@ -177,3 +178,25 @@ await approveExpectFail(collectionId, itemId, Alice, Charlie); }); }); + +describe('Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:', () => { + let Alice: IKeyringPair; + let Bob: IKeyringPair; + let Charlie: IKeyringPair; + + before(async () => { + await usingApi(async () => { + Alice = privateKey('//Alice'); + Bob = privateKey('//Bob'); + Charlie = privateKey('//Charlie'); + }); + }); + + it('can be called by collection admin on non-owned item', async () => { + const collectionId = await createCollectionExpectSuccess(); + const itemId = await createItemExpectSuccess(Alice, collectionId, 'NFT', Alice.address); + + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + await approveExpectSuccess(collectionId, itemId, Bob, Charlie); + }); +}); --- a/tests/src/burnItem.test.ts +++ b/tests/src/burnItem.test.ts @@ -12,6 +12,7 @@ getGenericResult, destroyCollectionExpectSuccess, normalizeAccountId, + addCollectionAdminExpectSuccess, } from './util/helpers'; import chai from 'chai'; @@ -48,8 +49,8 @@ // tslint:disable-next-line:no-unused-expression expect(item).to.be.null; }); - }); + it('Burn item in Fungible collection', async () => { const createMode = 'Fungible'; const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0 }}); @@ -70,8 +71,8 @@ expect(balance).to.be.not.null; expect(balance.Value).to.be.equal(9); }); + }); - }); it('Burn item in ReFungible collection', async () => { const createMode = 'ReFungible'; const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }}); @@ -89,7 +90,6 @@ expect(result.success).to.be.true; expect(balance).to.be.null; }); - }); it('Burn owned portion of item in ReFungible collection', async () => { @@ -136,6 +136,36 @@ }); +describe('integration test: ext. burnItem() with admin permissions:', () => { + before(async () => { + await usingApi(async () => { + const keyring = new Keyring({ type: 'sr25519' }); + alice = keyring.addFromUri('//Alice'); + bob = keyring.addFromUri('//Bob'); + }); + }); + + it('Burn item in NFT collection', async () => { + const createMode = 'NFT'; + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}}); + const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + + await usingApi(async (api) => { + const tx = api.tx.nft.burnItem(collectionId, tokenId, 0); + const events = await submitTransactionAsync(bob, 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.null; + }); + }); +}); + describe('Negative integration test: ext. burnItem():', () => { before(async () => { await usingApi(async () => { --- a/tests/src/change-collection-owner.test.ts +++ b/tests/src/change-collection-owner.test.ts @@ -7,7 +7,7 @@ import chaiAsPromised from 'chai-as-promised'; import privateKey from './substrate/privateKey'; import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api'; -import { createCollectionExpectSuccess } from './util/helpers'; +import { createCollectionExpectSuccess, addCollectionAdminExpectSuccess } from './util/helpers'; chai.use(chaiAsPromised); const expect = chai.expect; @@ -48,6 +48,26 @@ await createCollectionExpectSuccess(); }); }); + + it('Collection admin can\'t change owner.', async () => { + await usingApi(async api => { + const collectionId = await createCollectionExpectSuccess(); + const alice = privateKey('//Alice'); + const bob = privateKey('//Bob'); + + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + + const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address); + await expect(submitTransactionExpectFailAsync(bob, changeOwnerTx)).to.be.rejected; + + const collectionAfterOwnerChange: any = (await api.query.nft.collectionById(collectionId)).toJSON(); + expect(collectionAfterOwnerChange.Owner).to.be.deep.eq(alice.address); + + // Verifying that nothing bad happened (network is live, new collections can be created, etc.) + await createCollectionExpectSuccess(); + }); + }); + it('Can\'t change owner of a non-existing collection.', async () => { await usingApi(async api => { const collectionId = (1<<32) - 1; --- a/tests/src/confirmSponsorship.test.ts +++ b/tests/src/confirmSponsorship.test.ts @@ -19,6 +19,7 @@ enablePublicMintingExpectSuccess, addToWhiteListExpectSuccess, normalizeAccountId, + addCollectionAdminExpectSuccess, } from './util/helpers'; import { Keyring } from '@polkadot/api'; import { IKeyringPair } from '@polkadot/types/types'; @@ -365,6 +366,13 @@ await confirmSponsorshipExpectFailure(collectionId, '//Alice'); }); + it('(!negative test!) Confirm sponsorship by collection admin', async () => { + const collectionId = await createCollectionExpectSuccess(); + await setCollectionSponsorExpectSuccess(collectionId, bob.address); + await addCollectionAdminExpectSuccess(alice, collectionId, charlie); + await confirmSponsorshipExpectFailure(collectionId, '//Charlie'); + }); + it('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async () => { const collectionId = await createCollectionExpectSuccess(); await confirmSponsorshipExpectFailure(collectionId, '//Bob'); --- a/tests/src/createItem.test.ts +++ b/tests/src/createItem.test.ts @@ -4,20 +4,25 @@ // import { default as usingApi } from './substrate/substrate-api'; +import chai from 'chai'; import { Keyring } from '@polkadot/api'; import { IKeyringPair } from '@polkadot/types/types'; import { createCollectionExpectSuccess, createItemExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; +const expect = chai.expect; let alice: IKeyringPair; +let bob: IKeyringPair; describe('integration test: ext. createItem():', () => { before(async () => { await usingApi(async () => { const keyring = new Keyring({ type: 'sr25519' }); alice = keyring.addFromUri('//Alice'); + bob = keyring.addFromUri('//Bob'); }); }); @@ -36,4 +41,48 @@ const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}}); await createItemExpectSuccess(alice, newCollectionID, createMode); }); + it('Create new item in NFT collection with collection admin permissions', async () => { + const createMode = 'NFT'; + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}}); + await addCollectionAdminExpectSuccess(alice, newCollectionID, bob); + await createItemExpectSuccess(bob, newCollectionID, createMode); + }); + it('Create new item in Fungible collection with collection admin permissions', async () => { + const createMode = 'Fungible'; + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}}); + await addCollectionAdminExpectSuccess(alice, newCollectionID, bob); + await createItemExpectSuccess(bob, newCollectionID, createMode); + }); + it('Create new item in ReFungible collection with collection admin permissions', async () => { + const createMode = 'ReFungible'; + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}}); + await addCollectionAdminExpectSuccess(alice, newCollectionID, bob); + await createItemExpectSuccess(bob, newCollectionID, createMode); + }); +}); + +describe('Negative integration test: ext. createItem():', () => { + before(async () => { + await usingApi(async () => { + const keyring = new Keyring({ type: 'sr25519' }); + alice = keyring.addFromUri('//Alice'); + bob = keyring.addFromUri('//Bob'); + }); + }); + + it('Regular user cannot create new item in NFT collection', async () => { + const createMode = 'NFT'; + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}}); + await expect(createItemExpectSuccess(bob, newCollectionID, createMode)).to.be.rejected; + }); + it('Regular user cannot create new item in Fungible collection', async () => { + const createMode = 'Fungible'; + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}}); + await expect(createItemExpectSuccess(bob, newCollectionID, createMode)).to.be.rejected; + }); + it('Regular user cannot create new item in ReFungible collection', async () => { + const createMode = 'ReFungible'; + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}}); + await expect(createItemExpectSuccess(bob, newCollectionID, createMode)).to.be.rejected; + }); }); --- a/tests/src/createMultipleItems.test.ts +++ b/tests/src/createMultipleItems.test.ts @@ -3,6 +3,7 @@ // file 'LICENSE', which is part of this source code package. // import { ApiPromise } from '@polkadot/api'; +import { IKeyringPair } from '@polkadot/types/types'; import BN from 'bn.js'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; @@ -12,9 +13,11 @@ createCollectionExpectSuccess, destroyCollectionExpectSuccess, getGenericResult, + IFungibleTokenDataType, IReFungibleTokenDataType, normalizeAccountId, setCollectionLimitsExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); @@ -57,6 +60,26 @@ }); }); + it('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', 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 = [ + {fungible: { value: 1 }}, + {fungible: { value: 2 }}, + {fungible: { value: 3 }}, + ]; + const createMultipleItemsTx = api.tx.nft + .createMultipleItems(collectionId, normalizeAccountId(Alice.address), args); + await submitTransactionAsync(Alice, createMultipleItemsTx); + const token1Data = (await api.query.nft.fungibleItemList(collectionId, Alice.address) as any).toJSON() as unknown as IFungibleTokenDataType; + + expect(token1Data.Value).to.be.equal(6); // 1 + 2 + 3 + }); + }); + 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'}}); @@ -116,11 +139,167 @@ }); }); +describe('Integration Test createMultipleItems(collection_id, owner, items_data) with collection admin permissions:', () => { + + let Alice: IKeyringPair; + let Bob: IKeyringPair; + + before(async () => { + await usingApi(async () => { + Alice = privateKey('//Alice'); + Bob = privateKey('//Bob'); + }); + }); + + 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); + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + const args = [{ nft: ['0x31', '0x31'] }, { nft: ['0x32', '0x32'] }, { nft: ['0x33', '0x33'] }]; + const createMultipleItemsTx = api.tx.nft + .createMultipleItems(collectionId, normalizeAccountId(Bob.address), args); + await submitTransactionAsync(Bob, 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)).toJSON() as unknown as ITokenDataType; + const token2Data = (await api.query.nft.nftItemList(collectionId, 2)).toJSON() as unknown as ITokenDataType; + const token3Data = (await api.query.nft.nftItemList(collectionId, 3)).toJSON() as unknown as ITokenDataType; + + expect(token1Data.Owner).to.be.deep.equal(normalizeAccountId(Bob.address)); + expect(token2Data.Owner).to.be.deep.equal(normalizeAccountId(Bob.address)); + expect(token3Data.Owner).to.be.deep.equal(normalizeAccountId(Bob.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('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN; + expect(itemsListIndexBefore.toNumber()).to.be.equal(0); + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + const args = [ + {fungible: { value: 1 }}, + {fungible: { value: 2 }}, + {fungible: { value: 3 }}, + ]; + const createMultipleItemsTx = api.tx.nft + .createMultipleItems(collectionId, normalizeAccountId(Bob.address), args); + await submitTransactionAsync(Bob, createMultipleItemsTx); + const token1Data = (await api.query.nft.fungibleItemList(collectionId, Bob.address) as any).toJSON() as unknown as IFungibleTokenDataType; + + expect(token1Data.Value).to.be.equal(6); // 1 + 2 + 3 + }); + }); + + 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'}}); + const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN; + expect(itemsListIndexBefore.toNumber()).to.be.equal(0); + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + const args = [ + {refungible: {const_data: [0x31], variable_data: [0x31], pieces: 1}}, + {refungible: {const_data: [0x32], variable_data: [0x32], pieces: 1}}, + {refungible: {const_data: [0x33], variable_data: [0x33], pieces: 1}}, + ]; + const createMultipleItemsTx = api.tx.nft + .createMultipleItems(collectionId, normalizeAccountId(Bob.address), args); + await submitTransactionAsync(Bob, 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 any).toJSON() as unknown as IReFungibleTokenDataType; + const token2Data = (await api.query.nft.reFungibleItemList(collectionId, 2) as any).toJSON() as unknown as IReFungibleTokenDataType; + const token3Data = (await api.query.nft.reFungibleItemList(collectionId, 3) as any).toJSON() as unknown as IReFungibleTokenDataType; + + expect(token1Data.Owner[0].Owner).to.be.deep.equal(normalizeAccountId(Bob.address)); + expect(token1Data.Owner[0].Fraction).to.be.equal(1); + + expect(token2Data.Owner[0].Owner).to.be.deep.equal(normalizeAccountId(Bob.address)); + expect(token2Data.Owner[0].Fraction).to.be.equal(1); + + expect(token3Data.Owner[0].Owner).to.be.deep.equal(normalizeAccountId(Bob.address)); + expect(token3Data.Owner[0].Fraction).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'); + }); + }); +}); + describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => { + + let Alice: IKeyringPair; + let Bob: IKeyringPair; + + before(async () => { + await usingApi(async () => { + Alice = privateKey('//Alice'); + Bob = privateKey('//Bob'); + }); + }); + + it('Regular user cannot create items in active NFT collection', 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 args = [{ nft: ['0x31', '0x31'] }, { nft: ['0x32', '0x32'] }, { nft: ['0x33', '0x33'] }]; + const createMultipleItemsTx = api.tx.nft + .createMultipleItems(collectionId, normalizeAccountId(Alice.address), args); + await expect(submitTransactionAsync(Bob, createMultipleItemsTx)).to.be.rejected; + }); + }); + + it('Regular user cannot create items in active Fungible collection', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN; + expect(itemsListIndexBefore.toNumber()).to.be.equal(0); + const args = [ + {fungible: { value: 1 }}, + {fungible: { value: 2 }}, + {fungible: { value: 3 }}, + ]; + const createMultipleItemsTx = api.tx.nft + .createMultipleItems(collectionId, normalizeAccountId(Alice.address), args); + await expect(submitTransactionAsync(Bob, createMultipleItemsTx)).to.be.rejected; + }); + }); + + it('Regular user cannot create items in active ReFungible collection', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); + const itemsListIndexBefore = await api.query.nft.itemListIndex(collectionId) as unknown as BN; + expect(itemsListIndexBefore.toNumber()).to.be.equal(0); + const args = [ + {refungible: {const_data: [0x31], variable_data: [0x31], pieces: 1}}, + {refungible: {const_data: [0x32], variable_data: [0x32], pieces: 1}}, + {refungible: {const_data: [0x33], variable_data: [0x33], pieces: 1}}, + ]; + const createMultipleItemsTx = api.tx.nft + .createMultipleItems(collectionId, normalizeAccountId(Alice.address), args); + await expect(submitTransactionAsync(Bob, createMultipleItemsTx)).to.be.rejected; + }); + }); + 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 @@ -136,7 +315,6 @@ 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 = api.tx.nft .createMultipleItems(collectionId, normalizeAccountId(Alice.address), ['NFT', 'NFT', 'NFT']); await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected; @@ -174,7 +352,6 @@ it('Create tokens with different types', async () => { await usingApi(async (api: ApiPromise) => { const collectionId = await createCollectionExpectSuccess(); - const Alice = privateKey('//Alice'); const createMultipleItemsTx = api.tx.nft .createMultipleItems(collectionId, normalizeAccountId(Alice.address), ['NFT', 'Fungible', 'ReFungible']); await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected; @@ -186,7 +363,6 @@ 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)] }, @@ -200,18 +376,17 @@ it('Fails when minting tokens exceeds collectionLimits amount', async () => { await usingApi(async (api) => { - const alice = privateKey('//Alice'); const collectionId = await createCollectionExpectSuccess(); - await setCollectionLimitsExpectSuccess(alice, collectionId, { + await setCollectionLimitsExpectSuccess(Alice, collectionId, { TokenLimit: 1, }); const args = [ { nft: ['A', 'A'] }, { nft: ['B', 'B'] }, ]; - const createMultipleItemsTx = api.tx.nft.createMultipleItems(collectionId, normalizeAccountId(alice.address), args); - await expect(submitTransactionExpectFailAsync(alice, createMultipleItemsTx)).to.be.rejected; + const createMultipleItemsTx = api.tx.nft.createMultipleItems(collectionId, normalizeAccountId(Alice.address), args); + await expect(submitTransactionExpectFailAsync(Alice, createMultipleItemsTx)).to.be.rejected; }); }); }); --- a/tests/src/destroyCollection.test.ts +++ b/tests/src/destroyCollection.test.ts @@ -8,7 +8,12 @@ import chaiAsPromised from 'chai-as-promised'; import privateKey from './substrate/privateKey'; import { default as usingApi } from './substrate/substrate-api'; -import { createCollectionExpectSuccess, destroyCollectionExpectSuccess, destroyCollectionExpectFailure, setCollectionLimitsExpectSuccess } from './util/helpers'; +import { createCollectionExpectSuccess, + destroyCollectionExpectSuccess, + destroyCollectionExpectFailure, + setCollectionLimitsExpectSuccess, + addCollectionAdminExpectSuccess, +} from './util/helpers'; chai.use(chaiAsPromised); @@ -29,10 +34,12 @@ describe('(!negative test!) integration test: ext. destroyCollection():', () => { let alice: IKeyringPair; + let bob: IKeyringPair; before(async () => { await usingApi(async () => { alice = privateKey('//Alice'); + bob = privateKey('//Bob'); }); }); @@ -53,6 +60,11 @@ await destroyCollectionExpectFailure(collectionId, '//Bob'); await destroyCollectionExpectSuccess(collectionId, '//Alice'); }); + it('(!negative test!) Destroy a collection using collection admin account', async () => { + const collectionId = await createCollectionExpectSuccess(); + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + await destroyCollectionExpectFailure(collectionId, '//Bob'); + }); it('fails when OwnerCanDestroy == false', async () => { const collectionId = await createCollectionExpectSuccess(); await setCollectionLimitsExpectSuccess(alice, collectionId, { OwnerCanDestroy: false }); --- a/tests/src/removeCollectionAdmin.test.ts +++ b/tests/src/removeCollectionAdmin.test.ts @@ -83,4 +83,22 @@ await createCollectionExpectSuccess(); }); }); + + it('Regular user Can\'t remove collection admin', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess(); + const Alice = privateKey('//Alice'); + const Bob = privateKey('//Bob'); + const Charlie = privateKey('//Charlie'); + + const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(Bob.address)); + await submitTransactionAsync(Alice, addAdminTx); + + const changeOwnerTx = api.tx.nft.removeCollectionAdmin(collectionId, normalizeAccountId(Bob.address)); + await expect(submitTransactionExpectFailAsync(Charlie, changeOwnerTx)).to.be.rejected; + + // Verifying that nothing bad happened (network is live, new collections can be created, etc.) + await createCollectionExpectSuccess(); + }); + }); }); --- a/tests/src/removeCollectionSponsor.test.ts +++ b/tests/src/removeCollectionSponsor.test.ts @@ -17,6 +17,7 @@ removeCollectionSponsorExpectSuccess, removeCollectionSponsorExpectFailure, normalizeAccountId, + addCollectionAdminExpectSuccess, } from './util/helpers'; import { Keyring } from '@polkadot/api'; import { IKeyringPair } from '@polkadot/types/types'; @@ -104,6 +105,13 @@ await removeCollectionSponsorExpectFailure(collectionId); }); + it('(!negative test!) Remove sponsor for a collection with collection admin permissions', async () => { + const collectionId = await createCollectionExpectSuccess(); + await setCollectionSponsorExpectSuccess(collectionId, bob.address); + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + await removeCollectionSponsorExpectFailure(collectionId, '//Bob'); + }); + it('(!negative test!) Remove sponsor in a destroyed collection', async () => { const collectionId = await createCollectionExpectSuccess(); await setCollectionSponsorExpectSuccess(collectionId, bob.address); --- a/tests/src/removeFromWhiteList.test.ts +++ b/tests/src/removeFromWhiteList.test.ts @@ -17,6 +17,7 @@ removeFromWhiteListExpectFailure, disableWhiteListExpectSuccess, normalizeAccountId, + addCollectionAdminExpectSuccess, } from './util/helpers'; import { IKeyringPair } from '@polkadot/types/types'; import privateKey from './substrate/privateKey'; @@ -88,3 +89,39 @@ }); }); }); + +describe('Integration Test removeFromWhiteList with collection admin permissions', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingApi(async () => { + alice = privateKey('//Alice'); + bob = privateKey('//Bob'); + charlie = privateKey('//Charlie'); + }); + }); + + it('ensure address is not in whitelist after removal', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await enableWhiteListExpectSuccess(alice, collectionId); + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + await addToWhiteListExpectSuccess(alice, collectionId, charlie.address); + await removeFromWhiteListExpectSuccess(bob, collectionId, normalizeAccountId(charlie.address)); + expect(await isWhitelisted(collectionId, charlie.address)).to.be.false; + }); + }); + + it('Collection admin allowed to remove from whitelist with unset whitelist status', async () => { + await usingApi(async () => { + const collectionWithoutWhitelistId = await createCollectionExpectSuccess(); + await enableWhiteListExpectSuccess(alice, collectionWithoutWhitelistId); + await addCollectionAdminExpectSuccess(alice, collectionWithoutWhitelistId, bob); + await addToWhiteListExpectSuccess(alice, collectionWithoutWhitelistId, charlie.address); + await disableWhiteListExpectSuccess(alice, collectionWithoutWhitelistId); + await removeFromWhiteListExpectSuccess(bob, collectionWithoutWhitelistId, normalizeAccountId(charlie.address)); + }); + }); +}); \ No newline at end of file --- /dev/null +++ b/tests/src/setChainLimits.test.ts @@ -0,0 +1,56 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import { IKeyringPair } from '@polkadot/types/types'; +import privateKey from './substrate/privateKey'; +import usingApi from './substrate/substrate-api'; +import { + createCollectionExpectSuccess, + addCollectionAdminExpectSuccess, + setChainLimitsExpectFailure, + IChainLimits, +} from './util/helpers'; + +describe.only('Negative Integration Test setChainLimits', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let dave: IKeyringPair; + let limits: IChainLimits; + + before(async () => { + await usingApi(async () => { + alice = privateKey('//Alice'); + bob = privateKey('//Bob'); + dave = privateKey('//Dave'); + limits = { + CollectionNumbersLimit : 1, + AccountTokenOwnershipLimit: 1, + CollectionsAdminsLimit: 1, + CustomDataLimit: 1, + NftSponsorTransferTimeout: 1, + FungibleSponsorTransferTimeout: 1, + RefungibleSponsorTransferTimeout: 1, + OffchainSchemaLimit: 1, + VariableOnChainSchemaLimit: 1, + ConstOnChainSchemaLimit: 1, + }; + }); + }); + + it('Collection owner cannot set chain limits', async () => { + await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await setChainLimitsExpectFailure(alice, limits); + }); + + it('Collection admin cannot set chain limits', async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + await setChainLimitsExpectFailure(bob, limits); + }); + + it('Regular user cannot set chain limits', async () => { + await setChainLimitsExpectFailure(dave, limits); + }); +}); --- a/tests/src/setCollectionLimits.test.ts +++ b/tests/src/setCollectionLimits.test.ts @@ -16,6 +16,7 @@ getDetailedCollectionInfo, setCollectionLimitsExpectFailure, setCollectionLimitsExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); @@ -150,6 +151,21 @@ await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected; }); }); + it('execute setCollectionLimits from admin collection', async () => { + await addCollectionAdminExpectSuccess(alice, collectionIdForTesting, bob); + await usingApi(async (api: ApiPromise) => { + tx = api.tx.nft.setCollectionLimits( + collectionIdForTesting, + { + accountTokenOwnershipLimit, + sponsoredDataSize, + sponsoredMintSize, + tokenLimit, + }, + ); + await expect(submitTransactionExpectFailAsync(bob, tx)).to.be.rejected; + }); + }); it('execute setCollectionLimits with incorrect limits', async () => { await usingApi(async (api: ApiPromise) => { tx = api.tx.nft.setCollectionLimits( --- a/tests/src/setCollectionSponsor.test.ts +++ b/tests/src/setCollectionSponsor.test.ts @@ -6,19 +6,27 @@ import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; import { default as usingApi } from './substrate/substrate-api'; -import { createCollectionExpectSuccess, setCollectionSponsorExpectSuccess, destroyCollectionExpectSuccess, setCollectionSponsorExpectFailure } from './util/helpers'; +import { createCollectionExpectSuccess, + setCollectionSponsorExpectSuccess, + destroyCollectionExpectSuccess, + setCollectionSponsorExpectFailure, + addCollectionAdminExpectSuccess, +} from './util/helpers'; import { Keyring } from '@polkadot/api'; import { IKeyringPair } from '@polkadot/types/types'; chai.use(chaiAsPromised); +let alice: IKeyringPair; let bob: IKeyringPair; +let charlie: IKeyringPair; describe('integration test: ext. setCollectionSponsor():', () => { before(async () => { await usingApi(async () => { const keyring = new Keyring({ type: 'sr25519' }); + alice = keyring.addFromUri('//Alice'); bob = keyring.addFromUri('//Bob'); }); }); @@ -55,7 +63,9 @@ before(async () => { await usingApi(async () => { const keyring = new Keyring({ type: 'sr25519' }); + alice = keyring.addFromUri('//Alice'); bob = keyring.addFromUri('//Bob'); + charlie = keyring.addFromUri('//Charlie'); }); }); @@ -77,4 +87,9 @@ await destroyCollectionExpectSuccess(collectionId); await setCollectionSponsorExpectFailure(collectionId, bob.address); }); + it('(!negative test!) Collection admin add sponsor', async () => { + const collectionId = await createCollectionExpectSuccess(); + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + await setCollectionSponsorExpectFailure(collectionId, charlie.address, '//Bob'); + }); }); --- a/tests/src/setConstOnChainSchema.test.ts +++ b/tests/src/setConstOnChainSchema.test.ts @@ -11,6 +11,7 @@ import { createCollectionExpectSuccess, destroyCollectionExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); @@ -43,6 +44,17 @@ }); }); + it('Collection admin can set the scheme', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON(); + expect(collection.Owner).to.be.eq(Alice.address); + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + const setShema = api.tx.nft.setConstOnChainSchema(collectionId, Shema); + await submitTransactionAsync(Bob, setShema); + }); + }); + it('Checking collection data using the ConstOnChainSchema parameter', async () => { await usingApi(async (api) => { const collectionId = await createCollectionExpectSuccess(); --- a/tests/src/setMintPermission.test.ts +++ b/tests/src/setMintPermission.test.ts @@ -16,6 +16,7 @@ findNotExistingCollection, setMintPermissionExpectFailure, setMintPermissionExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; describe('Integration Test setMintPermission', () => { @@ -91,6 +92,14 @@ await setMintPermissionExpectFailure(bob, collectionId, true); }); + it('Collection admin fails on set', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + await setMintPermissionExpectFailure(bob, collectionId, true); + }); + }); + it('ensure non-white-listed non-privileged address can\'t mint tokens', async () => { await usingApi(async () => { const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); --- a/tests/src/setOffchainSchema.test.ts +++ b/tests/src/setOffchainSchema.test.ts @@ -15,6 +15,7 @@ queryCollectionExpectSuccess, setOffchainSchemaExpectFailure, setOffchainSchemaExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); @@ -24,10 +25,12 @@ describe('Integration Test setOffchainSchema', () => { let alice: IKeyringPair; + let bob: IKeyringPair; before(async () => { await usingApi(async () => { alice = privateKey('//Alice'); + bob = privateKey('//Bob'); }); }); @@ -38,6 +41,15 @@ expect(collection.OffchainSchema).to.be.equal('0x' + Buffer.from(DATA).toString('hex')); }); + + it('execute setOffchainSchema (collection admin), verify data was set', async () => { + const collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + await setOffchainSchemaExpectSuccess(bob, collectionId, DATA); + const collection = await queryCollectionExpectSuccess(collectionId); + + expect(collection.OffchainSchema).to.be.equal('0x' + Buffer.from(DATA).toString('hex')); + }); }); describe('Negative Integration Test setOffchainSchema', () => { --- a/tests/src/setPublicAccessMode.test.ts +++ b/tests/src/setPublicAccessMode.test.ts @@ -18,6 +18,7 @@ enablePublicMintingExpectSuccess, enableWhiteListExpectSuccess, normalizeAccountId, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); @@ -92,3 +93,21 @@ }); }); }); + +describe('Negative Integration Test ext. collection admin setPublicAccessMode(): ', () => { + before(async () => { + await usingApi(async () => { + Alice = privateKey('//Alice'); + Bob = privateKey('//Bob'); + }); + }); + it('Set the collection that has been deleted', async () => { + await usingApi(async (api: ApiPromise) => { + // tslint:disable-next-line: no-bitwise + const collectionId = await createCollectionExpectSuccess(); + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList'); + await expect(submitTransactionExpectFailAsync(Bob, tx)).to.be.rejected; + }); + }); +}); --- a/tests/src/setSchemaVersion.test.ts +++ b/tests/src/setSchemaVersion.test.ts @@ -16,12 +16,14 @@ getCreatedCollectionCount, getCreateItemResult, getDetailedCollectionInfo, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); const expect = chai.expect; let alice: IKeyringPair; +let bob: IKeyringPair; let collectionIdForTesting: number; /* @@ -66,11 +68,37 @@ expect(collectionInfo ? collectionInfo.SchemaVersion.toString() : '').to.be.equal('Unique'); }); }); +}); + +describe('Collection admin setSchemaVersion positive', () => { + let tx; + before(async () => { + await usingApi(async () => { + const keyring = new Keyring({ type: 'sr25519' }); + alice = keyring.addFromUri('//Alice'); + bob = keyring.addFromUri('//Bob'); + await addCollectionAdminExpectSuccess(alice, collectionIdForTesting, bob); + }); + }); + it('execute setSchemaVersion with image url and unique ', async () => { + await usingApi(async (api: ApiPromise) => { + tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'Unique'); + const events = await submitTransactionAsync(bob, tx); + const result = getCreateItemResult(events); + const collectionInfo = await getDetailedCollectionInfo(api, collectionIdForTesting) as ICollectionInterface; + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.true; + // tslint:disable-next-line:no-unused-expression + expect(collectionInfo).to.be.exist; + // tslint:disable-next-line:no-unused-expression + expect(collectionInfo ? collectionInfo.SchemaVersion.toString() : '').to.be.equal('Unique'); + }); + }); it('validate schema version with just entered data', async () => { await usingApi(async (api: ApiPromise) => { tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'ImageURL'); - const events = await submitTransactionAsync(alice, tx); + const events = await submitTransactionAsync(bob, tx); const result = getCreateItemResult(events); const collectionInfo = await getDetailedCollectionInfo(api, collectionIdForTesting) as ICollectionInterface; // tslint:disable-next-line:no-unused-expression --- a/tests/src/setVariableMetaData.test.ts +++ b/tests/src/setVariableMetaData.test.ts @@ -16,6 +16,7 @@ findNotExistingCollection, setVariableMetaDataExpectFailure, setVariableMetaDataExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); @@ -48,6 +49,36 @@ }); }); +describe('Integration Test collection admin setVariableMetaData', () => { + const data = [1, 2, 254, 255]; + + let alice: IKeyringPair; + let bob: IKeyringPair; + let collectionId: number; + let tokenId: number; + before(async () => { + await usingApi(async () => { + alice = privateKey('//Alice'); + bob = privateKey('//Bob'); + collectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } }); + tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT'); + await addCollectionAdminExpectSuccess(alice, collectionId, bob); + }); + }); + + it('execute setVariableMetaData', async () => { + await setVariableMetaDataExpectSuccess(bob, collectionId, tokenId, data); + }); + + it('verify data was set', async () => { + await usingApi(async api => { + const item: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap(); + + expect(Array.from(item.VariableData)).to.deep.equal(Array.from(data)); + }); + }); +}); + describe('Negative Integration Test setVariableMetaData', () => { const data = [1]; --- a/tests/src/setVariableOnChainSchema.test.ts +++ b/tests/src/setVariableOnChainSchema.test.ts @@ -11,6 +11,7 @@ import { createCollectionExpectSuccess, destroyCollectionExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; chai.use(chaiAsPromised); @@ -55,6 +56,32 @@ }); }); +describe('Integration Test ext. collection admin setVariableOnChainSchema()', () => { + + it('Run extrinsic with parameters of the collection id, set the scheme', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON(); + expect(collection.Owner).to.be.eq(Alice.address); + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema); + await submitTransactionAsync(Bob, setSchema); + }); + }); + + it('Checking collection data using the setVariableOnChainSchema parameter', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess(); + await addCollectionAdminExpectSuccess(Alice, collectionId, Bob); + const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, Schema); + await submitTransactionAsync(Bob, setSchema); + const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON(); + expect(collection.VariableOnChainSchema.toString()).to.be.eq(Schema); + + }); + }); +}); + describe('Negative Integration Test ext. setVariableOnChainSchema()', () => { it('Set a non-existent collection', async () => { --- a/tests/src/transfer.test.ts +++ b/tests/src/transfer.test.ts @@ -18,6 +18,7 @@ getCreateItemResult, transferExpectFailure, transferExpectSuccess, + addCollectionAdminExpectSuccess, } from './util/helpers'; let Alice: IKeyringPair; @@ -89,6 +90,35 @@ ); }); }); + + it('Collection admin can transfer owned token', async () => { + await usingApi(async () => { + const Alice = privateKey('//Alice'); + const Bob = privateKey('//Bob'); + // nft + const nftCollectionId = await createCollectionExpectSuccess(); + await addCollectionAdminExpectSuccess(Alice, nftCollectionId, Bob); + const newNftTokenId = await createItemExpectSuccess(Bob, nftCollectionId, 'NFT', Bob.address); + await transferExpectSuccess(nftCollectionId, newNftTokenId, Bob, Alice, 1, 'NFT'); + // fungible + const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + await addCollectionAdminExpectSuccess(Alice, fungibleCollectionId, Bob); + const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible', Bob.address); + await transferExpectSuccess(fungibleCollectionId, newFungibleTokenId, Bob, Alice, 1, 'Fungible'); + // reFungible + const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); + await addCollectionAdminExpectSuccess(Alice, reFungibleCollectionId, Bob); + const newReFungibleTokenId = await createItemExpectSuccess(Bob, reFungibleCollectionId, 'ReFungible', Bob.address); + await transferExpectSuccess( + reFungibleCollectionId, + newReFungibleTokenId, + Bob, + Alice, + 100, + 'ReFungible', + ); + }); + }); }); describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => { --- a/tests/src/util/helpers.ts +++ b/tests/src/util/helpers.ts @@ -95,6 +95,23 @@ checkMsgSysMethod: string; } +export interface IFungibleTokenDataType { + Value: number; +} + +export interface IChainLimits { + CollectionNumbersLimit: number; + AccountTokenOwnershipLimit: number; + CollectionsAdminsLimit: number; + CustomDataLimit: number; + NftSponsorTransferTimeout: number; + FungibleSponsorTransferTimeout: number; + RefungibleSponsorTransferTimeout: number; + OffchainSchemaLimit: number; + VariableOnChainSchemaLimit: number; + ConstOnChainSchemaLimit: number; +} + export interface IReFungibleTokenDataType { Owner: IReFungibleOwner[]; ConstData: number[]; @@ -452,11 +469,11 @@ }); } -export async function removeCollectionSponsorExpectFailure(collectionId: number) { +export async function removeCollectionSponsorExpectFailure(collectionId: number, senderSeed = '//Alice') { await usingApi(async (api) => { // Run the transaction - const alicePrivateKey = privateKey('//Alice'); + const alicePrivateKey = privateKey(senderSeed); const tx = api.tx.nft.removeCollectionSponsor(collectionId); await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected; }); @@ -765,6 +782,15 @@ }); } +export async function addCollectionAdminExpectSuccess(sender: IKeyringPair, collectionId: number, address: IKeyringPair) { + await usingApi(async (api) => { + const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(address.address)); + const events = await submitTransactionAsync(sender, changeAdminTx); + const result = getCreateCollectionResult(events); + expect(result.success).to.be.true; + }); +} + export async function scheduleTransferExpectSuccess( collectionId: number, @@ -1021,6 +1047,17 @@ }); } +export async function setChainLimitsExpectFailure(sender: IKeyringPair, limits: IChainLimits) { + await usingApi(async (api) => { + // Run the transaction + const tx = api.tx.nft.setChainLimits(limits); + const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected; + const result = getCreateCollectionResult(events); + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.false; + }); +} + export async function isWhitelisted(collectionId: number, address: string) { let whitelisted = false; await usingApi(async (api) => {