From af9de18a00d2e8a60b4ec59d2a50e7f7c6a2e8c4 Mon Sep 17 00:00:00 2001 From: kpozdnikin Date: Fri, 15 Jan 2021 13:56:35 +0000 Subject: [PATCH] Merge remote-tracking branch 'origin/develop' into feature/NFTPAR-235-258 # Conflicts: # tests/src/createCollection.test.ts # tests/src/util/helpers.ts --- --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -1153,7 +1153,7 @@ } // Reduce approval by transferred amount or remove if remaining approval drops to 0 - if approval - value > 0 { + if approval.checked_sub(value).unwrap_or(0) > 0 { >::insert(collection_id, (item_id, &from, &recipient), approval - value); } else { --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -1,7 +1,7 @@ // Tests to be written here use super::*; use crate::mock::*; -use crate::{AccessMode, ApprovePermissions, CollectionMode, +use crate::{AccessMode, CollectionMode, Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData, CollectionId, TokenId, MAX_DECIMAL_POINTS}; use frame_support::{assert_noop, assert_ok}; @@ -28,7 +28,7 @@ } fn default_fungible_data () -> CreateFungibleData { - CreateFungibleData { } + CreateFungibleData { value: 5 } } fn default_re_fungible_data () -> CreateReFungibleData { @@ -238,35 +238,35 @@ let data = default_fungible_data(); create_test_item(collection_id, &data.into()); - assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).owner, 1); + assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).value, 5); }); } -#[test] -fn create_multiple_fungible_items() { - new_test_ext().execute_with(|| { - default_limits(); +//#[test] +// fn create_multiple_fungible_items() { +// new_test_ext().execute_with(|| { +// default_limits(); - create_test_collection(&CollectionMode::Fungible(3), 1); +// create_test_collection(&CollectionMode::Fungible(3), 1); - let origin1 = Origin::signed(1); +// let origin1 = Origin::signed(1); - let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()]; +// let items_data = vec![default_fungible_data(), default_fungible_data(), default_fungible_data()]; - assert_ok!(TemplateModule::create_multiple_items( - origin1.clone(), - 1, - 1, - items_data.clone().into_iter().map(|d| { d.into() }).collect() - )); +// assert_ok!(TemplateModule::create_multiple_items( +// origin1.clone(), +// 1, +// 1, +// items_data.clone().into_iter().map(|d| { d.into() }).collect() +// )); - for (index, _) in items_data.iter().enumerate() { - assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).owner, 1); - } - assert_eq!(TemplateModule::balance_count(1, 1), 3000); - assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]); - }); -} +// for (index, _) in items_data.iter().enumerate() { +// assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).value, 5); +// } +// assert_eq!(TemplateModule::balance_count(1, 1), 3000); +// assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]); +// }); +// } #[test] fn transfer_fungible_item() { @@ -281,36 +281,26 @@ let data = default_fungible_data(); create_test_item(collection_id, &data.into()); - assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 1); - assert_eq!(TemplateModule::balance_count(1, 1), 1000); - assert_eq!(TemplateModule::address_tokens(1, 1), [1]); + assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 5); + assert_eq!(TemplateModule::balance_count(1, 1), 5); // change owner scenario - assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1000)); - assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 2); - assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 1000); + assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 5)); + assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 0); assert_eq!(TemplateModule::balance_count(1, 1), 0); - assert_eq!(TemplateModule::balance_count(1, 2), 1000); - // assert_eq!(TemplateModule::address_tokens(1, 1), []); - assert_eq!(TemplateModule::address_tokens(1, 2), [1]); + assert_eq!(TemplateModule::balance_count(1, 2), 5); // split item scenario - assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 500)); - assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 2); - assert_eq!(TemplateModule::fungible_item_id(1, 2).owner, 3); - assert_eq!(TemplateModule::balance_count(1, 2), 500); - assert_eq!(TemplateModule::balance_count(1, 3), 500); - assert_eq!(TemplateModule::address_tokens(1, 2), [1]); - assert_eq!(TemplateModule::address_tokens(1, 3), [2]); + assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 3)); + assert_eq!(TemplateModule::balance_count(1, 2), 2); + assert_eq!(TemplateModule::balance_count(1, 3), 3); // split item and new owner has account scenario - assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 200)); - assert_eq!(TemplateModule::fungible_item_id(1, 1).value, 300); - assert_eq!(TemplateModule::fungible_item_id(1, 2).value, 700); - assert_eq!(TemplateModule::balance_count(1, 2), 300); - assert_eq!(TemplateModule::balance_count(1, 3), 700); - assert_eq!(TemplateModule::address_tokens(1, 2), [1]); - assert_eq!(TemplateModule::address_tokens(1, 3), [2]); + assert_ok!(TemplateModule::transfer(origin2.clone(), 3, 1, 1, 1)); + assert_eq!(TemplateModule::fungible_item_id(1, 2).value, 1); + assert_eq!(TemplateModule::fungible_item_id(1, 3).value, 4); + assert_eq!(TemplateModule::balance_count(1, 2), 1); + assert_eq!(TemplateModule::balance_count(1, 3), 4); }); } @@ -451,14 +441,11 @@ 1), Error::::NoPermission); // do approve - assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); + assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5)); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5); assert_eq!( - TemplateModule::approved(1, (1, 1))[0], - ApprovePermissions { - approved: 2, - amount: 100000000 - } + TemplateModule::approved(1, (1, 1, 2)), + 5 ); assert_ok!(TemplateModule::transfer_from( @@ -469,7 +456,7 @@ 1, 1 )); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 4); }); } @@ -505,17 +492,10 @@ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3)); // do approve - assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); - assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2); - assert_eq!( - TemplateModule::approved(1, (1, 1))[0], - ApprovePermissions { - approved: 2, - amount: 100000000 - } - ); + assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5)); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5); + assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5)); + assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5); assert_ok!(TemplateModule::transfer_from( origin2.clone(), @@ -525,7 +505,7 @@ 1, 1 )); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0); + assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 4); }); } @@ -560,17 +540,10 @@ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3)); // do approve - assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); - assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2); - assert_eq!( - TemplateModule::approved(1, (1, 1))[0], - ApprovePermissions { - approved: 2, - amount: 100000000 - } - ); + assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5)); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5); + assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 1000)); + assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1000); assert_ok!(TemplateModule::transfer_from( origin2.clone(), @@ -585,13 +558,9 @@ assert_eq!(TemplateModule::address_tokens(1, 1), [1]); assert_eq!(TemplateModule::address_tokens(1, 3), [1]); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); assert_eq!( - TemplateModule::approved(1, (1, 1))[0], - ApprovePermissions { - approved: 3, - amount: 100000000 - } + TemplateModule::approved(1, (1, 1, 3)), + 900 ); }); } @@ -609,8 +578,7 @@ let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); - assert_eq!(TemplateModule::balance_count(1, 1), 1000); - assert_eq!(TemplateModule::address_tokens(1, 1), [1]); + assert_eq!(TemplateModule::balance_count(1, 1), 5); assert_ok!(TemplateModule::set_mint_permission( origin1.clone(), @@ -627,16 +595,13 @@ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3)); // do approve - assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); - assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 2); + assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5)); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5); + assert_ok!(TemplateModule::approve(origin1.clone(), 3, 1, 1, 5)); + assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 5); assert_eq!( - TemplateModule::approved(1, (1, 1))[0], - ApprovePermissions { - approved: 2, - amount: 100000000 - } + TemplateModule::approved(1, (1, 1, 2)), + 5 ); assert_ok!(TemplateModule::transfer_from( @@ -645,37 +610,23 @@ 3, 1, 1, - 100 + 4 )); - assert_eq!(TemplateModule::balance_count(1, 1), 900); - assert_eq!(TemplateModule::balance_count(1, 3), 100); - assert_eq!(TemplateModule::address_tokens(1, 1), [1]); - assert_eq!(TemplateModule::address_tokens(1, 3), [2]); + assert_eq!(TemplateModule::balance_count(1, 1), 1); + assert_eq!(TemplateModule::balance_count(1, 3), 4); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); - assert_eq!( - TemplateModule::approved(1, (1, 1))[0], - ApprovePermissions { - approved: 3, - amount: 100000000 - } - ); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 5); + assert_eq!(TemplateModule::approved(1, (1, 1, 3)), 1); - assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1)); - assert_ok!(TemplateModule::transfer_from( + assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 5)); + assert_noop!(TemplateModule::transfer_from( origin2.clone(), 1, 3, 1, 1, - 900 - )); - assert_eq!(TemplateModule::balance_count(1, 1), 0); - assert_eq!(TemplateModule::balance_count(1, 3), 1000); - // assert_eq!(TemplateModule::address_tokens(1, 1), []); - assert_eq!(TemplateModule::address_tokens(1, 3), [2]); - - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0); + 4 + ), Error::::TokenValueNotEnough); }); } @@ -725,9 +676,9 @@ assert_eq!(TemplateModule::balance_count(1, 1), 1); // burn item - assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1)); + assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5)); assert_noop!( - TemplateModule::burn_item(origin1.clone(), 1, 1), + TemplateModule::burn_item(origin1.clone(), 1, 1, 5), Error::::TokenNotFound ); @@ -749,12 +700,12 @@ create_test_item(collection_id, &data.into()); // check balance (collection with id = 1, user id = 1) - assert_eq!(TemplateModule::balance_count(1, 1), 1000); + assert_eq!(TemplateModule::balance_count(1, 1), 5); // burn item - assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1)); + assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 5)); assert_noop!( - TemplateModule::burn_item(origin1.clone(), 1, 1), + TemplateModule::burn_item(origin1.clone(), 1, 1, 5), Error::::TokenNotFound ); @@ -791,9 +742,9 @@ assert_eq!(TemplateModule::balance_count(1, 1), 1000); // burn item - assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1)); + assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 1, 1000)); assert_noop!( - TemplateModule::burn_item(origin1.clone(), 1, 1), + TemplateModule::burn_item(origin1.clone(), 1, 1, 1000), Error::::TokenNotFound ); @@ -875,10 +826,10 @@ // check balance (collection with id = 1, user id = 1) assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 1); - assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 1000); + assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 5); assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1000); assert_eq!(TemplateModule::nft_item_id(nft_collection_id, 1).owner, 1); - assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).owner, 1); + assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).value, 5); assert_eq!(TemplateModule::refungible_item_id(re_fungible_collection_id, 1).owner[0].owner, 1); }); } @@ -896,8 +847,8 @@ let origin1 = Origin::signed(1); // approve - assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2); + assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1)); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1); }); } @@ -914,8 +865,8 @@ create_test_item(collection_id, &data.into()); // approve - assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2); + assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1, 1)); + assert_eq!(TemplateModule::approved(1, (1, 1, 2)), 1); assert_ok!(TemplateModule::set_mint_permission( origin1.clone(), @@ -1199,8 +1150,8 @@ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2)); // do approve - assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); + assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1)); + assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1); assert_ok!(TemplateModule::remove_from_white_list( origin1.clone(), @@ -1263,8 +1214,8 @@ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2)); // do approve - assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); + assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 1)); + assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 1); assert_ok!(TemplateModule::remove_from_white_list( origin1.clone(), @@ -1298,7 +1249,7 @@ AccessMode::WhiteList )); assert_noop!( - TemplateModule::burn_item(origin1.clone(), 1, 1), + TemplateModule::burn_item(origin1.clone(), 1, 1, 5), Error::::AddresNotInWhiteList ); }); @@ -1321,7 +1272,7 @@ // do approve assert_noop!( - TemplateModule::approve(origin1.clone(), 1, 1, 1), + TemplateModule::approve(origin1.clone(), 1, 1, 1, 5), Error::::AddresNotInWhiteList ); }); @@ -1374,8 +1325,8 @@ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2)); // do approve - assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1)); - assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); + assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1, 5)); + assert_eq!(TemplateModule::approved(1, (1, 1, 1)), 5); assert_ok!(TemplateModule::transfer_from( origin1.clone(), --- a/tests/package.json +++ b/tests/package.json @@ -20,6 +20,7 @@ "load": "mocha --timeout 9999999 -r ts-node/register ./**/*.load.ts", "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts", "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts", + "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", "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts", --- /dev/null +++ b/tests/src/burnItem.test.ts @@ -0,0 +1,237 @@ +import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from './substrate/substrate-api'; +import { Keyring } from "@polkadot/api"; +import { IKeyringPair } from "@polkadot/types/types"; +import { + createCollectionExpectSuccess, + createItemExpectSuccess, + getGenericResult, + destroyCollectionExpectSuccess +} from './util/helpers'; +import { nullPublicKey } from "./accounts"; + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +chai.use(chaiAsPromised); +const expect = chai.expect; + +let alice: IKeyringPair; +let bob: IKeyringPair; + +describe('integration test: ext. burnItem():', () => { + before(async () => { + await usingApi(async (api) => { + 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 usingApi(async (api) => { + const tx = api.tx.nft.burnItem(collectionId, tokenId, 0); + const events = await submitTransactionAsync(alice, tx); + const result = getGenericResult(events); + + // Get the item + const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON(); + + // What to expect + expect(result.success).to.be.true; + expect(item).to.be.not.null; + expect(item.Owner).to.be.equal(nullPublicKey); + }); + + }); + it('Burn item in Fungible collection', async () => { + const createMode = 'Fungible'; + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0 }}); + await createItemExpectSuccess(alice, collectionId, createMode); // Helper creates 10 fungible tokens + const tokenId = 0; // ignored + + await usingApi(async (api) => { + // Destroy 1 of 10 + const tx = api.tx.nft.burnItem(collectionId, tokenId, 1); + const events = await submitTransactionAsync(alice, tx); + const result = getGenericResult(events); + + // Get alice balance + const balance: any = (await api.query.nft.fungibleItemList(collectionId, alice.address)).toJSON(); + + // What to expect + expect(result.success).to.be.true; + 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, decimalPoints: 2 }}); + const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); + + await usingApi(async (api) => { + const tx = api.tx.nft.burnItem(collectionId, tokenId, 1); + const events = await submitTransactionAsync(alice, tx); + const result = getGenericResult(events); + + // Get alice balance + const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON(); + + // What to expect + expect(result.success).to.be.true; + expect(balance).to.be.not.null; + expect(balance.Owner.length).to.be.equal(0); + }); + + }); + + it('Burn owned portion of item in ReFungible collection', async () => { + const createMode = 'ReFungible'; + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 2 }}); + const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); + + await usingApi(async (api) => { + // Transfer 1/100 of the token to Bob + const transfertx = api.tx.nft.transfer(bob.address, collectionId, tokenId, 1); + const events1 = await submitTransactionAsync(alice, transfertx); + const result1 = getGenericResult(events1); + + // Get balances + const balanceBefore: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON(); + + // Bob burns his portion + const tx = api.tx.nft.burnItem(collectionId, tokenId, 0); + const events2 = await submitTransactionAsync(bob, tx); + const result2 = getGenericResult(events2); + + // Get balances + const balance: any = (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON(); + // console.log(balance); + + // What to expect before burning + expect(result1.success).to.be.true; + expect(balanceBefore).to.be.not.null; + expect(balanceBefore.Owner.length).to.be.equal(2); + expect(balanceBefore.Owner[0].Owner).to.be.equal(alice.address); + expect(balanceBefore.Owner[0].Fraction).to.be.equal(99); + expect(balanceBefore.Owner[1].Owner).to.be.equal(bob.address); + expect(balanceBefore.Owner[1].Fraction).to.be.equal(1); + + // What to expect after burning + expect(result2.success).to.be.true; + expect(balance).to.be.not.null; + expect(balance.Owner.length).to.be.equal(1); + expect(balance.Owner[0].Fraction).to.be.equal(99); + expect(balance.Owner[0].Owner).to.be.equal(alice.address); + }); + + }); + +}); + +describe('Negative integration test: ext. burnItem():', () => { + before(async () => { + await usingApi(async (api) => { + const keyring = new Keyring({ type: 'sr25519' }); + alice = keyring.addFromUri(`//Alice`); + bob = keyring.addFromUri(`//Bob`); + }); + }); + + it('Burn a token in a destroyed collection', async () => { + const createMode = 'NFT'; + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }}); + const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); + await destroyCollectionExpectSuccess(collectionId); + + await usingApi(async (api) => { + const tx = api.tx.nft.burnItem(collectionId, tokenId, 0); + const badTransaction = async function () { + await submitTransactionExpectFailAsync(alice, tx); + }; + await expect(badTransaction()).to.be.rejected; + }); + + }); + + it('Burn a token that was never created', async () => { + const createMode = 'NFT'; + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }}); + const tokenId = 10; + + await usingApi(async (api) => { + const tx = api.tx.nft.burnItem(collectionId, tokenId, 0); + const badTransaction = async function () { + await submitTransactionExpectFailAsync(alice, tx); + }; + await expect(badTransaction()).to.be.rejected; + }); + + }); + + it('Burn a token using the address that does not own it', async () => { + const createMode = 'NFT'; + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }}); + const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); + + await usingApi(async (api) => { + const tx = api.tx.nft.burnItem(collectionId, tokenId, 0); + const badTransaction = async function () { + await submitTransactionExpectFailAsync(bob, tx); + }; + await expect(badTransaction()).to.be.rejected; + }); + + }); + + it('Transfer a burned a token', async () => { + const createMode = 'NFT'; + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode }}); + const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); + + await usingApi(async (api) => { + + const burntx = api.tx.nft.burnItem(collectionId, tokenId, 0); + const events1 = await submitTransactionAsync(alice, burntx); + const result1 = getGenericResult(events1); + expect(result1.success).to.be.true; + + const tx = api.tx.nft.transfer(bob.address, collectionId, tokenId, 0); + const badTransaction = async function () { + await submitTransactionExpectFailAsync(alice, tx); + }; + await expect(badTransaction()).to.be.rejected; + }); + + }); + + it('Burn more than owned in Fungible collection', async () => { + const createMode = 'Fungible'; + const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0 }}); + // Helper creates 10 fungible tokens + await createItemExpectSuccess(alice, collectionId, createMode); + const tokenId = 0; // ignored + + await usingApi(async (api) => { + // Destroy 11 of 10 + const tx = api.tx.nft.burnItem(collectionId, tokenId, 11); + const badTransaction = async function () { + await submitTransactionExpectFailAsync(alice, tx); + }; + await expect(badTransaction()).to.be.rejected; + + // Get alice balance + const balance: any = (await api.query.nft.fungibleItemList(collectionId, alice.address)).toJSON(); + + // What to expect + expect(balance).to.be.not.null; + expect(balance.Value).to.be.equal(10); + }); + + }); + +}); --- a/tests/src/confirmSponsorship.test.ts +++ b/tests/src/confirmSponsorship.test.ts @@ -89,7 +89,7 @@ }); it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => { - const collectionId = await createCollectionExpectSuccess({mode: 'Fungible'}); + const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0 }}); await setCollectionSponsorExpectSuccess(collectionId, bob.address); await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); @@ -115,7 +115,7 @@ }); it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => { - const collectionId = await createCollectionExpectSuccess({mode: 'ReFungible'}); + const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }}); await setCollectionSponsorExpectSuccess(collectionId, bob.address); await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); @@ -210,7 +210,7 @@ }); it('Fungible: Sponsoring is rate limited', async () => { - const collectionId = await createCollectionExpectSuccess({mode: 'Fungible'}); + const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0 }}); await setCollectionSponsorExpectSuccess(collectionId, bob.address); await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); @@ -247,7 +247,7 @@ }); it('ReFungible: Sponsoring is rate limited', async () => { - const collectionId = await createCollectionExpectSuccess({mode: 'ReFungible'}); + const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0 }}); await setCollectionSponsorExpectSuccess(collectionId, bob.address); await confirmSponsorshipExpectSuccess(collectionId, '//Bob'); --- a/tests/src/createCollection.test.ts +++ b/tests/src/createCollection.test.ts @@ -37,7 +37,9 @@ await usingApi(async (api) => { const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString(), 10); - const badTransaction = async () => await createCollectionExpectSuccess({mode: {type: 'Invalid'}}); + const badTransaction = async () => { + await createCollectionExpectSuccess({mode: {type: 'Invalid'}}); + }; // tslint:disable-next-line:no-unused-expression expect(badTransaction()).to.be.rejected; --- a/tests/src/createItem.test.ts +++ b/tests/src/createItem.test.ts @@ -18,17 +18,17 @@ it('Create new item in NFT collection', async () => { const createMode = 'NFT'; - const newCollectionID = await createCollectionExpectSuccess({mode: createMode}); + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}}); await createItemExpectSuccess(alice, newCollectionID, createMode); }); it('Create new item in Fungible collection', async () => { const createMode = 'Fungible'; - const newCollectionID = await createCollectionExpectSuccess({mode: createMode}); + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}}); await createItemExpectSuccess(alice, newCollectionID, createMode); }); it('Create new item in ReFungible collection', async () => { const createMode = 'ReFungible'; - const newCollectionID = await createCollectionExpectSuccess({mode: createMode}); + const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}}); await createItemExpectSuccess(alice, newCollectionID, createMode); }); }); --- a/tests/src/destroyCollection.test.ts +++ b/tests/src/destroyCollection.test.ts @@ -14,11 +14,11 @@ await destroyCollectionExpectSuccess(collectionId); }); it('Fungible collection can be destroyed', async () => { - const collectionId = await createCollectionExpectSuccess({ mode: 'Fungible' }); + const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); await destroyCollectionExpectSuccess(collectionId); }); it('ReFungible collection can be destroyed', async () => { - const collectionId = await createCollectionExpectSuccess({ mode: 'ReFungible' }); + const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible', decimalPoints: 0}}); await destroyCollectionExpectSuccess(collectionId); }); }); --- /dev/null +++ b/tests/src/removeCollectionAdmin.test.ts @@ -0,0 +1,82 @@ +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} from './util/helpers'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => { + it('Remove collection admin.', async () => { + await usingApi(async (api: ApiPromise) => { + const collectionId = await createCollectionExpectSuccess(); + const Alice = privateKey('//Alice'); + const Bob = privateKey('//Bob'); + const collection: any = (await api.query.nft.collection(collectionId)); + expect(collection.Owner.toString()).to.be.eq(Alice.address); + // first - add collection admin Bob + const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, Bob.address); + await submitTransactionAsync(Alice, addAdminTx); + + const adminListAfterAddAdmin: any = (await api.query.nft.adminList(collectionId)); + expect(adminListAfterAddAdmin).to.be.contains(Bob.address); + + // then remove bob from admins of collection + const removeAdminTx = api.tx.nft.removeCollectionAdmin(collectionId, Bob.address); + await submitTransactionAsync(Alice, removeAdminTx); + + const adminListAfterRemoveAdmin: any = (await api.query.nft.adminList(collectionId)); + expect(adminListAfterRemoveAdmin).not.to.be.contains(Bob.address); + }); + }); +}); + +describe('Negative Integration Test removeCollectionAdmin(collection_id, account_id):', () => { + it('Can\'t remove collection admin from not existing collection', async () => { + await usingApi(async (api: ApiPromise) => { + // tslint:disable-next-line: no-bitwise + const collectionId = (1 << 32) - 1; + const alice = privateKey('//Alice'); + const bob = privateKey('//Bob'); + + const changeOwnerTx = api.tx.nft.removeCollectionAdmin(collectionId, bob.address); + await expect(submitTransactionExpectFailAsync(alice, changeOwnerTx)).to.be.rejected; + + // Verifying that nothing bad happened (network is live, new collections can be created, etc.) + await createCollectionExpectSuccess(); + }); + }); + + it('Can\'t remove collection admin from deleted collection', async () => { + await usingApi(async (api: ApiPromise) => { + // tslint:disable-next-line: no-bitwise + const collectionId = await createCollectionExpectSuccess(); + const Alice = privateKey('//Alice'); + const Bob = privateKey('//Bob'); + + await destroyCollectionExpectSuccess(collectionId); + + const changeOwnerTx = api.tx.nft.removeCollectionAdmin(collectionId, Bob.address); + await expect(submitTransactionExpectFailAsync(Alice, changeOwnerTx)).to.be.rejected; + + // Verifying that nothing bad happened (network is live, new collections can be created, etc.) + await createCollectionExpectSuccess(); + }); + }); + + it('Remove admin from collection that has no admins', async () => { + await usingApi(async (api: ApiPromise) => { + const Alice = privateKey('//Alice'); + const collectionId = await createCollectionExpectSuccess(); + + const adminListBeforeAddAdmin: any = (await api.query.nft.adminList(collectionId)); + expect(adminListBeforeAddAdmin).to.have.lengthOf(0); + + const tx = api.tx.nft.removeCollectionAdmin(collectionId, Alice.address); + await expect(submitTransactionExpectFailAsync(Alice, tx)).to.be.rejected; + }); + }); +}); --- a/tests/src/setCollectionLimits.test.ts +++ b/tests/src/setCollectionLimits.test.ts @@ -32,7 +32,7 @@ }); it('choose or create collection for testing', async () => { await usingApi(async () => { - collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'}); + collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}}); }); }); }); --- a/tests/src/setCollectionSponsor.test.ts +++ b/tests/src/setCollectionSponsor.test.ts @@ -30,11 +30,11 @@ await setCollectionSponsorExpectSuccess(collectionId, bob.address); }); it('Set Fungible collection sponsor', async () => { - const collectionId = await createCollectionExpectSuccess({ mode: 'Fungible' }); + const collectionId = await createCollectionExpectSuccess({ mode: {type: 'Fungible', decimalPoints: 0} }); await setCollectionSponsorExpectSuccess(collectionId, bob.address); }); it('Set ReFungible collection sponsor', async () => { - const collectionId = await createCollectionExpectSuccess({ mode: 'ReFungible' }); + const collectionId = await createCollectionExpectSuccess({ mode: {type: 'ReFungible', decimalPoints: 0} }); await setCollectionSponsorExpectSuccess(collectionId, bob.address); }); --- a/tests/src/setSchemaVersion.test.ts +++ b/tests/src/setSchemaVersion.test.ts @@ -35,7 +35,7 @@ }); it('choose or create collection for testing', async () => { await usingApi(async () => { - collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'}); + collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}}); }); }); }); @@ -93,12 +93,6 @@ const nonExistedCollectionId = collectionCount + 1; tx = api.tx.nft.setSchemaVersion(nonExistedCollectionId, 'ImageURL'); await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected; - /*try { - await submitTransactionAsync(alice, tx); - } catch (e) { - // tslint:disable-next-line:no-unused-expression - expect(e).to.be.exist; - }*/ }); }); @@ -119,13 +113,6 @@ await destroyCollectionExpectSuccess(collectionIdForTesting); tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'ImageURL'); await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected; - /*try { - tx = api.tx.nft.setSchemaVersion(collectionIdForTesting, 'ImageURL'); - await submitTransactionAsync(alice, tx); - } catch (e) { - // tslint:disable-next-line:no-unused-expression - expect(e).to.be.exist; - }*/ }); }); }); --- a/tests/src/util/helpers.ts +++ b/tests/src/util/helpers.ts @@ -112,6 +112,20 @@ decimalPoints: number; } +interface Nft { + type: 'NFT' +} + +interface Fungible { + type: 'Fungible', + decimalPoints: number +} + +interface ReFungible { + type: 'ReFungible', + decimalPoints: number +} + type CollectionMode = Nft | Fungible | ReFungible | Invalid; export type CreateCollectionParams = { -- gitstuff