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

difftreelog

source

tests/src/nesting/properties.test.ts29.2 KiBsourcehistory
1import {expect} from 'chai';2import privateKey from '../substrate/privateKey';3import usingApi, {executeTransaction} from '../substrate/substrate-api';4import {5  addCollectionAdminExpectSuccess,6  createCollectionExpectSuccess,7  createItemExpectSuccess,8  getCreateCollectionResult,9  transferExpectSuccess,10} from '../util/helpers';11import {IKeyringPair} from '@polkadot/types/types';1213let alice: IKeyringPair;14let bob: IKeyringPair;15let charlie: IKeyringPair;1617// ---------- COLLECTION PROPERTIES1819describe('Integration Test: Collection Properties', () => {20  before(async () => {21    await usingApi(async () => {22      alice = privateKey('//Alice');23      bob = privateKey('//Bob');24    });25  });2627  it('Reads properties from a collection', async () => {28    await usingApi(async api => {29      const collection = await createCollectionExpectSuccess();30      const properties = (await api.query.common.collectionProperties(collection)).toJSON();31      expect(properties.map).to.be.empty;32      expect(properties.consumedSpace).to.equal(0);33    });34  });3536  it('Sets properties for a collection', async () => {37    await usingApi(async api => {38      const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));39      const {collectionId} = getCreateCollectionResult(events);4041      // As owner42      await expect(executeTransaction(43        api, 44        bob, 45        api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 46      )).to.not.be.rejected;4748      await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);4950      // As administrator51      await expect(executeTransaction(52        api, 53        alice, 54        api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 55      )).to.not.be.rejected;5657      const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();58      expect(properties).to.be.deep.equal([59        {key: 'electron', value: 'come bond'},60        {key: 'black_hole', value: ''},61      ]);62    });63  });6465  it('Changes properties of a collection', async () => {66    await usingApi(async api => {67      const collection = await createCollectionExpectSuccess();6869      await expect(executeTransaction(70        api, 71        alice, 72        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 73      )).to.not.be.rejected;7475      // Mutate the properties76      await expect(executeTransaction(77        api, 78        alice, 79        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 80      )).to.not.be.rejected;8182      const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();83      expect(properties).to.be.deep.equal([84        {key: 'electron', value: 'bonded'},85        {key: 'black_hole', value: 'LIGO'},86      ]);87    });88  });8990  it('Deletes properties of a collection', async () => {91    await usingApi(async api => {92      const collection = await createCollectionExpectSuccess();9394      await expect(executeTransaction(95        api, 96        alice, 97        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 98      )).to.not.be.rejected;99100      await expect(executeTransaction(101        api, 102        alice, 103        api.tx.unique.deleteCollectionProperties(collection, ['electron']), 104      )).to.not.be.rejected;105106      const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();107      expect(properties).to.be.deep.equal([108        {key: 'black_hole', value: 'LIGO'},109      ]);110    });111  });112});113114describe('Negative Integration Test: Collection Properties', () => {115  before(async () => {116    await usingApi(async () => {117      alice = privateKey('//Alice');118      bob = privateKey('//Bob');119    });120  });121  122  it('Fails to set properties in a collection if not its onwer/administrator', async () => {123    await usingApi(async api => {124      const collection = await createCollectionExpectSuccess();125126      await expect(executeTransaction(127        api, 128        bob, 129        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 130      )).to.be.rejectedWith(/common\.NoPermission/);131  132      const properties = (await api.query.common.collectionProperties(collection)).toJSON();133      expect(properties.map).to.be.empty;134      expect(properties.consumedSpace).to.equal(0);135    });136  });137  138  it('Fails to set properties that exceed the limits', async () => {139    await usingApi(async api => {140      const collection = await createCollectionExpectSuccess();141      const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 142143      // Mute the general tx parsing error, too many bytes to process144      {145        console.error = () => {};146        await expect(executeTransaction(147          api, 148          alice, 149          api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 150        )).to.be.rejected;151      }152153      let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();154      expect(properties).to.be.empty;155156      await expect(executeTransaction(157        api, 158        alice, 159        api.tx.unique.setCollectionProperties(collection, [160          {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 161          {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 162        ]), 163      )).to.be.rejectedWith(/common\.NoSpaceForProperty/);164165      properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();166      expect(properties).to.be.empty;167    });168  });169  170  it('Fails to set more properties than it is allowed', async () => {171    await usingApi(async api => {172      const collection = await createCollectionExpectSuccess();173174      const propertiesToBeSet = [];175      for (let i = 0; i < 65; i++) {176        propertiesToBeSet.push({177          key: 'electron_' + i,178          value: Math.random() > 0.5 ? 'high' : 'low',179        });180      }181182      await expect(executeTransaction(183        api, 184        alice, 185        api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 186      )).to.be.rejectedWith(/common\.PropertyLimitReached/);187188      const properties = (await api.query.common.collectionProperties(collection)).toJSON();189      expect(properties.map).to.be.empty;190      expect(properties.consumedSpace).to.equal(0);191    });192  });193194  it('Fails to set properties with invalid names', async () => {195    await usingApi(async api => {196      const collection = await createCollectionExpectSuccess();197198      const invalidProperties = [199        [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],200        [{key: 'Mr.Sandman', value: 'Bring me a gene'}],201        [{key: 'déjà vu', value: 'hmm...'}],202      ];203204      for (let i = 0; i < invalidProperties.length; i++) {205        await expect(executeTransaction(206          api, 207          alice, 208          api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 209        ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);210      }211212      await expect(executeTransaction(213        api, 214        alice, 215        api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 216      ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);217218      await expect(executeTransaction(219        api, 220        alice, 221        api.tx.unique.setCollectionProperties(collection, [222          {key: 'CRISPR-Cas9', value: 'rewriting nature!'},223        ]), 224      ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;225226      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');227228      const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();229      expect(properties).to.be.deep.equal([230        {key: 'CRISPR-Cas9', value: 'rewriting nature!'},231      ]);232233      for (let i = 0; i < invalidProperties.length; i++) {234        await expect(executeTransaction(235          api, 236          alice, 237          api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 238        ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);239      }240    });241  });242});243244// ---------- ACCESS RIGHTS245246describe('Integration Test: Access Rights to Token Properties', () => {247  before(async () => {248    await usingApi(async () => {249      alice = privateKey('//Alice');250      bob = privateKey('//Bob');251    });252  });253  254  it('Reads access rights to properties of a collection', async () => {255    await usingApi(async api => {256      const collection = await createCollectionExpectSuccess();257      const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();258      expect(propertyRights).to.be.empty;259    });260  });261  262  it('Sets access rights to properties of a collection', async () => {263    await usingApi(async api => {264      const collection = await createCollectionExpectSuccess();265266      await expect(executeTransaction(267        api, 268        alice, 269        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 270      )).to.not.be.rejected;271272      await addCollectionAdminExpectSuccess(alice, collection, bob.address);273274      await expect(executeTransaction(275        api, 276        alice, 277        api.tx.unique.setPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 278      )).to.not.be.rejected;279280      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();281      expect(propertyRights).to.be.deep.equal([282        {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},283        {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},284      ]);285    });286  });287  288  it('Changes access rights to properties of a collection', async () => {289    await usingApi(async api => {290      const collection = await createCollectionExpectSuccess();291292      await expect(executeTransaction(293        api, 294        alice, 295        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 296      )).to.not.be.rejected;297298      await expect(executeTransaction(299        api, 300        alice, 301        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 302      )).to.not.be.rejected;303304      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();305      expect(propertyRights).to.be.deep.equal([306        {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},307      ]);308    });309  });310});311312describe('Negative Integration Test: Access Rights to Token Properties', () => {313  before(async () => {314    await usingApi(async () => {315      alice = privateKey('//Alice');316      bob = privateKey('//Bob');317    });318  });319320  it('Prevents from setting access rights to properties of a collection if not an onwer/admin', async () => {321    await usingApi(async api => {322      const collection = await createCollectionExpectSuccess();323324      await expect(executeTransaction(325        api, 326        bob, 327        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 328      )).to.be.rejectedWith(/common\.NoPermission/);329330      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();331      expect(propertyRights).to.be.empty;332    });333  });334335  it('Prevents from adding too many possible properties', async () => {336    await usingApi(async api => {337      const collection = await createCollectionExpectSuccess();338339      const constitution = [];340      for (let i = 0; i < 65; i++) {341        constitution.push({342          key: 'property_' + i,343          permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},344        });345      }346347      await expect(executeTransaction(348        api, 349        alice, 350        api.tx.unique.setPropertyPermissions(collection, constitution), 351      )).to.be.rejectedWith(/common\.PropertyLimitReached/);352353      const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();354      expect(propertyRights).to.be.empty;355    });356  });357358  it('Prevents access rights to be modified if constant', async () => {359    await usingApi(async api => {360      const collection = await createCollectionExpectSuccess();361362      await expect(executeTransaction(363        api, 364        alice, 365        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 366      )).to.not.be.rejected;367368      await expect(executeTransaction(369        api, 370        alice, 371        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 372      )).to.be.rejectedWith(/common\.NoPermission/);373374      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();375      expect(propertyRights).to.deep.equal([376        {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},377      ]);378    });379  });380381  it('Prevents adding properties with invalid names', async () => {382    await usingApi(async api => {383      const collection = await createCollectionExpectSuccess();384385      const invalidProperties = [386        [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],387        [{key: 'G#4', permission: {tokenOwner: true}}],388        [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],389      ];390391      for (let i = 0; i < invalidProperties.length; i++) {392        await expect(executeTransaction(393          api, 394          alice, 395          api.tx.unique.setPropertyPermissions(collection, invalidProperties[i]), 396        ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);397      }398399      await expect(executeTransaction(400        api, 401        alice, 402        api.tx.unique.setPropertyPermissions(collection, [{key: '', permission: {}}]), 403      ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);404405      const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string406      await expect(executeTransaction(407        api, 408        alice, 409        api.tx.unique.setPropertyPermissions(collection, [410          {key: correctKey, permission: {collectionAdmin: true}},411        ]), 412      ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;413414      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');415416      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();417      expect(propertyRights).to.be.deep.equal([418        {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},419      ]);420    });421  });422});423424// ---------- TOKEN PROPERTIES425426describe('Integration Test: Token Properties', () => {427  let collection: number;428  let token: number;429  let permissions: {permission: any, signers: IKeyringPair[]}[];430431  before(async () => {432    await usingApi(async () => {433      alice = privateKey('//Alice');434      bob = privateKey('//Bob');435      charlie = privateKey('//Charlie');436437      permissions = [438        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},439        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},440        {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},441        {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},442        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},443        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},444      ];445    });446  });447448  beforeEach(async () => {449    await usingApi(async () => {450      collection = await createCollectionExpectSuccess();451      token = await createItemExpectSuccess(alice, collection, 'NFT');452      await addCollectionAdminExpectSuccess(alice, collection, bob.address);453      await transferExpectSuccess(collection, token, alice, charlie);454    });455  });456  457  it('Reads yet empty properties of a token', async () => {458    await usingApi(async api => {459      const collection = await createCollectionExpectSuccess();460      const token = await createItemExpectSuccess(alice, collection, 'NFT');461  462      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();463      expect(properties.map).to.be.empty;464      expect(properties.consumedSpace).to.be.equal(0);465466      const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;467      expect(tokenData).to.be.empty;468    });469  });470471  it('Assigns properties to a token according to permissions', async () => {472    await usingApi(async api => {473      const propertyKeys: string[] = [];474      let i = 0;475      for (const permission of permissions) {476        for (const signer of permission.signers) {477          const key = i + '_' + signer.address;478          propertyKeys.push(key);479480          await expect(executeTransaction(481            api, 482            alice, 483            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 484          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;485486          await expect(executeTransaction(487            api, 488            signer, 489            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 490          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;491        }492493        i++;494      }495496      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];497      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];498      for (let i = 0; i < properties.length; i++) {499        expect(properties[i].value).to.be.equal('Serotonin increase');500        expect(tokensData[i].value).to.be.equal('Serotonin increase');501      }502    });503  });504505  it('Changes properties of a token according to permissions', async () => {506    await usingApi(async api => {507      const propertyKeys: string[] = [];508      let i = 0;509      for (const permission of permissions) {510        if (!permission.permission.mutable) continue;511        512        for (const signer of permission.signers) {513          const key = i + '_' + signer.address;514          propertyKeys.push(key);515516          await expect(executeTransaction(517            api, 518            alice, 519            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 520          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;521522          await expect(executeTransaction(523            api, 524            signer, 525            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 526          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;527528          await expect(executeTransaction(529            api, 530            signer, 531            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 532          ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;533        }534535        i++;536      }537538      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];539      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];540      for (let i = 0; i < properties.length; i++) {541        expect(properties[i].value).to.be.equal('Serotonin stable');542        expect(tokensData[i].value).to.be.equal('Serotonin stable');543      }544    });545  });546547  it('Deletes properties of a token according to permissions', async () => {548    await usingApi(async api => {549      const propertyKeys: string[] = [];550      let i = 0;551552      for (const permission of permissions) {553        if (!permission.permission.mutable) continue;554        555        for (const signer of permission.signers) {556          const key = i + '_' + signer.address;557          propertyKeys.push(key);558559          await expect(executeTransaction(560            api, 561            alice, 562            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 563          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;564565          await expect(executeTransaction(566            api, 567            signer, 568            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 569          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;570571          await expect(executeTransaction(572            api, 573            signer, 574            api.tx.unique.deleteTokenProperties(collection, token, [key]), 575          ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;576        }577        578        i++;579      }580581      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];582      expect(properties).to.be.empty;583      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];584      expect(tokensData).to.be.empty;585      expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);586    });587  });588});589590describe('Negative Integration Test: Token Properties', () => {591  let collection: number;592  let token: number;593  let originalSpace: number;594  let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];595596  before(async () => {597    await usingApi(async () => {598      alice = privateKey('//Alice');599      bob = privateKey('//Bob');600      charlie = privateKey('//Charlie');601      const dave = privateKey('//Dave');602603      constitution = [604        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},605        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},606        {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},607        {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},608        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},609        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},610      ];611    });612  });613614  beforeEach(async () => {615    collection = await createCollectionExpectSuccess();616    token = await createItemExpectSuccess(alice, collection, 'NFT');617    await addCollectionAdminExpectSuccess(alice, collection, bob.address);618    await transferExpectSuccess(collection, token, alice, charlie);619        620    await usingApi(async api => {621      let i = 0;622      for (const passage of constitution) {623        const signer = passage.signers[0];624        625        await expect(executeTransaction(626          api, 627          alice, 628          api.tx.unique.setPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 629        ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;630631        await expect(executeTransaction(632          api, 633          signer, 634          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 635        ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;636637        i++;638      }639640      originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;641    });642  });643644  it('Forbids changing/deleting properties of a token if the user is outside of permissions', async () => {645    await usingApi(async api => {646      let i = -1;647      for (const forbiddance of constitution) {648        i++;649        if (!forbiddance.permission.mutable) continue;650651        await expect(executeTransaction(652          api, 653          forbiddance.sinner, 654          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 655        ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);656657        await expect(executeTransaction(658          api, 659          forbiddance.sinner, 660          api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 661        ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);662      }663664      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();665      expect(properties.consumedSpace).to.be.equal(originalSpace);666    });667  });668669  it('Forbids changing/deleting properties of a token if the property is permanent (constant)', async () => {670    await usingApi(async api => {671      let i = -1;672      for (const permission of constitution) {673        i++;674        if (permission.permission.mutable) continue;675676        await expect(executeTransaction(677          api, 678          permission.signers[0], 679          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 680        ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);681682        await expect(executeTransaction(683          api, 684          permission.signers[0], 685          api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 686        ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);687      }688689      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();690      expect(properties.consumedSpace).to.be.equal(originalSpace);691    });692  });693694  it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission', async () => {695    await usingApi(async api => {696      await expect(executeTransaction(697        api, 698        alice, 699        api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 700      ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);701        702      await expect(executeTransaction(703        api, 704        alice, 705        api.tx.unique.setPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 706      ), 'on setting a new non-permitted property').to.not.be.rejected;707708      await expect(executeTransaction(709        api, 710        alice, 711        api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 712      ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);713714      expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;715      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();716      expect(properties.consumedSpace).to.be.equal(originalSpace);717    });718  });719720  it('Forbids adding too many properties to a token', async () => {721    await usingApi(async api => {722      await expect(executeTransaction(723        api, 724        alice, 725        api.tx.unique.setPropertyPermissions(collection, [726          {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 727          {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},728        ]), 729      ), 'on setting a new non-permitted property').to.not.be.rejected;730731      // Mute the general tx parsing error732      {733        console.error = () => {};734        await expect(executeTransaction(735          api, 736          alice, 737          api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 738        )).to.be.rejected;739      }740741      await expect(executeTransaction(742        api, 743        alice, 744        api.tx.unique.setTokenProperties(collection, token, [745          {key: 'a_holy_book', value: 'word '.repeat(3277)}, 746          {key: 'young_years', value: 'neverending'.repeat(1490)},747        ]), 748      )).to.be.rejectedWith(/common\.NoSpaceForProperty/);749750      expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young years'])).toJSON()).to.be.empty;751      const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();752      expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);753    });754  });755});