git.delta.rocks / unique-network / refs/commits / 73ad88465834

difftreelog

source

tests/src/nesting/properties.test.ts30.9 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;1617describe('Composite Properties Test', () => {18  before(async () => {19    await usingApi(async () => {20      alice = privateKey('//Alice');21      bob = privateKey('//Bob');22    });23  });2425  it('Makes sure collectionById supplies required fields', async () => {26    await usingApi(async api => {27      const collectionId = await createCollectionExpectSuccess();2829      const collectionOption = await api.rpc.unique.collectionById(collectionId);30      expect(collectionOption.isSome).to.be.true;31      let collection = collectionOption.unwrap();32      expect(collection.tokenPropertyPermissions.toHuman()).to.be.empty;33      expect(collection.properties.toHuman()).to.be.empty;3435      const propertyPermissions = [36        {key: 'mindgame', permission: {collectionAdmin: true, mutable: false, tokenOwner: true}},37        {key: 'skullduggery', permission: {collectionAdmin: false, mutable: true, tokenOwner: false}},38      ];39      await expect(executeTransaction(40        api, 41        alice, 42        api.tx.unique.setPropertyPermissions(collectionId, propertyPermissions), 43      )).to.not.be.rejected;4445      const collectionProperties = [46        {key: 'black_hole', value: 'LIGO'},47        {key: 'electron', value: 'come bond'}, 48      ];49      await expect(executeTransaction(50        api, 51        alice, 52        api.tx.unique.setCollectionProperties(collectionId, collectionProperties), 53      )).to.not.be.rejected;5455      collection = (await api.rpc.unique.collectionById(collectionId)).unwrap();56      expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);57      expect(collection.properties.toHuman()).to.be.deep.equal(collectionProperties);58    });59  });60});6162// ---------- COLLECTION PROPERTIES6364describe('Integration Test: Collection Properties', () => {65  before(async () => {66    await usingApi(async () => {67      alice = privateKey('//Alice');68      bob = privateKey('//Bob');69    });70  });7172  it('Reads properties from a collection', async () => {73    await usingApi(async api => {74      const collection = await createCollectionExpectSuccess();75      const properties = (await api.query.common.collectionProperties(collection)).toJSON();76      expect(properties.map).to.be.empty;77      expect(properties.consumedSpace).to.equal(0);78    });79  });8081  it('Sets properties for a collection', async () => {82    await usingApi(async api => {83      const events = await executeTransaction(api, bob, api.tx.unique.createCollectionEx({mode: 'NFT'}));84      const {collectionId} = getCreateCollectionResult(events);8586      // As owner87      await expect(executeTransaction(88        api, 89        bob, 90        api.tx.unique.setCollectionProperties(collectionId, [{key: 'electron', value: 'come bond'}]), 91      )).to.not.be.rejected;9293      await addCollectionAdminExpectSuccess(bob, collectionId, alice.address);9495      // As administrator96      await expect(executeTransaction(97        api, 98        alice, 99        api.tx.unique.setCollectionProperties(collectionId, [{key: 'black_hole'}]), 100      )).to.not.be.rejected;101102      const properties = (await api.rpc.unique.collectionProperties(collectionId, ['electron', 'black_hole'])).toHuman();103      expect(properties).to.be.deep.equal([104        {key: 'electron', value: 'come bond'},105        {key: 'black_hole', value: ''},106      ]);107    });108  });109110  it('Changes properties of a collection', async () => {111    await usingApi(async api => {112      const collection = await createCollectionExpectSuccess();113114      await expect(executeTransaction(115        api, 116        alice, 117        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole'}]), 118      )).to.not.be.rejected;119120      // Mutate the properties121      await expect(executeTransaction(122        api, 123        alice, 124        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'bonded'}, {key: 'black_hole', value: 'LIGO'}]), 125      )).to.not.be.rejected;126127      const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();128      expect(properties).to.be.deep.equal([129        {key: 'electron', value: 'bonded'},130        {key: 'black_hole', value: 'LIGO'},131      ]);132    });133  });134135  it('Deletes properties of a collection', async () => {136    await usingApi(async api => {137      const collection = await createCollectionExpectSuccess();138139      await expect(executeTransaction(140        api, 141        alice, 142        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 143      )).to.not.be.rejected;144145      await expect(executeTransaction(146        api, 147        alice, 148        api.tx.unique.deleteCollectionProperties(collection, ['electron']), 149      )).to.not.be.rejected;150151      const properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black_hole'])).toHuman();152      expect(properties).to.be.deep.equal([153        {key: 'black_hole', value: 'LIGO'},154      ]);155    });156  });157});158159describe('Negative Integration Test: Collection Properties', () => {160  before(async () => {161    await usingApi(async () => {162      alice = privateKey('//Alice');163      bob = privateKey('//Bob');164    });165  });166  167  it('Fails to set properties in a collection if not its onwer/administrator', async () => {168    await usingApi(async api => {169      const collection = await createCollectionExpectSuccess();170171      await expect(executeTransaction(172        api, 173        bob, 174        api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]), 175      )).to.be.rejectedWith(/common\.NoPermission/);176  177      const properties = (await api.query.common.collectionProperties(collection)).toJSON();178      expect(properties.map).to.be.empty;179      expect(properties.consumedSpace).to.equal(0);180    });181  });182  183  it('Fails to set properties that exceed the limits', async () => {184    await usingApi(async api => {185      const collection = await createCollectionExpectSuccess();186      const spaceLimit = (await api.query.common.collectionProperties(collection)).toJSON().spaceLimit as number; 187188      // Mute the general tx parsing error, too many bytes to process189      {190        console.error = () => {};191        await expect(executeTransaction(192          api, 193          alice, 194          api.tx.unique.setCollectionProperties(collection, [{key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))}]), 195        )).to.be.rejected;196      }197198      let properties = (await api.rpc.unique.collectionProperties(collection, ['electron'])).toJSON();199      expect(properties).to.be.empty;200201      await expect(executeTransaction(202        api, 203        alice, 204        api.tx.unique.setCollectionProperties(collection, [205          {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))}, 206          {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))}, 207        ]), 208      )).to.be.rejectedWith(/common\.NoSpaceForProperty/);209210      properties = (await api.rpc.unique.collectionProperties(collection, ['electron', 'black hole'])).toJSON();211      expect(properties).to.be.empty;212    });213  });214  215  it('Fails to set more properties than it is allowed', async () => {216    await usingApi(async api => {217      const collection = await createCollectionExpectSuccess();218219      const propertiesToBeSet = [];220      for (let i = 0; i < 65; i++) {221        propertiesToBeSet.push({222          key: 'electron_' + i,223          value: Math.random() > 0.5 ? 'high' : 'low',224        });225      }226227      await expect(executeTransaction(228        api, 229        alice, 230        api.tx.unique.setCollectionProperties(collection, propertiesToBeSet), 231      )).to.be.rejectedWith(/common\.PropertyLimitReached/);232233      const properties = (await api.query.common.collectionProperties(collection)).toJSON();234      expect(properties.map).to.be.empty;235      expect(properties.consumedSpace).to.equal(0);236    });237  });238239  it('Fails to set properties with invalid names', async () => {240    await usingApi(async api => {241      const collection = await createCollectionExpectSuccess();242243      const invalidProperties = [244        [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],245        [{key: 'Mr.Sandman', value: 'Bring me a gene'}],246        [{key: 'déjà vu', value: 'hmm...'}],247      ];248249      for (let i = 0; i < invalidProperties.length; i++) {250        await expect(executeTransaction(251          api, 252          alice, 253          api.tx.unique.setCollectionProperties(collection, invalidProperties[i]), 254        ), `on rejecting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);255      }256257      await expect(executeTransaction(258        api, 259        alice, 260        api.tx.unique.setCollectionProperties(collection, [{key: '', value: 'nothing must not exist'}]), 261      ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);262263      await expect(executeTransaction(264        api, 265        alice, 266        api.tx.unique.setCollectionProperties(collection, [267          {key: 'CRISPR-Cas9', value: 'rewriting nature!'},268        ]), 269      ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;270271      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');272273      const properties = (await api.rpc.unique.collectionProperties(collection, keys)).toHuman();274      expect(properties).to.be.deep.equal([275        {key: 'CRISPR-Cas9', value: 'rewriting nature!'},276      ]);277278      for (let i = 0; i < invalidProperties.length; i++) {279        await expect(executeTransaction(280          api, 281          alice, 282          api.tx.unique.deleteCollectionProperties(collection, invalidProperties[i].map(propertySet => propertySet.key)), 283        ), `on trying to delete the non-existent badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);284      }285    });286  });287});288289// ---------- ACCESS RIGHTS290291describe('Integration Test: Access Rights to Token Properties', () => {292  before(async () => {293    await usingApi(async () => {294      alice = privateKey('//Alice');295      bob = privateKey('//Bob');296    });297  });298  299  it('Reads access rights to properties of a collection', async () => {300    await usingApi(async api => {301      const collection = await createCollectionExpectSuccess();302      const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();303      expect(propertyRights).to.be.empty;304    });305  });306  307  it('Sets access rights to properties of a collection', async () => {308    await usingApi(async api => {309      const collection = await createCollectionExpectSuccess();310311      await expect(executeTransaction(312        api, 313        alice, 314        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true}}]), 315      )).to.not.be.rejected;316317      await addCollectionAdminExpectSuccess(alice, collection, bob.address);318319      await expect(executeTransaction(320        api, 321        alice, 322        api.tx.unique.setPropertyPermissions(collection, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]), 323      )).to.not.be.rejected;324325      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery', 'mindgame'])).toHuman();326      expect(propertyRights).to.be.deep.equal([327        {key: 'skullduggery', permission: {'mutable': true, 'collectionAdmin': false, 'tokenOwner': false}},328        {key: 'mindgame', permission: {'mutable': false, 'collectionAdmin': true, 'tokenOwner': false}},329      ]);330    });331  });332  333  it('Changes access rights to properties of a collection', async () => {334    await usingApi(async api => {335      const collection = await createCollectionExpectSuccess();336337      await expect(executeTransaction(338        api, 339        alice, 340        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]), 341      )).to.not.be.rejected;342343      await expect(executeTransaction(344        api, 345        alice, 346        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 347      )).to.not.be.rejected;348349      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();350      expect(propertyRights).to.be.deep.equal([351        {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},352      ]);353    });354  });355});356357describe('Negative Integration Test: Access Rights to Token Properties', () => {358  before(async () => {359    await usingApi(async () => {360      alice = privateKey('//Alice');361      bob = privateKey('//Bob');362    });363  });364365  it('Prevents from setting access rights to properties of a collection if not an onwer/admin', async () => {366    await usingApi(async api => {367      const collection = await createCollectionExpectSuccess();368369      await expect(executeTransaction(370        api, 371        bob, 372        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]), 373      )).to.be.rejectedWith(/common\.NoPermission/);374375      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toJSON();376      expect(propertyRights).to.be.empty;377    });378  });379380  it('Prevents from adding too many possible properties', async () => {381    await usingApi(async api => {382      const collection = await createCollectionExpectSuccess();383384      const constitution = [];385      for (let i = 0; i < 65; i++) {386        constitution.push({387          key: 'property_' + i,388          permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},389        });390      }391392      await expect(executeTransaction(393        api, 394        alice, 395        api.tx.unique.setPropertyPermissions(collection, constitution), 396      )).to.be.rejectedWith(/common\.PropertyLimitReached/);397398      const propertyRights = (await api.query.common.collectionPropertyPermissions(collection)).toJSON();399      expect(propertyRights).to.be.empty;400    });401  });402403  it('Prevents access rights to be modified if constant', async () => {404    await usingApi(async api => {405      const collection = await createCollectionExpectSuccess();406407      await expect(executeTransaction(408        api, 409        alice, 410        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]), 411      )).to.not.be.rejected;412413      await expect(executeTransaction(414        api, 415        alice, 416        api.tx.unique.setPropertyPermissions(collection, [{key: 'skullduggery', permission: {}}]), 417      )).to.be.rejectedWith(/common\.NoPermission/);418419      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, ['skullduggery'])).toHuman();420      expect(propertyRights).to.deep.equal([421        {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},422      ]);423    });424  });425426  it('Prevents adding properties with invalid names', async () => {427    await usingApi(async api => {428      const collection = await createCollectionExpectSuccess();429430      const invalidProperties = [431        [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],432        [{key: 'G#4', permission: {tokenOwner: true}}],433        [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],434      ];435436      for (let i = 0; i < invalidProperties.length; i++) {437        await expect(executeTransaction(438          api, 439          alice, 440          api.tx.unique.setPropertyPermissions(collection, invalidProperties[i]), 441        ), `on setting the new badly-named property #${i}`).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);442      }443444      await expect(executeTransaction(445        api, 446        alice, 447        api.tx.unique.setPropertyPermissions(collection, [{key: '', permission: {}}]), 448      ), 'on rejecting an unnamed property').to.be.rejectedWith(/common\.EmptyPropertyKey/);449450      const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string451      await expect(executeTransaction(452        api, 453        alice, 454        api.tx.unique.setPropertyPermissions(collection, [455          {key: correctKey, permission: {collectionAdmin: true}},456        ]), 457      ), 'on setting the correctly-but-still-badly-named property').to.not.be.rejected;458459      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');460461      const propertyRights = (await api.rpc.unique.propertyPermissions(collection, keys)).toHuman();462      expect(propertyRights).to.be.deep.equal([463        {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},464      ]);465    });466  });467});468469// ---------- TOKEN PROPERTIES470471describe('Integration Test: Token Properties', () => {472  let collection: number;473  let token: number;474  let permissions: {permission: any, signers: IKeyringPair[]}[];475476  before(async () => {477    await usingApi(async () => {478      alice = privateKey('//Alice');479      bob = privateKey('//Bob');480      charlie = privateKey('//Charlie');481482      permissions = [483        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},484        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},485        {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},486        {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},487        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},488        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},489      ];490    });491  });492493  beforeEach(async () => {494    await usingApi(async () => {495      collection = await createCollectionExpectSuccess();496      token = await createItemExpectSuccess(alice, collection, 'NFT');497      await addCollectionAdminExpectSuccess(alice, collection, bob.address);498      await transferExpectSuccess(collection, token, alice, charlie);499    });500  });501  502  it('Reads yet empty properties of a token', async () => {503    await usingApi(async api => {504      const collection = await createCollectionExpectSuccess();505      const token = await createItemExpectSuccess(alice, collection, 'NFT');506  507      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();508      expect(properties.map).to.be.empty;509      expect(properties.consumedSpace).to.be.equal(0);510511      const tokenData = (await api.rpc.unique.tokenData(collection, token, ['anything'])).toJSON().properties;512      expect(tokenData).to.be.empty;513    });514  });515516  it('Assigns properties to a token according to permissions', async () => {517    await usingApi(async api => {518      const propertyKeys: string[] = [];519      let i = 0;520      for (const permission of permissions) {521        for (const signer of permission.signers) {522          const key = i + '_' + signer.address;523          propertyKeys.push(key);524525          await expect(executeTransaction(526            api, 527            alice, 528            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 529          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;530531          await expect(executeTransaction(532            api, 533            signer, 534            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 535          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;536        }537538        i++;539      }540541      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];542      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];543      for (let i = 0; i < properties.length; i++) {544        expect(properties[i].value).to.be.equal('Serotonin increase');545        expect(tokensData[i].value).to.be.equal('Serotonin increase');546      }547    });548  });549550  it('Changes properties of a token according to permissions', async () => {551    await usingApi(async api => {552      const propertyKeys: string[] = [];553      let i = 0;554      for (const permission of permissions) {555        if (!permission.permission.mutable) continue;556        557        for (const signer of permission.signers) {558          const key = i + '_' + signer.address;559          propertyKeys.push(key);560561          await expect(executeTransaction(562            api, 563            alice, 564            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 565          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;566567          await expect(executeTransaction(568            api, 569            signer, 570            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 571          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;572573          await expect(executeTransaction(574            api, 575            signer, 576            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin stable'}]), 577          ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected;578        }579580        i++;581      }582583      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toHuman() as any[];584      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toHuman().properties as any[];585      for (let i = 0; i < properties.length; i++) {586        expect(properties[i].value).to.be.equal('Serotonin stable');587        expect(tokensData[i].value).to.be.equal('Serotonin stable');588      }589    });590  });591592  it('Deletes properties of a token according to permissions', async () => {593    await usingApi(async api => {594      const propertyKeys: string[] = [];595      let i = 0;596597      for (const permission of permissions) {598        if (!permission.permission.mutable) continue;599        600        for (const signer of permission.signers) {601          const key = i + '_' + signer.address;602          propertyKeys.push(key);603604          await expect(executeTransaction(605            api, 606            alice, 607            api.tx.unique.setPropertyPermissions(collection, [{key: key, permission: permission.permission}]), 608          ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;609610          await expect(executeTransaction(611            api, 612            signer, 613            api.tx.unique.setTokenProperties(collection, token, [{key: key, value: 'Serotonin increase'}]), 614          ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;615616          await expect(executeTransaction(617            api, 618            signer, 619            api.tx.unique.deleteTokenProperties(collection, token, [key]), 620          ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected;621        }622        623        i++;624      }625626      const properties = (await api.rpc.unique.tokenProperties(collection, token, propertyKeys)).toJSON() as any[];627      expect(properties).to.be.empty;628      const tokensData = (await api.rpc.unique.tokenData(collection, token, propertyKeys)).toJSON().properties as any[];629      expect(tokensData).to.be.empty;630      expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0);631    });632  });633});634635describe('Negative Integration Test: Token Properties', () => {636  let collection: number;637  let token: number;638  let originalSpace: number;639  let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];640641  before(async () => {642    await usingApi(async () => {643      alice = privateKey('//Alice');644      bob = privateKey('//Bob');645      charlie = privateKey('//Charlie');646      const dave = privateKey('//Dave');647648      constitution = [649        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},650        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},651        {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},652        {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},653        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},654        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},655      ];656    });657  });658659  beforeEach(async () => {660    collection = await createCollectionExpectSuccess();661    token = await createItemExpectSuccess(alice, collection, 'NFT');662    await addCollectionAdminExpectSuccess(alice, collection, bob.address);663    await transferExpectSuccess(collection, token, alice, charlie);664        665    await usingApi(async api => {666      let i = 0;667      for (const passage of constitution) {668        const signer = passage.signers[0];669        670        await expect(executeTransaction(671          api, 672          alice, 673          api.tx.unique.setPropertyPermissions(collection, [{key: `${i}`, permission: passage.permission}]), 674        ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected;675676        await expect(executeTransaction(677          api, 678          signer, 679          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin increase'}]), 680        ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected;681682        i++;683      }684685      originalSpace = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace as number;686    });687  });688689  it('Forbids changing/deleting properties of a token if the user is outside of permissions', async () => {690    await usingApi(async api => {691      let i = -1;692      for (const forbiddance of constitution) {693        i++;694        if (!forbiddance.permission.mutable) continue;695696        await expect(executeTransaction(697          api, 698          forbiddance.sinner, 699          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 700        ), `on failing to change property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);701702        await expect(executeTransaction(703          api, 704          forbiddance.sinner, 705          api.tx.unique.deleteTokenProperties(collection, token, [`${i}`]), 706        ), `on failing to delete property ${i} by ${forbiddance.sinner.address}`).to.be.rejectedWith(/common\.NoPermission/);707      }708709      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();710      expect(properties.consumedSpace).to.be.equal(originalSpace);711    });712  });713714  it('Forbids changing/deleting properties of a token if the property is permanent (immutable)', async () => {715    await usingApi(async api => {716      let i = -1;717      for (const permission of constitution) {718        i++;719        if (permission.permission.mutable) continue;720721        await expect(executeTransaction(722          api, 723          permission.signers[0], 724          api.tx.unique.setTokenProperties(collection, token, [{key: `${i}`, value: 'Serotonin down'}]), 725        ), `on failing to change property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);726727        await expect(executeTransaction(728          api, 729          permission.signers[0], 730          api.tx.unique.deleteTokenProperties(collection, token, [i.toString()]), 731        ), `on failing to delete property ${i} by ${permission.signers[0].address}`).to.be.rejectedWith(/common\.NoPermission/);732      }733734      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();735      expect(properties.consumedSpace).to.be.equal(originalSpace);736    });737  });738739  it('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission', async () => {740    await usingApi(async api => {741      await expect(executeTransaction(742        api, 743        alice, 744        api.tx.unique.setTokenProperties(collection, token, [{key: 'non-existent', value: 'I exist!'}]), 745      ), 'on failing to add a previously non-existent property').to.be.rejectedWith(/common\.NoPermission/);746        747      await expect(executeTransaction(748        api, 749        alice, 750        api.tx.unique.setPropertyPermissions(collection, [{key: 'now-existent', permission: {}}]), 751      ), 'on setting a new non-permitted property').to.not.be.rejected;752753      await expect(executeTransaction(754        api, 755        alice, 756        api.tx.unique.setTokenProperties(collection, token, [{key: 'now-existent', value: 'I exist!'}]), 757      ), 'on failing to add a property forbidden by the \'None\' permission').to.be.rejectedWith(/common\.NoPermission/);758759      expect((await api.rpc.unique.tokenProperties(collection, token, ['non-existent', 'now-existent'])).toJSON()).to.be.empty;760      const properties = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();761      expect(properties.consumedSpace).to.be.equal(originalSpace);762    });763  });764765  it('Forbids adding too many properties to a token', async () => {766    await usingApi(async api => {767      await expect(executeTransaction(768        api, 769        alice, 770        api.tx.unique.setPropertyPermissions(collection, [771          {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}}, 772          {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},773        ]), 774      ), 'on setting a new non-permitted property').to.not.be.rejected;775776      // Mute the general tx parsing error777      {778        console.error = () => {};779        await expect(executeTransaction(780          api, 781          alice, 782          api.tx.unique.setCollectionProperties(collection, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]), 783        )).to.be.rejected;784      }785786      await expect(executeTransaction(787        api, 788        alice, 789        api.tx.unique.setTokenProperties(collection, token, [790          {key: 'a_holy_book', value: 'word '.repeat(3277)}, 791          {key: 'young_years', value: 'neverending'.repeat(1490)},792        ]), 793      )).to.be.rejectedWith(/common\.NoSpaceForProperty/);794795      expect((await api.rpc.unique.tokenProperties(collection, token, ['a_holy_book', 'young years'])).toJSON()).to.be.empty;796      const propertiesMap = (await api.query.nonfungible.tokenProperties(collection, token)).toJSON();797      expect(propertiesMap.consumedSpace).to.be.equal(originalSpace);798    });799  });800});