git.delta.rocks / unique-network / refs/commits / 36ef0466a311

difftreelog

Add tests for properties

kryadinskii2022-05-10parent: #2976d69.patch.diff
in: master

6 files changed

modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -50,6 +50,7 @@
     "testContracts": "mocha --timeout 9999999 -r ts-node/register ./**/contracts.test.ts",
     "testCreateItem": "mocha --timeout 9999999 -r ts-node/register ./**/createItem.test.ts",
     "testCreateMultipleItems": "mocha --timeout 9999999 -r ts-node/register ./**/createMultipleItems.test.ts",
+    "testCreateMultipleItemsEx": "mocha --timeout 9999999 -r ts-node/register ./**/createMultipleItemsEx.test.ts",
     "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts",
     "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts",
     "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
modifiedtests/src/createCollection.test.tsdiffbeforeafterboth
--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -17,7 +17,7 @@
 import {expect} from 'chai';
 import privateKey from './substrate/privateKey';
 import usingApi, {executeTransaction, submitTransactionAsync} from './substrate/substrate-api';
-import {createCollectionExpectFailure, createCollectionExpectSuccess, getCreateCollectionResult, getDetailedCollectionInfo} from './util/helpers';
+import {createCollectionWithPropsExpectFailure, createCollectionExpectFailure, createCollectionExpectSuccess, getCreateCollectionResult, getDetailedCollectionInfo, createCollectionWithPropsExpectSuccess} from './util/helpers';
 
 describe('integration test: ext. createCollection():', () => {
   it('Create new NFT collection', async () => {
@@ -38,6 +38,25 @@
   it('Create new ReFungible collection', async () => {
     await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
   });
+
+  it('create new collection with properties #1', async () => {
+    await createCollectionWithPropsExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}, 
+      properties: [{key: 'key1', value: 'val1'}], 
+      propPerm:   [{key: 'key1', tokenOwner: true, mutable: false, collectionAdmin: true}]});
+  });
+
+  it('create new collection with properties #2', async () => {
+    await createCollectionWithPropsExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}, 
+      properties: [{key: 'key1', value: 'val1'}], 
+      propPerm:   [{key: 'key1', tokenOwner: false, mutable: true, collectionAdmin: false}]});
+  });
+
+  it('create new collection with properties #3', async () => {
+    await createCollectionWithPropsExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}, 
+      properties: [{key: 'key1', value: 'val1'}], 
+      propPerm:   [{key: 'key1', tokenOwner: true, mutable: false, collectionAdmin: false}]});
+  });
+
   it('Create new collection with extra fields', async () => {
     await usingApi(async api => {
       const alice = privateKey('//Alice');
@@ -96,4 +115,24 @@
       await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/^common.CollectionTokenLimitExceeded$/);
     });
   });
+
+  it('(!negative test!) create collection with incorrect property limit (64 elements)', async () => {
+    const props = [];
+
+    for (let i = 0; i < 65; i++) {
+      props.push({key: `key${i}`, value: `value${i}`});
+    }
+
+    await createCollectionWithPropsExpectFailure({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}, properties: props});
+  });
+
+  it('(!negative test!) create collection with incorrect property limit (40 kb)', async () => {
+    const props = [];
+
+    for (let i = 0; i < 32; i++) {
+      props.push({key: `key${i}`.repeat(80), value: `value${i}`.repeat(80)});
+    }
+
+    await createCollectionWithPropsExpectFailure({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}, properties: props});
+  });
 });
modifiedtests/src/createItem.test.tsdiffbeforeafterboth
--- a/tests/src/createItem.test.ts
+++ b/tests/src/createItem.test.ts
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-import {default as usingApi} from './substrate/substrate-api';
+import {default as usingApi, executeTransaction} from './substrate/substrate-api';
 import chai from 'chai';
 import {Keyring} from '@polkadot/api';
 import {IKeyringPair} from '@polkadot/types/types';
@@ -22,6 +22,7 @@
   createCollectionExpectSuccess,
   createItemExpectSuccess,
   addCollectionAdminExpectSuccess,
+  createCollectionWithPropsExpectSuccess,
 } from './util/helpers';
 
 const expect = chai.expect;
@@ -70,6 +71,33 @@
     await addCollectionAdminExpectSuccess(alice, newCollectionID, bob.address);
     await createItemExpectSuccess(bob, newCollectionID, createMode);
   });
+
+  it('Set property Admin', async () => {
+    const createMode = 'NFT';
+    const newCollectionID = await createCollectionWithPropsExpectSuccess({mode: {type: createMode}, 
+      properties: [{key: 'key1', value: 'val1'}], 
+      propPerm:   [{key: 'key1', mutable: true, collectionAdmin: true, tokenOwner: false}]});
+    
+    await createItemExpectSuccess(alice, newCollectionID, createMode);
+  });
+
+  it('Set property AdminConst', async () => {
+    const createMode = 'NFT';
+    const newCollectionID = await createCollectionWithPropsExpectSuccess({mode: {type: createMode}, 
+      properties: [{key: 'key1', value: 'val1'}], 
+      propPerm:   [{key: 'key1', mutable: false, collectionAdmin: true, tokenOwner: false}]});
+    
+    await createItemExpectSuccess(alice, newCollectionID, createMode);
+  });
+
+  it('Set property itemOwnerOrAdmin', async () => {
+    const createMode = 'NFT';
+    const newCollectionID = await createCollectionWithPropsExpectSuccess({mode: {type: createMode}, 
+      properties: [{key: 'key1', value: 'val1'}], 
+      propPerm:   [{key: 'key1', mutable: true, collectionAdmin: true, tokenOwner: true}]});
+    
+    await createItemExpectSuccess(alice, newCollectionID, createMode);
+  });
 });
 
 describe('Negative integration test: ext. createItem():', () => {
@@ -96,4 +124,76 @@
     const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
     await expect(createItemExpectSuccess(bob, newCollectionID, createMode)).to.be.rejected;
   });
+
+  it('No editing rights', async () => {
+    await usingApi(async api => {
+      const createMode = 'NFT';
+      const newCollectionID = await createCollectionWithPropsExpectSuccess({mode: {type: createMode}, 
+        propPerm:   [{key: 'key1', mutable: false, collectionAdmin: false, tokenOwner: false}]});
+
+      const token = await createItemExpectSuccess(alice, newCollectionID, 'NFT');
+      await addCollectionAdminExpectSuccess(alice, newCollectionID, bob.address);
+
+      await expect(executeTransaction(
+        api, 
+        alice, 
+        api.tx.unique.setTokenProperties(newCollectionID, token, [{key: 'key1', value: 'v2'}]), 
+      )).to.be.rejected;
+    });
+  });
+
+  it('User doesnt have editing rights', async () => {
+    await usingApi(async api => {
+      const createMode = 'NFT';
+      const newCollectionID = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'key1', mutable: true, collectionAdmin: false, tokenOwner: false}]});
+      const token = await createItemExpectSuccess(alice, newCollectionID, 'NFT');
+
+      await expect(executeTransaction(
+        api, 
+        bob, 
+        api.tx.unique.setTokenProperties(newCollectionID, token, [{key: 'key1', value: 'v2'}]), 
+      )).to.be.rejected;
+    });
+  });
+
+  it('Adding property without access rights', async () => {
+    await usingApi(async api => {
+      const createMode = 'NFT';
+      const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
+
+      const token = await createItemExpectSuccess(alice, newCollectionID, 'NFT');
+      await addCollectionAdminExpectSuccess(alice, newCollectionID, bob.address);
+      
+      await expect(executeTransaction(
+        api, 
+        bob, 
+        api.tx.unique.setTokenProperties(newCollectionID, token, [{key: 'key1', value: 'v2'}]), 
+      )).to.be.rejected;
+    });
+  });
+
+  it('Adding more than 64 prps', async () => {
+    await usingApi(async api => {
+      const createMode = 'NFT';
+
+      const prps = [];
+
+      for (let i = 0; i < 65; i++) {
+        prps.push({key: `key${i}`, value: `value${i}`});
+      }
+
+      const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
+      
+      await expect(executeTransaction(api, alice, api.tx.unique.setCollectionProperties(newCollectionID, prps))).to.be.rejectedWith(/common\.PropertyLimitReached/);
+    });
+  });
+
+  it('Trying to add bigger property than allowed', async () => {
+    await usingApi(async api => {
+      const createMode = 'NFT';
+      const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
+      
+      await expect(executeTransaction(api, alice, api.tx.unique.setCollectionProperties(newCollectionID, [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]))).to.be.rejectedWith(/common\.NoSpaceForProperty/);
+    });
+  });
 });
modifiedtests/src/createMultipleItems.test.tsdiffbeforeafterboth
19import chai from 'chai';19import chai from 'chai';
20import chaiAsPromised from 'chai-as-promised';20import chaiAsPromised from 'chai-as-promised';
21import privateKey from './substrate/privateKey';21import privateKey from './substrate/privateKey';
22import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';22import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync, executeTransaction} from './substrate/substrate-api';
23import {23import {
24 createCollectionExpectSuccess,24 createCollectionExpectSuccess,
25 destroyCollectionExpectSuccess,25 destroyCollectionExpectSuccess,
33 getVariableMetadata,33 getVariableMetadata,
34 getConstMetadata,34 getConstMetadata,
35 getCreatedCollectionCount,35 getCreatedCollectionCount,
36 createCollectionWithPropsExpectSuccess,
37 getCreateItemsResult,
36} from './util/helpers';38} from './util/helpers';
3739
38chai.use(chaiAsPromised);40chai.use(chaiAsPromised);
45 const itemsListIndexBefore = await getLastTokenId(api, collectionId);47 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
46 expect(itemsListIndexBefore).to.be.equal(0);48 expect(itemsListIndexBefore).to.be.equal(0);
47 const alice = privateKey('//Alice');49 const alice = privateKey('//Alice');
48 const args = [{NFT: ['0x31', '0x31']}, {NFT: ['0x32', '0x32']}, {NFT: ['0x33', '0x33']}];50 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
51 {Nft: {const_data: '0x32', variable_data: '0x32'}},
52 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
49 const createMultipleItemsTx = api.tx.unique53 const createMultipleItemsTx = api.tx.unique
50 .createMultipleItems(collectionId, normalizeAccountId(alice.address), args);54 .createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
51 await submitTransactionAsync(alice, createMultipleItemsTx);55 await submitTransactionAsync(alice, createMultipleItemsTx);
136 });140 });
137 });141 });
142
143 it('Create 0x31, 0x32, 0x33 items in active NFT with property Admin', async () => {
144 await usingApi(async (api: ApiPromise) => {
145 const collectionId = await createCollectionExpectSuccess();
146 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
147 expect(itemsListIndexBefore).to.be.equal(0);
148 const alice = privateKey('//Alice');
149 const bob = privateKey('//Bob');
150 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
151 {Nft: {const_data: '0x32', variable_data: '0x32'}},
152 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
153 const createMultipleItemsTx = api.tx.unique
154 .createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
155 await submitTransactionAsync(alice, createMultipleItemsTx);
156 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
157 expect(itemsListIndexAfter).to.be.equal(3);
158
159 await expect(executeTransaction(
160 api,
161 alice,
162 api.tx.unique.setPropertyPermissions(collectionId, [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: false}}]),
163 )).to.not.be.rejected;
164
165 expect(await getTokenOwner(api, collectionId, 1)).to.be.deep.equal(normalizeAccountId(alice.address));
166 expect(await getTokenOwner(api, collectionId, 2)).to.be.deep.equal(normalizeAccountId(alice.address));
167 expect(await getTokenOwner(api, collectionId, 3)).to.be.deep.equal(normalizeAccountId(alice.address));
168
169 expect(await getConstMetadata(api, collectionId, 1)).to.be.deep.equal([0x31]);
170 expect(await getConstMetadata(api, collectionId, 2)).to.be.deep.equal([0x32]);
171 expect(await getConstMetadata(api, collectionId, 3)).to.be.deep.equal([0x33]);
172
173 expect(await getVariableMetadata(api, collectionId, 1)).to.be.deep.equal([0x31]);
174 expect(await getVariableMetadata(api, collectionId, 2)).to.be.deep.equal([0x32]);
175 expect(await getVariableMetadata(api, collectionId, 3)).to.be.deep.equal([0x33]);
176 });
177 });
178
179 it('Create 0x31, 0x32, 0x33 items in active NFT with property AdminConst', async () => {
180 await usingApi(async (api: ApiPromise) => {
181 const collectionId = await createCollectionExpectSuccess();
182 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
183 expect(itemsListIndexBefore).to.be.equal(0);
184 const alice = privateKey('//Alice');
185 const bob = privateKey('//Bob');
186 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
187 {Nft: {const_data: '0x32', variable_data: '0x32'}},
188 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
189 const createMultipleItemsTx = api.tx.unique
190 .createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
191 await submitTransactionAsync(alice, createMultipleItemsTx);
192 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
193 expect(itemsListIndexAfter).to.be.equal(3);
194
195 await expect(executeTransaction(
196 api,
197 alice,
198 api.tx.unique.setPropertyPermissions(collectionId, [{key: 'k', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}]),
199 )).to.not.be.rejected;
200
201 expect(await getTokenOwner(api, collectionId, 1)).to.be.deep.equal(normalizeAccountId(alice.address));
202 expect(await getTokenOwner(api, collectionId, 2)).to.be.deep.equal(normalizeAccountId(alice.address));
203 expect(await getTokenOwner(api, collectionId, 3)).to.be.deep.equal(normalizeAccountId(alice.address));
204
205 expect(await getConstMetadata(api, collectionId, 1)).to.be.deep.equal([0x31]);
206 expect(await getConstMetadata(api, collectionId, 2)).to.be.deep.equal([0x32]);
207 expect(await getConstMetadata(api, collectionId, 3)).to.be.deep.equal([0x33]);
208
209 expect(await getVariableMetadata(api, collectionId, 1)).to.be.deep.equal([0x31]);
210 expect(await getVariableMetadata(api, collectionId, 2)).to.be.deep.equal([0x32]);
211 expect(await getVariableMetadata(api, collectionId, 3)).to.be.deep.equal([0x33]);
212 });
213 });
214
215 it('Create 0x31, 0x32, 0x33 items in active NFT with property itemOwnerOrAdmin', async () => {
216 await usingApi(async (api: ApiPromise) => {
217 const collectionId = await createCollectionExpectSuccess();
218 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
219 expect(itemsListIndexBefore).to.be.equal(0);
220 const alice = privateKey('//Alice');
221 const bob = privateKey('//Bob');
222 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
223 {Nft: {const_data: '0x32', variable_data: '0x32'}},
224 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
225 const createMultipleItemsTx = api.tx.unique
226 .createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
227 await submitTransactionAsync(alice, createMultipleItemsTx);
228 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
229 expect(itemsListIndexAfter).to.be.equal(3);
230
231 await expect(executeTransaction(
232 api,
233 alice,
234 api.tx.unique.setPropertyPermissions(collectionId, [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}]),
235 )).to.not.be.rejected;
236
237 expect(await getTokenOwner(api, collectionId, 1)).to.be.deep.equal(normalizeAccountId(alice.address));
238 expect(await getTokenOwner(api, collectionId, 2)).to.be.deep.equal(normalizeAccountId(alice.address));
239 expect(await getTokenOwner(api, collectionId, 3)).to.be.deep.equal(normalizeAccountId(alice.address));
240
241 expect(await getConstMetadata(api, collectionId, 1)).to.be.deep.equal([0x31]);
242 expect(await getConstMetadata(api, collectionId, 2)).to.be.deep.equal([0x32]);
243 expect(await getConstMetadata(api, collectionId, 3)).to.be.deep.equal([0x33]);
244
245 expect(await getVariableMetadata(api, collectionId, 1)).to.be.deep.equal([0x31]);
246 expect(await getVariableMetadata(api, collectionId, 2)).to.be.deep.equal([0x32]);
247 expect(await getVariableMetadata(api, collectionId, 3)).to.be.deep.equal([0x33]);
248 });
249 });
138});250});
139251
140describe('Integration Test createMultipleItems(collection_id, owner, items_data) with collection admin permissions:', () => {252describe('Integration Test createMultipleItems(collection_id, owner, items_data) with collection admin permissions:', () => {
155 const itemsListIndexBefore = await getLastTokenId(api, collectionId);267 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
156 expect(itemsListIndexBefore).to.be.equal(0);268 expect(itemsListIndexBefore).to.be.equal(0);
157 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);269 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
158 const args = [{NFT: ['0x31', '0x31']}, {NFT: ['0x32', '0x32']}, {NFT: ['0x33', '0x33']}];270 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
271 {Nft: {const_data: '0x32', variable_data: '0x32'}},
272 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
159 const createMultipleItemsTx = api.tx.unique273 const createMultipleItemsTx = api.tx.unique
160 .createMultipleItems(collectionId, normalizeAccountId(bob.address), args);274 .createMultipleItems(collectionId, normalizeAccountId(bob.address), args);
161 await submitTransactionAsync(bob, createMultipleItemsTx);275 await submitTransactionAsync(bob, createMultipleItemsTx);
245 const collectionId = await createCollectionExpectSuccess();359 const collectionId = await createCollectionExpectSuccess();
246 const itemsListIndexBefore = await getLastTokenId(api, collectionId);360 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
247 expect(itemsListIndexBefore).to.be.equal(0);361 expect(itemsListIndexBefore).to.be.equal(0);
248 const args = [{NFT: ['0x31', '0x31']}, {NFT: ['0x32', '0x32']}, {NFT: ['0x33', '0x33']}];362 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
363 {Nft: {const_data: '0x32', variable_data: '0x32'}},
364 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
249 const createMultipleItemsTx = api.tx.unique365 const createMultipleItemsTx = api.tx.unique
250 .createMultipleItems(collectionId, normalizeAccountId(alice.address), args);366 .createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
251 await expect(submitTransactionAsync(bob, createMultipleItemsTx)).to.be.rejected;367 await expect(submitTransactionAsync(bob, createMultipleItemsTx)).to.be.rejected;
362 });478 });
363 });479 });
480
481 it('No editing rights', async () => {
482 await usingApi(async (api: ApiPromise) => {
483 const collectionId = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],
484 propPerm: [{key: 'key1', mutable: true, collectionAdmin: false, tokenOwner: false}]});
485 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
486 expect(itemsListIndexBefore).to.be.equal(0);
487 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
488 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
489 {Nft: {const_data: '0x32', variable_data: '0x32'}},
490 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
491
492 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
493
494 const events = await submitTransactionAsync(alice, createMultipleItemsTx);
495 const result = getCreateItemsResult(events);
496
497 for (const elem of result) {
498 await expect(executeTransaction(
499 api,
500 bob,
501 api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, [{key: 'key1', value: 'v2'}]),
502 )).to.be.rejected;
503 }
504
505 // await expect(submitTransactionAsync(bob, createMultipleItemsTx)).to.be.rejected;
506 });
507 });
508
509 it('User doesnt have editing rights', async () => {
510 await usingApi(async (api: ApiPromise) => {
511 const collectionId = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],
512 propPerm: [{key: 'key1', mutable: false, collectionAdmin: false, tokenOwner: false}]});
513 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
514 expect(itemsListIndexBefore).to.be.equal(0);
515 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
516 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
517 {Nft: {const_data: '0x32', variable_data: '0x32'}},
518 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
519
520 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
521
522 const events = await submitTransactionAsync(alice, createMultipleItemsTx);
523 const result = getCreateItemsResult(events);
524
525 for (const elem of result) {
526 await expect(executeTransaction(
527 api,
528 bob,
529 api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, [{key: 'key1', value: 'v2'}]),
530 )).to.be.rejected;
531 }
532 });
533 });
534
535 it('Adding property without access rights', async () => {
536 await usingApi(async (api: ApiPromise) => {
537 const collectionId = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}]});
538 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
539 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
540 expect(itemsListIndexBefore).to.be.equal(0);
541 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
542 {Nft: {const_data: '0x32', variable_data: '0x32'}},
543 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
544
545 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
546
547 const events = await submitTransactionAsync(alice, createMultipleItemsTx);
548 const result = getCreateItemsResult(events);
549
550 for (const elem of result) {
551 await expect(executeTransaction(
552 api,
553 bob,
554 api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, [{key: 'key1', value: 'v2'}]),
555 )).to.be.rejected;
556 }
557 });
558 });
559
560 it('Adding more than 64 prps', async () => {
561 await usingApi(async (api: ApiPromise) => {
562 const collectionId = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],
563 propPerm: [{key: 'key1', mutable: true, collectionAdmin: true, tokenOwner: false}]});
564 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
565 expect(itemsListIndexBefore).to.be.equal(0);
566 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
567
568 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
569 {Nft: {const_data: '0x32', variable_data: '0x32'}},
570 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
571
572 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
573 const events = await submitTransactionAsync(alice, createMultipleItemsTx);
574
575 const result = getCreateItemsResult(events);
576
577 const prps = [];
578
579 for (let i = 0; i < 65; i++) {
580 prps.push({key: `key${i}`, value: `value${i}`});
581 }
582
583 await expect(executeTransaction(api, bob, api.tx.unique.setCollectionProperties(collectionId, prps))).to.be.rejectedWith(/common\.PropertyLimitReached/);
584
585 for (const elem of result) {
586 await expect(executeTransaction(
587 api,
588 bob,
589 api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, prps),
590 )).to.be.rejected;
591 }
592 });
593 });
594
595 it('Trying to add bigger property than allowed', async () => {
596 await usingApi(async (api: ApiPromise) => {
597 const collectionId = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],
598 propPerm: [{key: 'key1', mutable: true, collectionAdmin: false, tokenOwner: false}]});
599 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
600 expect(itemsListIndexBefore).to.be.equal(0);
601 const args = [{Nft: {const_data: '0x31', variable_data: '0x31'}},
602 {Nft: {const_data: '0x32', variable_data: '0x32'}},
603 {Nft: {const_data: '0x33', variable_data: '0x33'}}];
604
605 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
606
607 const events = await submitTransactionAsync(alice, createMultipleItemsTx);
608 const result = getCreateItemsResult(events);
609
610
611 await expect(executeTransaction(api, alice, api.tx.unique.setCollectionProperties(collectionId, [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]))).to.be.rejectedWith(/common\.NoSpaceForProperty/);
612
613 for (const elem of result) {
614 await expect(executeTransaction(
615 api,
616 bob,
617 api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]),
618 )).to.be.rejected;
619 }
620 });
621 });
364});622});
365623
modifiedtests/src/createMultipleItemsEx.test.tsdiffbeforeafterboth
--- a/tests/src/createMultipleItemsEx.test.ts
+++ b/tests/src/createMultipleItemsEx.test.ts
@@ -16,8 +16,8 @@
 
 import {expect} from 'chai';
 import privateKey from './substrate/privateKey';
-import usingApi, {executeTransaction} from './substrate/substrate-api';
-import {createCollectionExpectSuccess} from './util/helpers';
+import usingApi, {executeTransaction, submitTransactionAsync} from './substrate/substrate-api';
+import {createCollectionExpectSuccess, createCollectionWithPropsExpectSuccess, addCollectionAdminExpectSuccess, getCreateItemsResult} from './util/helpers';
 
 describe('createMultipleItemsEx', () => {
   it('can initialize multiple NFT with different owners', async () => {
@@ -51,6 +51,362 @@
     });
   });
 
+  it('createMultipleItemsEx with property Admin', async () => {
+    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+          constData: '0x0000',
+          variableData: '0x1111',
+        }, {
+          owner: {substrate: bob.address},
+          constData: '0x2222',
+          variableData: '0x3333',
+        }, {
+          owner: {substrate: charlie.address},
+          constData: '0x4444',
+          variableData: '0x5555',
+        },
+      ];
+
+      await expect(executeTransaction(
+        api, 
+        alice, 
+        api.tx.unique.setPropertyPermissions(collection, [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: false}}]), 
+      )).to.not.be.rejected;
+
+      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {
+        NFT: data,
+      }));
+      const tokens = await api.query.nonfungible.tokenData.entries(collection);
+      const json = tokens.map(([, token]) => token.toJSON());
+      expect(json).to.be.deep.equal(data);
+    });
+  });
+
+  it('createMultipleItemsEx with property AdminConst', async () => {
+    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+          constData: '0x0000',
+          variableData: '0x1111',
+        }, {
+          owner: {substrate: bob.address},
+          constData: '0x2222',
+          variableData: '0x3333',
+        }, {
+          owner: {substrate: charlie.address},
+          constData: '0x4444',
+          variableData: '0x5555',
+        },
+      ];
+      await expect(executeTransaction(
+        api, 
+        alice, 
+        api.tx.unique.setPropertyPermissions(collection, [{key: 'k', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}]), 
+      )).to.not.be.rejected;
+
+      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {
+        NFT: data,
+      }));
+      
+
+      const tokens = await api.query.nonfungible.tokenData.entries(collection);
+      const json = tokens.map(([, token]) => token.toJSON());
+      expect(json).to.be.deep.equal(data);
+    });
+  });
+
+  it('createMultipleItemsEx with property itemOwnerOrAdmin', async () => {
+    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+          constData: '0x0000',
+          variableData: '0x1111',
+        }, {
+          owner: {substrate: bob.address},
+          constData: '0x2222',
+          variableData: '0x3333',
+        }, {
+          owner: {substrate: charlie.address},
+          constData: '0x4444',
+          variableData: '0x5555',
+        },
+      ];
+      await expect(executeTransaction(
+        api, 
+        alice, 
+        api.tx.unique.setPropertyPermissions(collection, [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}]), 
+      )).to.not.be.rejected;
+
+      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {
+        NFT: data,
+      }));
+      
+
+      const tokens = await api.query.nonfungible.tokenData.entries(collection);
+      const json = tokens.map(([, token]) => token.toJSON());
+      expect(json).to.be.deep.equal(data);
+    });
+  });
+
+  it('No editing rights', async () => {
+    const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],
+      propPerm:   [{key: 'key1', mutable: true, collectionAdmin: false, tokenOwner: false}]});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await addCollectionAdminExpectSuccess(alice, collection, bob.address);
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+        }, {
+          owner: {substrate: bob.address},
+        }, {
+          owner: {substrate: charlie.address},
+        },
+      ];
+
+      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});
+      await executeTransaction(api, alice, tx);
+      
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getCreateItemsResult(events);
+      
+      for (const elem of result) {
+        await expect(executeTransaction(
+          api, 
+          bob, 
+          api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, [{key: 'key1', value: 'v2'}]), 
+        )).to.be.rejected;
+      }
+    });
+  });
+
+  it('User doesnt have editing rights', async () => {
+    const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],
+      propPerm:   [{key: 'key1', mutable: false, collectionAdmin: false, tokenOwner: false}]});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await addCollectionAdminExpectSuccess(alice, collection, bob.address);
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+        }, {
+          owner: {substrate: bob.address},
+        }, {
+          owner: {substrate: charlie.address},
+        },
+      ];
+
+      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});
+      await executeTransaction(api, alice, tx);
+      
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getCreateItemsResult(events);
+      
+      for (const elem of result) {
+        await expect(executeTransaction(
+          api, 
+          bob, 
+          api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, [{key: 'key1', value: 'v2'}]), 
+        )).to.be.rejected;
+      }
+    });
+  });
+
+  it('Adding property without access rights', async () => {
+    const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}]});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await addCollectionAdminExpectSuccess(alice, collection, bob.address);
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+        }, {
+          owner: {substrate: bob.address},
+        }, {
+          owner: {substrate: charlie.address},
+        },
+      ];
+
+      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});
+      await executeTransaction(api, alice, tx);
+      
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getCreateItemsResult(events);
+      
+      for (const elem of result) {
+        await expect(executeTransaction(
+          api, 
+          bob, 
+          api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, [{key: 'key1', value: 'v2'}]), 
+        )).to.be.rejected;
+      }
+    });
+  });
+
+  it('Adding more than 64 prps', async () => {
+    const collection = await createCollectionWithPropsExpectSuccess();
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await addCollectionAdminExpectSuccess(alice, collection, bob.address);
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+        }, {
+          owner: {substrate: bob.address},
+        }, {
+          owner: {substrate: charlie.address},
+        },
+      ];
+
+      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});
+      await executeTransaction(api, alice, tx);
+      
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getCreateItemsResult(events);
+
+      const prps = [];
+      
+      for (let i = 0; i < 65; i++) {
+        prps.push({key: `key${i}`, value: `value${i}`});
+      }
+
+      await expect(executeTransaction(api, bob, api.tx.unique.setCollectionProperties(collection, prps))).to.be.rejectedWith(/common\.PropertyLimitReached/);
+
+      
+      for (const elem of result) {
+        await expect(executeTransaction(
+          api, 
+          bob, 
+          api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, prps), 
+        )).to.be.rejected;
+      }
+    });
+  });
+
+  it('Trying to add bigger property than allowed', async () => {
+    const collection = await createCollectionWithPropsExpectSuccess();
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await addCollectionAdminExpectSuccess(alice, collection, bob.address);
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+        }, {
+          owner: {substrate: bob.address},
+        }, {
+          owner: {substrate: charlie.address},
+        },
+      ];
+
+      const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});
+      await executeTransaction(api, alice, tx);
+      
+      const events = await submitTransactionAsync(alice, tx);
+      const result = getCreateItemsResult(events);
+
+      const prps = [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}];
+
+      await expect(executeTransaction(api, bob, api.tx.unique.setCollectionProperties(collection, prps))).to.be.rejectedWith(/common\.NoSpaceForProperty/);
+
+      
+      for (const elem of result) {
+        await expect(executeTransaction(
+          api, 
+          bob, 
+          api.tx.unique.setTokenProperties(elem.collectionId, elem.itemId, prps), 
+        )).to.be.rejected;
+      }
+    });
+  });
+
+  it('can initialize multiple NFT with different owners', async () => {
+    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+          constData: '0x0000',
+          variableData: '0x1111',
+        }, {
+          owner: {substrate: bob.address},
+          constData: '0x2222',
+          variableData: '0x3333',
+        }, {
+          owner: {substrate: charlie.address},
+          constData: '0x4444',
+          variableData: '0x5555',
+        },
+      ];
+
+      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {
+        NFT: data,
+      }));
+      const tokens = await api.query.nonfungible.tokenData.entries(collection);
+      const json = tokens.map(([, token]) => token.toJSON());
+      expect(json).to.be.deep.equal(data);
+    });
+  });
+
+  it('can initialize multiple NFT with different owners', async () => {
+    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+          constData: '0x0000',
+          variableData: '0x1111',
+        }, {
+          owner: {substrate: bob.address},
+          constData: '0x2222',
+          variableData: '0x3333',
+        }, {
+          owner: {substrate: charlie.address},
+          constData: '0x4444',
+          variableData: '0x5555',
+        },
+      ];
+
+      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {
+        NFT: data,
+      }));
+      const tokens = await api.query.nonfungible.tokenData.entries(collection);
+      const json = tokens.map(([, token]) => token.toJSON());
+      expect(json).to.be.deep.equal(data);
+    });
+  });
+
   it('fails when trying to set multiple owners when creating multiple refungibles', async () => {
     const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
     const alice = privateKey('//Alice');
@@ -72,3 +428,4 @@
     });
   });
 });
+'';
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -201,6 +201,37 @@
   return result;
 }
 
+export function getCreateItemsResult(events: EventRecord[]): CreateItemResult[] {
+  let success = false;
+  let collectionId = 0;
+  let itemId = 0;
+  let recipient;
+
+  const results : CreateItemResult[]  = [];
+
+  events.forEach(({event: {data, method, section}}) => {
+    // console.log(`    ${phase}: ${section}.${method}:: ${data}`);
+    if (method == 'ExtrinsicSuccess') {
+      success = true;
+    } else if ((section == 'common') && (method == 'ItemCreated')) {
+      collectionId = parseInt(data[0].toString(), 10);
+      itemId = parseInt(data[1].toString(), 10);
+      recipient = normalizeAccountId(data[2].toJSON() as any);
+
+      const itemRes: CreateItemResult = {
+        success,
+        collectionId,
+        itemId,
+        recipient,
+      };
+
+      results.push(itemRes);
+    }
+  });
+
+  return results;
+}
+
 export function getCreateItemResult(events: EventRecord[]): CreateItemResult {
   let success = false;
   let collectionId = 0;
@@ -263,12 +294,26 @@
 
 type CollectionMode = Nft | Fungible | ReFungible;
 
+export type Property = {
+  key: any,
+  value: any,
+};
+
+type PropertyPermission = {
+  key: any,
+  mutable: boolean;
+  collectionAdmin: boolean;
+  tokenOwner: boolean;
+}
+
 export type CreateCollectionParams = {
   mode: CollectionMode,
   name: string,
   description: string,
   tokenPrefix: string,
   schemaVersion: string,
+  properties?: Array<Property>,
+  propPerm?: Array<PropertyPermission>
 };
 
 const defaultCreateCollectionParams: CreateCollectionParams = {
@@ -333,6 +378,86 @@
   return collectionId;
 }
 
+export async function createCollectionWithPropsExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {
+  const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};
+
+  let collectionId = 0;
+  await usingApi(async (api) => {
+    // Get number of collections before the transaction
+    const collectionCountBefore = await getCreatedCollectionCount(api);
+
+    // Run the CreateCollection transaction
+    const alicePrivateKey = privateKey('//Alice');
+
+    let modeprm = {};
+    if (mode.type === 'NFT') {
+      modeprm = {nft: null};
+    } else if (mode.type === 'Fungible') {
+      modeprm = {fungible: mode.decimalPoints};
+    } else if (mode.type === 'ReFungible') {
+      modeprm = {refungible: null};
+    }
+
+    const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any, properties: params.properties, tokenPropertyPermissions: params.propPerm});
+    const events = await submitTransactionAsync(alicePrivateKey, tx);
+    const result = getCreateCollectionResult(events);
+
+    // Get number of collections after the transaction
+    const collectionCountAfter = await getCreatedCollectionCount(api);
+
+    // Get the collection
+    const collection = await queryCollectionExpectSuccess(api, result.collectionId);
+
+    // What to expect
+    // tslint:disable-next-line:no-unused-expression
+    expect(result.success).to.be.true;
+    expect(result.collectionId).to.be.equal(collectionCountAfter);
+    // tslint:disable-next-line:no-unused-expression
+    expect(collection).to.be.not.null;
+    expect(collectionCountAfter).to.be.equal(collectionCountBefore + 1, 'Error: NFT collection NOT created.');
+    expect(collection.owner.toString()).to.be.equal(toSubstrateAddress(alicesPublicKey));
+    expect(utf16ToStr(collection.name.toJSON() as any)).to.be.equal(name);
+    expect(utf16ToStr(collection.description.toJSON() as any)).to.be.equal(description);
+    expect(hexToStr(collection.tokenPrefix.toJSON())).to.be.equal(tokenPrefix);
+
+
+    collectionId = result.collectionId;
+  });
+
+  return collectionId;
+}
+
+export async function createCollectionWithPropsExpectFailure(params: Partial<CreateCollectionParams> = {}) {
+  const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};
+
+  const collectionId = 0;
+  await usingApi(async (api) => {
+    // Get number of collections before the transaction
+    const collectionCountBefore = await getCreatedCollectionCount(api);
+
+    // Run the CreateCollection transaction
+    const alicePrivateKey = privateKey('//Alice');
+
+    let modeprm = {};
+    if (mode.type === 'NFT') {
+      modeprm = {nft: null};
+    } else if (mode.type === 'Fungible') {
+      modeprm = {fungible: mode.decimalPoints};
+    } else if (mode.type === 'ReFungible') {
+      modeprm = {refungible: null};
+    }
+
+    const tx = api.tx.unique.createCollectionEx({name: strToUTF16(name), description: strToUTF16(description), tokenPrefix: strToUTF16(tokenPrefix), mode: modeprm as any, properties: params.properties, tokenPropertyPermissions: params.propPerm});
+    await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
+
+
+    // Get number of collections after the transaction
+    const collectionCountAfter = await getCreatedCollectionCount(api);
+
+    expect(collectionCountAfter).to.be.equal(collectionCountBefore, 'Error: Collection with incorrect data created.');
+  });
+}
+
 export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {
   const {name, description, mode, tokenPrefix} = {...defaultCreateCollectionParams, ...params};