git.delta.rocks / unique-network / refs/commits / 1be4d6be040e

difftreelog

source

tests/src/nesting/properties.test.ts31.1 KiBsourcehistory
1import {expect} from 'chai';2import usingApi, {executeTransaction} from '../substrate/substrate-api';3import {4  addCollectionAdminExpectSuccess,5  createCollectionExpectSuccess,6  createItemExpectSuccess,7  getCreateCollectionResult,8  transferExpectSuccess,9} from '../util/helpers';10import {IKeyringPair} from '@polkadot/types/types';1112let alice: IKeyringPair;13let bob: IKeyringPair;14let charlie: IKeyringPair;1516describe('Composite Properties Test', () => {17  before(async () => {18    await usingApi(async (api, privateKeyWrapper) => {19      alice = privateKeyWrapper('//Alice');20      bob = privateKeyWrapper('//Bob');21    });22  });2324  it('Makes sure collectionById supplies required fields', async () => {25    await usingApi(async api => {26      const collectionId = await createCollectionExpectSuccess();2728      const collectionOption = await api.rpc.unique.collectionById(collectionId);29      expect(collectionOption.isSome).to.be.true;30      let collection = collectionOption.unwrap();31      expect(collection.tokenPropertyPermissions.toHuman()).to.be.empty;32      expect(collection.properties.toHuman()).to.be.empty;3334      const propertyPermissions = [35        {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},36        {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},37      ];38      await expect(executeTransaction(39        api, 40        alice, 41        api.tx.unique.setPropertyPermissions(collectionId, propertyPermissions), 42      )).to.not.be.rejected;4344      const collectionProperties = [45        {key: 'black_hole', value: 'LIGO'},46        {key: 'electron', value: 'come bond'}, 47      ];48      await expect(executeTransaction(49        api, 50        alice, 51        api.tx.unique.setCollectionProperties(collectionId, collectionProperties), 52      )).to.not.be.rejected;5354      collection = (await api.rpc.unique.collectionById(collectionId)).unwrap();55      expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);56      expect(collection.properties.toHuman()).to.be.deep.equal(collectionProperties);57    });58  });59});6061// ---------- COLLECTION PROPERTIES6263describe('Integration Test: Collection Properties', () => {64  before(async () => {65    await usingApi(async (api, privateKeyWrapper) => {66      alice = privateKeyWrapper('//Alice');67      bob = privateKeyWrapper('//Bob');68    });69  });7071  it('Reads properties from a collection', async () => {72    await usingApi(async api => {73      const collection = await createCollectionExpectSuccess();74      const properties = (await api.query.common.collectionProperties(collection)).toJSON();75      expect(properties.map).to.be.empty;76      expect(properties.consumedSpace).to.equal(0);77    });78  });7980  it('Sets properties for a collection', async () => {81    await usingApi(async api => {82      const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));83      const {collectionId} = getCreateCollectionResult(events);8485      // As owner86      await expect(executeTransaction(87        api, 88        bob, 89        api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 90      )).to.not.be.rejected;9192      await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);9394      // As administrator95      await expect(executeTransaction(96        api, 97        alice, 98        api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 99      )).to.not.be.rejected;100101      const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();102      expect(properties).to.be.deep.equal([103        {key: 'electron', value: 'come bond'},104        {key: 'black_hole', value: ''},105      ]);106    });107  });108109  it('Changes properties of a collection', async () => {110    await usingApi(async api => {111      const collection = await createCollectionExpectSuccess();112113      await expect(executeTransaction(114        api, 115        alice, 116        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 117      )).to.not.be.rejected;118119      // Mutate the properties120      await expect(executeTransaction(121        api, 122        alice, 123        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 124      )).to.not.be.rejected;125126      const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();127      expect(properties).to.be.deep.equal([128        {key: 'electron', value: 'bonded'},129        {key: 'black_hole', value: 'LIGO'},130      ]);131    });132  });133134  it('Deletes properties of a collection', async () => {135    await usingApi(async api => {136      const collection = await createCollectionExpectSuccess();137138      await expect(executeTransaction(139        api, 140        alice, 141        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 142      )).to.not.be.rejected;143144      await expect(executeTransaction(145        api, 146        alice, 147        api.tx.unique.deleteCollectionProperties(collection, ['electron']), 148      )).to.not.be.rejected;149150      const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();151      expect(properties).to.be.deep.equal([152        {key: 'black_hole', value: 'LIGO'},153      ]);154    });155  });156});157158describe('Negative Integration Test: Collection Properties', () => {159  before(async () => {160    await usingApi(async (api, privateKeyWrapper) => {161      alice = privateKeyWrapper('//Alice');162      bob = privateKeyWrapper('//Bob');163    });164  });165  166  it('Fails to set properties in a collection if not its onwer/administrator', async () => {167    await usingApi(async api => {168      const collection = await createCollectionExpectSuccess();169170      await expect(executeTransaction(171        api, 172        bob, 173        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 174      )).to.be.rejectedWith(/common\.NoPermission/);175  176      const properties = (await api.query.common.collectionProperties(collection)).toJSON();177      expect(properties.map).to.be.empty;178      expect(properties.consumedSpace).to.equal(0);179    });180  });181  182  it('Fails to set properties that exceed the limits', async () => {183    await usingApi(async api => {184      const collection = await createCollectionExpectSuccess();185      const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 186187      // Mute the general tx parsing error, too many bytes to process188      {189        console.error = () => {};190        await expect(executeTransaction(191          api, 192          alice, 193          api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 194        )).to.be.rejected;195      }196197      let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();198      expect(properties).to.be.empty;199200      await expect(executeTransaction(201        api, 202        alice, 203        api.tx.unique.setCollectionProperties(collection, [204          {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 205          {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 206        ]), 207      )).to.be.rejectedWith(/common\.NoSpaceForProperty/);208209      properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();210      expect(properties).to.be.empty;211    });212  });213  214  it('Fails to set more properties than it is allowed', async () => {215    await usingApi(async api => {216      const collection = await createCollectionExpectSuccess();217218      const propertiesToBeSet = [];219      for (let i = 0; i < 65; i++) {220        propertiesToBeSet.push({221          key: 'electron_' + i,222          value: Math.random() > 0.5 ? 'high' : 'low',223        });224      }225226      await expect(executeTransaction(227        api, 228        alice, 229        api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 230      )).to.be.rejectedWith(/common\.PropertyLimitReached/);231232      const properties = (await api.query.common.collectionProperties(collection)).toJSON();233      expect(properties.map).to.be.empty;234      expect(properties.consumedSpace).to.equal(0);235    });236  });237238  it('Fails to set properties with invalid names', async () => {239    await usingApi(async api => {240      const collection = await createCollectionExpectSuccess();241242      const invalidProperties = [243        [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],244        [{key: 'Mr.Sandman', value: 'Bring me a gene'}],245        [{key: 'déjà vu', value: 'hmm...'}],246      ];247248      for (let i = 0; i < invalidProperties.length; i++) {249        await expect(executeTransaction(250          api, 251          alice, 252          api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 253        ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);254      }255256      await expect(executeTransaction(257        api, 258        alice, 259        api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 260      ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);261262      await expect(executeTransaction(263        api, 264        alice, 265        api.tx.unique.setCollectionProperties(collection, [266          {key: 'CRISPR-Cas9', value: 'rewriting nature!'},267        ]), 268      ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;269270      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');271272      const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();273      expect(properties).to.be.deep.equal([274        {key: 'CRISPR-Cas9', value: 'rewriting nature!'},275      ]);276277      for (let i = 0; i < invalidProperties.length; i++) {278        await expect(executeTransaction(279          api, 280          alice, 281          api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 282        ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);283      }284    });285  });286});287288// ---------- ACCESS RIGHTS289290describe('Integration Test: Access Rights to Token Properties', () => {291  before(async () => {292    await usingApi(async (api, privateKeyWrapper) => {293      alice = privateKeyWrapper('//Alice');294      bob = privateKeyWrapper('//Bob');295    });296  });297  298  it('Reads access rights to properties of a collection', async () => {299    await usingApi(async api => {300      const collection = await createCollectionExpectSuccess();301      const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();302      expect(propertyRights).to.be.empty;303    });304  });305  306  it('Sets access rights to properties of a collection', async () => {307    await usingApi(async api => {308      const collection = await createCollectionExpectSuccess();309310      await expect(executeTransaction(311        api, 312        alice, 313        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 314      )).to.not.be.rejected;315316      await addCollectionAdminExpectSuccess(alice, collection, bob.address);317318      await expect(executeTransaction(319        api, 320        alice, 321        api.tx.unique.setPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 322      )).to.not.be.rejected;323324      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();325      expect(propertyRights).to.be.deep.equal([326        {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},327        {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},328      ]);329    });330  });331  332  it('Changes access rights to properties of a collection', async () => {333    await usingApi(async api => {334      const collection = await createCollectionExpectSuccess();335336      await expect(executeTransaction(337        api, 338        alice, 339        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 340      )).to.not.be.rejected;341342      await expect(executeTransaction(343        api, 344        alice, 345        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 346      )).to.not.be.rejected;347348      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();349      expect(propertyRights).to.be.deep.equal([350        {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},351      ]);352    });353  });354});355356describe('Negative Integration Test: Access Rights to Token Properties', () => {357  before(async () => {358    await usingApi(async (api, privateKeyWrapper) => {359      alice = privateKeyWrapper('//Alice');360      bob = privateKeyWrapper('//Bob');361    });362  });363364  it('Prevents from setting access rights to properties of a collection if not an onwer/admin', async () => {365    await usingApi(async api => {366      const collection = await createCollectionExpectSuccess();367368      await expect(executeTransaction(369        api, 370        bob, 371        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 372      )).to.be.rejectedWith(/common\.NoPermission/);373374      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();375      expect(propertyRights).to.be.empty;376    });377  });378379  it('Prevents from adding too many possible properties', async () => {380    await usingApi(async api => {381      const collection = await createCollectionExpectSuccess();382383      const constitution = [];384      for (let i = 0; i < 65; i++) {385        constitution.push({386          key: 'property_' + i,387          permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},388        });389      }390391      await expect(executeTransaction(392        api, 393        alice, 394        api.tx.unique.setPropertyPermissions(collection, constitution), 395      )).to.be.rejectedWith(/common\.PropertyLimitReached/);396397      const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();398      expect(propertyRights).to.be.empty;399    });400  });401402  it('Prevents access rights to be modified if constant', async () => {403    await usingApi(async api => {404      const collection = await createCollectionExpectSuccess();405406      await expect(executeTransaction(407        api, 408        alice, 409        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 410      )).to.not.be.rejected;411412      await expect(executeTransaction(413        api, 414        alice, 415        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 416      )).to.be.rejectedWith(/common\.NoPermission/);417418      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();419      expect(propertyRights).to.deep.equal([420        {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},421      ]);422    });423  });424425  it('Prevents adding properties with invalid names', async () => {426    await usingApi(async api => {427      const collection = await createCollectionExpectSuccess();428429      const invalidProperties = [430        [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],431        [{key: 'G#4', permission: {tokenOwner: true}}],432        [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],433      ];434435      for (let i = 0; i < invalidProperties.length; i++) {436        await expect(executeTransaction(437          api, 438          alice, 439          api.tx.unique.setPropertyPermissions(collection, invalidProperties[i]), 440        ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);441      }442443      await expect(executeTransaction(444        api, 445        alice, 446        api.tx.unique.setPropertyPermissions(collection, [{key: '', permission: {}}]), 447      ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);448449      const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string450      await expect(executeTransaction(451        api, 452        alice, 453        api.tx.unique.setPropertyPermissions(collection, [454          {key: correctKey, permission: {collectionAdmin: true}},455        ]), 456      ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;457458      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');459460      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();461      expect(propertyRights).to.be.deep.equal([462        {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},463      ]);464    });465  });466});467468// ---------- TOKEN PROPERTIES469470describe('Integration Test: Token Properties', () => {471  let collection: number;472  let token: number;473  let permissions: {permission: any, signers: IKeyringPair[]}[];474475  before(async () => {476    await usingApi(async (api, privateKeyWrapper) => {477      alice = privateKeyWrapper('//Alice');478      bob = privateKeyWrapper('//Bob');479      charlie = privateKeyWrapper('//Charlie');480481      permissions = [482        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},483        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},484        {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},485        {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},486        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},487        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},488      ];489    });490  });491492  beforeEach(async () => {493    await usingApi(async () => {494      collection = await createCollectionExpectSuccess();495      token = await createItemExpectSuccess(alice, collection, 'NFT');496      await addCollectionAdminExpectSuccess(alice, collection, bob.address);497      await transferExpectSuccess(collection, token, alice, charlie);498    });499  });500  501  it('Reads yet empty properties of a token', async () => {502    await usingApi(async api => {503      const collection = await createCollectionExpectSuccess();504      const token = await createItemExpectSuccess(alice, collection, 'NFT');505  506      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();507      expect(properties.map).to.be.empty;508      expect(properties.consumedSpace).to.be.equal(0);509510      const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;511      expect(tokenData).to.be.empty;512    });513  });514515  it('Assigns properties to a token according to permissions', async () => {516    await usingApi(async api => {517      const propertyKeys: string[] = [];518      let i = 0;519      for (const permission of permissions) {520        for (const signer of permission.signers) {521          const key = i + '_' + signer.address;522          propertyKeys.push(key);523524          await expect(executeTransaction(525            api, 526            alice, 527            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 528          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;529530          await expect(executeTransaction(531            api, 532            signer, 533            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 534          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;535        }536537        i++;538      }539540      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];541      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];542      for (let i = 0; i < properties.length; i++) {543        expect(properties[i].value).to.be.equal('Serotonin increase');544        expect(tokensData[i].value).to.be.equal('Serotonin increase');545      }546    });547  });548549  it('Changes properties of a token according to permissions', async () => {550    await usingApi(async api => {551      const propertyKeys: string[] = [];552      let i = 0;553      for (const permission of permissions) {554        if (!permission.permission.mutable) continue;555        556        for (const signer of permission.signers) {557          const key = i + '_' + signer.address;558          propertyKeys.push(key);559560          await expect(executeTransaction(561            api, 562            alice, 563            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 564          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;565566          await expect(executeTransaction(567            api, 568            signer, 569            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 570          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;571572          await expect(executeTransaction(573            api, 574            signer, 575            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 576          ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;577        }578579        i++;580      }581582      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];583      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];584      for (let i = 0; i < properties.length; i++) {585        expect(properties[i].value).to.be.equal('Serotonin stable');586        expect(tokensData[i].value).to.be.equal('Serotonin stable');587      }588    });589  });590591  it('Deletes properties of a token according to permissions', async () => {592    await usingApi(async api => {593      const propertyKeys: string[] = [];594      let i = 0;595596      for (const permission of permissions) {597        if (!permission.permission.mutable) continue;598        599        for (const signer of permission.signers) {600          const key = i + '_' + signer.address;601          propertyKeys.push(key);602603          await expect(executeTransaction(604            api, 605            alice, 606            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 607          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;608609          await expect(executeTransaction(610            api, 611            signer, 612            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 613          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;614615          await expect(executeTransaction(616            api, 617            signer, 618            api.tx.unique.deleteTokenProperties(collection, token, [key]), 619          ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;620        }621        622        i++;623      }624625      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];626      expect(properties).to.be.empty;627      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];628      expect(tokensData).to.be.empty;629      expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);630    });631  });632});633634describe('Negative Integration Test: Token Properties', () => {635  let collection: number;636  let token: number;637  let originalSpace: number;638  let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];639640  before(async () => {641    await usingApi(async (api, privateKeyWrapper) => {642      alice = privateKeyWrapper('//Alice');643      bob = privateKeyWrapper('//Bob');644      charlie = privateKeyWrapper('//Charlie');645      const dave = privateKeyWrapper('//Dave');646647      constitution = [648        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},649        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},650        {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},651        {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},652        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},653        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},654      ];655    });656  });657658  beforeEach(async () => {659    collection = await createCollectionExpectSuccess();660    token = await createItemExpectSuccess(alice, collection, 'NFT');661    await addCollectionAdminExpectSuccess(alice, collection, bob.address);662    await transferExpectSuccess(collection, token, alice, charlie);663        664    await usingApi(async api => {665      let i = 0;666      for (const passage of constitution) {667        const signer = passage.signers[0];668        669        await expect(executeTransaction(670          api, 671          alice, 672          api.tx.unique.setPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 673        ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;674675        await expect(executeTransaction(676          api, 677          signer, 678          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 679        ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;680681        i++;682      }683684      originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;685    });686  });687688  it('Forbids changing/deleting properties of a token if the user is outside of permissions', async () => {689    await usingApi(async api => {690      let i = -1;691      for (const forbiddance of constitution) {692        i++;693        if (!forbiddance.permission.mutable) continue;694695        await expect(executeTransaction(696          api, 697          forbiddance.sinner, 698          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 699        ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);700701        await expect(executeTransaction(702          api, 703          forbiddance.sinner, 704          api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 705        ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);706      }707708      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();709      expect(properties.consumedSpace).to.be.equal(originalSpace);710    });711  });712713  it('Forbids changing/deleting properties of a token if the property is permanent (immutable)', async () => {714    await usingApi(async api => {715      let i = -1;716      for (const permission of constitution) {717        i++;718        if (permission.permission.mutable) continue;719720        await expect(executeTransaction(721          api, 722          permission.signers[0], 723          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 724        ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);725726        await expect(executeTransaction(727          api, 728          permission.signers[0], 729          api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 730        ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);731      }732733      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();734      expect(properties.consumedSpace).to.be.equal(originalSpace);735    });736  });737738  it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission', async () => {739    await usingApi(async api => {740      await expect(executeTransaction(741        api, 742        alice, 743        api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 744      ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);745        746      await expect(executeTransaction(747        api, 748        alice, 749        api.tx.unique.setPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 750      ), 'on setting a new non-permitted property').to.not.be.rejected;751752      await expect(executeTransaction(753        api, 754        alice, 755        api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 756      ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);757758      expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;759      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();760      expect(properties.consumedSpace).to.be.equal(originalSpace);761    });762  });763764  it('Forbids adding too many properties to a token', async () => {765    await usingApi(async api => {766      await expect(executeTransaction(767        api, 768        alice, 769        api.tx.unique.setPropertyPermissions(collection, [770          {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 771          {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},772        ]), 773      ), 'on setting a new non-permitted property').to.not.be.rejected;774775      // Mute the general tx parsing error776      {777        console.error = () => {};778        await expect(executeTransaction(779          api, 780          alice, 781          api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 782        )).to.be.rejected;783      }784785      await expect(executeTransaction(786        api, 787        alice, 788        api.tx.unique.setTokenProperties(collection, token, [789          {key: 'a_holy_book', value: 'word '.repeat(3277)}, 790          {key: 'young_years', value: 'neverending'.repeat(1490)},791        ]), 792      )).to.be.rejectedWith(/common\.NoSpaceForProperty/);793794      expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young_years'])).toJSON()).to.be.empty;795      const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();796      expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);797    });798  });799});