git.delta.rocks / unique-network / refs/commits / 4a84b64c6fc6

difftreelog

source

js-packages/tests/nesting/collectionProperties.test.ts13.4 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import type {IKeyringPair} from '@polkadot/types/types';18import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js';1920describe('Integration Test: Collection Properties', () => {21  let alice: IKeyringPair;22  let bob: IKeyringPair;2324  before(async () => {25    await usingPlaygrounds(async (helper, privateKey) => {26      const donor = await privateKey({url: import.meta.url});27      [alice, bob] = await helper.arrange.createAccounts([200n, 10n], donor);28    });29  });3031  itSub('Properties are initially empty', async ({helper}) => {32    const collection = await helper.nft.mintCollection(alice);33    expect(await collection.getProperties()).to.be.empty;34  });3536  [37    {mode: 'nft' as const, requiredPallets: []},38    {mode: 'ft' as const, requiredPallets: []},39    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},40  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {41    before(async function() {42      // eslint-disable-next-line require-await43      await usingPlaygrounds(async helper => {44        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);45      });46    });4748    itSub('Sets properties for a collection', async ({helper}) =>  {49      const collection = await helper[testSuite.mode].mintCollection(alice);5051      // As owner52      await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}])).to.be.fulfilled;5354      await collection.addAdmin(alice, {Substrate: bob.address});5556      // As administrator57      await expect(collection.setProperties(bob, [{key: 'black_hole'}])).to.be.fulfilled;5859      const properties = await collection.getProperties();60      expect(properties).to.include.deep.members([61        {key: 'electron', value: 'come bond'},62        {key: 'black_hole', value: ''},63      ]);64    });6566    itSub('Check valid names for collection properties keys', async ({helper}) =>  {67      const collection = await helper[testSuite.mode].mintCollection(alice);6869      // alpha symbols70      await expect(collection.setProperties(alice, [{key: 'answer'}])).to.be.fulfilled;7172      // numeric symbols73      await expect(collection.setProperties(alice, [{key: '451'}])).to.be.fulfilled;7475      // underscore symbol76      await expect(collection.setProperties(alice, [{key: 'black_hole'}])).to.be.fulfilled;7778      // dash symbol79      await expect(collection.setProperties(alice, [{key: '-'}])).to.be.fulfilled;8081      // dot symbol82      await expect(collection.setProperties(alice, [{key: 'once.in.a.long.long.while...', value: 'you get a little lost'}])).to.be.fulfilled;8384      const properties = await collection.getProperties();85      expect(properties).to.include.deep.members([86        {key: 'answer', value: ''},87        {key: '451', value: ''},88        {key: 'black_hole', value: ''},89        {key: '-', value: ''},90        {key: 'once.in.a.long.long.while...', value: 'you get a little lost'},91      ]);92    });9394    itSub('Changes properties of a collection', async ({helper}) =>  {95      const collection = await helper[testSuite.mode].mintCollection(alice);9697      await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: ''}])).to.be.fulfilled;9899      // Mutate the properties100      await expect(collection.setProperties(alice, [{key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;101102      const properties = await collection.getProperties();103      expect(properties).to.include.deep.members([104        {key: 'electron', value: 'come bond'},105        {key: 'black_hole', value: 'LIGO'},106      ]);107    });108109    itSub('Deletes properties of a collection', async ({helper}) =>  {110      const collection = await helper[testSuite.mode].mintCollection(alice);111112      await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;113114      await expect(collection.deleteProperties(alice, ['electron'])).to.be.fulfilled;115116      const properties = await collection.getProperties(['black_hole', 'electron']);117      expect(properties).to.be.deep.equal([118        {key: 'black_hole', value: 'LIGO'},119      ]);120    });121122    itSub('Allows modifying a collection property multiple times with the same size', async({helper}) => {123      const propKey = 'tok-prop';124125      const collection = await helper[testSuite.mode].mintCollection(alice);126127      const maxCollectionPropertiesSize = 40960;128129      const propDataSize = 4096;130131      let propDataChar = 'a';132      const makeNewPropData = () => {133        propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1);134        return `${propDataChar}`.repeat(propDataSize);135      };136137      const property = {key: propKey, value: makeNewPropData()};138      await collection.setProperties(alice, [property]);139      const originalSpace = await collection.getPropertiesConsumedSpace();140      expect(originalSpace).to.be.equal(sizeOfProperty(property));141142      const sameSizePropertiesPossibleNum = maxCollectionPropertiesSize / propDataSize;143144      // It is possible to modify a property as many times as needed.145      // It will not consume any additional space.146      for(let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) {147        await collection.setProperties(alice, [{key: propKey, value: makeNewPropData()}]);148        const consumedSpace = await collection.getPropertiesConsumedSpace();149        expect(consumedSpace).to.be.equal(originalSpace);150      }151    });152153    itSub('Adding then removing a collection property doesn\'t change the consumed space', async({helper}) => {154      const propKey = 'tok-prop';155156      const collection = await helper[testSuite.mode].mintCollection(alice);157      const originalSpace = await collection.getPropertiesConsumedSpace();158159      const propDataSize = 4096;160      const propData = 'a'.repeat(propDataSize);161162      const property = {key: propKey, value: propData};163      await collection.setProperties(alice, [property]);164      let consumedSpace = await collection.getPropertiesConsumedSpace();165      expect(consumedSpace).to.be.equal(sizeOfProperty(property));166167      await collection.deleteProperties(alice, [propKey]);168      consumedSpace = await collection.getPropertiesConsumedSpace();169      expect(consumedSpace).to.be.equal(originalSpace);170    });171172    itSub('Modifying a collection property with different sizes correctly changes the consumed space', async({helper}) => {173      const propKey = 'tok-prop';174175      const collection = await helper[testSuite.mode].mintCollection(alice);176      const originalSpace = await collection.getPropertiesConsumedSpace();177178      const initProp = {key: propKey, value: 'a'.repeat(4096)};179      const biggerProp = {key: propKey, value: 'b'.repeat(5000)};180      const smallerProp = {key: propKey, value: 'c'.repeat(4000)};181182      let consumedSpace;183      let expectedConsumedSpaceDiff;184185      await collection.setProperties(alice, [initProp]);186      consumedSpace = await collection.getPropertiesConsumedSpace();187      expectedConsumedSpaceDiff = sizeOfProperty(initProp) - originalSpace;188      expect(consumedSpace).to.be.equal(originalSpace + expectedConsumedSpaceDiff);189190      await collection.setProperties(alice, [biggerProp]);191      consumedSpace = await collection.getPropertiesConsumedSpace();192      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(initProp);193      expect(consumedSpace).to.be.equal(sizeOfProperty(initProp) + expectedConsumedSpaceDiff);194195      await collection.setProperties(alice, [smallerProp]);196      consumedSpace = await collection.getPropertiesConsumedSpace();197      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(smallerProp);198      expect(consumedSpace).to.be.equal(sizeOfProperty(biggerProp) - expectedConsumedSpaceDiff);199    });200  }));201});202203describe('Negative Integration Test: Collection Properties', () => {204  let alice: IKeyringPair;205  let bob: IKeyringPair;206207  before(async () => {208    await usingPlaygrounds(async (helper, privateKey) => {209      const donor = await privateKey({url: import.meta.url});210      [alice, bob] = await helper.arrange.createAccounts([1000n, 100n], donor);211    });212  });213214  [215    {mode: 'nft' as const, requiredPallets: []},216    {mode: 'ft' as const, requiredPallets: []},217    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},218  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {219    before(async function() {220      // eslint-disable-next-line require-await221      await usingPlaygrounds(async helper => {222        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);223      });224    });225226    itSub('Fails to set properties in a collection if not its onwer/administrator', async ({helper}) =>  {227      const collection = await helper[testSuite.mode].mintCollection(alice);228229      await expect(collection.setProperties(bob, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]))230        .to.be.rejectedWith(/common\.NoPermission/);231232      expect(await collection.getProperties()).to.be.empty;233    });234235    itSub('Fails to set properties that exceed the limits', async ({helper}) =>  {236      const collection = await helper[testSuite.mode].mintCollection(alice);237238      const spaceLimit = (helper.getApi().consts.unique.maxCollectionPropertiesSize as any).toNumber();239240      // Mute the general tx parsing error, too many bytes to process241      {242        console.error = () => {};243        await expect(collection.setProperties(alice, [244          {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))},245        ])).to.be.rejected;246      }247248      expect(await collection.getProperties(['electron'])).to.be.empty;249250      await expect(collection.setProperties(alice, [251        {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))},252        {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))},253      ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);254255      expect(await collection.getProperties(['electron', 'black_hole'])).to.be.empty;256    });257258    itSub('Fails to set more properties than it is allowed', async ({helper}) =>  {259      const collection = await helper[testSuite.mode].mintCollection(alice);260261      const propertiesToBeSet = [];262      for(let i = 0; i < 65; i++) {263        propertiesToBeSet.push({264          key: 'electron_' + i,265          value: Math.random() > 0.5 ? 'high' : 'low',266        });267      }268269      await expect(collection.setProperties(alice, propertiesToBeSet)).270        to.be.rejectedWith(/common\.PropertyLimitReached/);271272      expect(await collection.getProperties()).to.be.empty;273    });274275    itSub('Fails to set properties with invalid names', async ({helper}) => {276      const collection = await helper[testSuite.mode].mintCollection(alice);277278      const invalidProperties = [279        [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],280        [{key: 'Mr/Sandman', value: 'Bring me a gene'}],281        [{key: 'déjà vu', value: 'hmm...'}],282      ];283284      for(let i = 0; i < invalidProperties.length; i++) {285        await expect(286          collection.setProperties(alice, invalidProperties[i]),287          `on rejecting the new badly-named property #${i}`,288        ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);289      }290291      await expect(292        collection.setProperties(alice, [{key: '', value: 'nothing must not exist'}]),293        'on rejecting an unnamed property',294      ).to.be.rejectedWith(/common\.EmptyPropertyKey/);295296      await expect(297        collection.setProperties(alice, [{key: 'CRISPR-Cas9', value: 'rewriting nature!'}]),298        'on setting the correctly-but-still-badly-named property',299      ).to.be.fulfilled;300301      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');302303      const properties = await collection.getProperties(keys);304      expect(properties).to.be.deep.equal([305        {key: 'CRISPR-Cas9', value: 'rewriting nature!'},306      ]);307308      for(let i = 0; i < invalidProperties.length; i++) {309        await expect(310          collection.deleteProperties(alice, invalidProperties[i].map(propertySet => propertySet.key)),311          `on trying to delete the non-existent badly-named property #${i}`,312        ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);313      }314    });315316    itSub('Forbids to repair a collection if called with non-sudo', async({helper}) => {317      const collection = await helper[testSuite.mode].mintCollection(alice, {properties: [318        {key: 'sea-creatures', value: 'mermaids'},319        {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'},320      ]});321322      await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true))323        .to.be.rejectedWith(/BadOrigin/);324    });325  }));326});