git.delta.rocks / unique-network / refs/commits / 2a4f1af36afb

difftreelog

feat(repair-item) change to force_repair_item + add force_repair_collection + tests

Fahrrader2022-12-16parent: #669456d.patch.diff
in: master

10 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1730,6 +1730,15 @@
 		);
 		Ok(new_permission)
 	}
+
+	/// Repair possibly broken properties of a collection.
+	pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {
+		CollectionProperties::<T>::mutate(collection_id, |properties| {
+			properties.recompute_consumed_space();
+		});
+
+		Ok(())
+	}
 }
 
 /// Indicates unsupported methods by returning [Error::UnsupportedOperation].
@@ -1819,7 +1828,7 @@
 	fn set_allowance_for_all() -> Weight;
 
 	/// The price of repairing an item.
-	fn repair_item() -> Weight;
+	fn force_repair_item() -> Weight;
 }
 
 /// Weight info extension trait for refungible pallet.
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -113,7 +113,7 @@
 		Weight::zero()
 	}
 
-	fn repair_item() -> Weight {
+	fn force_repair_item() -> Weight {
 		Weight::zero()
 	}
 }
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -127,7 +127,7 @@
 		<SelfWeightOf<T>>::set_allowance_for_all()
 	}
 
-	fn repair_item() -> Weight {
+	fn force_repair_item() -> Weight {
 		<SelfWeightOf<T>>::repair_item()
 	}
 }
@@ -540,7 +540,7 @@
 	fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {
 		with_weight(
 			<Pallet<T>>::repair_item(self, token),
-			<CommonWeights<T>>::repair_item(),
+			<CommonWeights<T>>::force_repair_item(),
 		)
 	}
 }
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -157,7 +157,7 @@
 		<SelfWeightOf<T>>::set_allowance_for_all()
 	}
 
-	fn repair_item() -> Weight {
+	fn force_repair_item() -> Weight {
 		<SelfWeightOf<T>>::repair_item()
 	}
 }
@@ -544,7 +544,7 @@
 	fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {
 		with_weight(
 			<Pallet<T>>::repair_item(self, token),
-			<CommonWeights<T>>::repair_item(),
+			<CommonWeights<T>>::force_repair_item(),
 		)
 	}
 }
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -82,7 +82,7 @@
 	BoundedVec,
 };
 use scale_info::TypeInfo;
-use frame_system::{self as system, ensure_signed};
+use frame_system::{self as system, ensure_signed, ensure_root};
 use sp_std::{vec, vec::Vec};
 use up_data_structs::{
 	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
@@ -983,18 +983,33 @@
 			})
 		}
 
-		/// Repairs a broken item
+		/// Repairs a collection's properties if the data was somehow corrupted.
 		///
 		/// # Arguments
 		///
+		/// * `collection_id`: ID of the collection to repair.
+		#[weight = <SelfWeightOf<T>>::force_repair_collection()]
+		pub fn force_repair_collection(
+			origin,
+			collection_id: CollectionId,
+		) -> DispatchResult {
+			ensure_root(origin)?;
+			<PalletCommon<T>>::repair_collection(collection_id)
+		}
+
+		/// Repairs a token's properties if the data was somehow corrupted.
+		///
+		/// # Arguments
+		///
 		/// * `collection_id`: ID of the collection the item belongs to.
 		/// * `item_id`: ID of the item.
-		#[weight = T::CommonWeightInfo::repair_item()]
-		pub fn repair_item(
-			_origin,
+		#[weight = T::CommonWeightInfo::force_repair_item()]
+		pub fn force_repair_item(
+			origin,
 			collection_id: CollectionId,
 			item_id: TokenId,
 		) -> DispatchResultWithPostInfo {
+			ensure_root(origin)?;
 			dispatch_tx::<T, _>(collection_id, |d| {
 				d.repair_item(item_id)
 			})
modifiedpallets/unique/src/weights.rsdiffbeforeafterboth
--- a/pallets/unique/src/weights.rs
+++ b/pallets/unique/src/weights.rs
@@ -45,6 +45,7 @@
 	fn remove_collection_sponsor() -> Weight;
 	fn set_transfers_enabled_flag() -> Weight;
 	fn set_collection_limits() -> Weight;
+	fn force_repair_collection() -> Weight;
 }
 
 /// Weights for pallet_unique using the Substrate node and recommended hardware.
@@ -139,6 +140,12 @@
 			.saturating_add(T::DbWeight::get().reads(1 as u64))
 			.saturating_add(T::DbWeight::get().writes(1 as u64))
 	}
+	// Storage: Common CollectionProperties (r:1 w:1)
+	fn force_repair_collection() -> Weight {
+		Weight::from_ref_time(5_701_000 as u64)
+			.saturating_add(T::DbWeight::get().reads(1 as u64))
+			.saturating_add(T::DbWeight::get().writes(1 as u64))
+	}
 }
 
 // For backwards compatibility and tests
@@ -232,4 +239,10 @@
 			.saturating_add(RocksDbWeight::get().reads(1 as u64))
 			.saturating_add(RocksDbWeight::get().writes(1 as u64))
 	}
+	// Storage: Common CollectionProperties (r:1 w:1)
+	fn force_repair_collection() -> Weight {
+		Weight::from_ref_time(5_701_000 as u64)
+			.saturating_add(RocksDbWeight::get().reads(1 as u64))
+			.saturating_add(RocksDbWeight::get().writes(1 as u64))
+	}
 }
modifiedruntime/common/weights.rsdiffbeforeafterboth
--- a/runtime/common/weights.rs
+++ b/runtime/common/weights.rs
@@ -125,8 +125,8 @@
 		max_weight_of!(set_allowance_for_all())
 	}
 
-	fn repair_item() -> Weight {
-		max_weight_of!(repair_item())
+	fn force_repair_item() -> Weight {
+		max_weight_of!(force_repair_item())
 	}
 }
 
modifiedtests/package.jsondiffbeforeafterboth
before · tests/package.json
1{2  "name": "unique-tests",3  "version": "1.0.0",4  "description": "Unique Chain Tests",5  "main": "",6  "devDependencies": {7    "@polkadot/typegen": "9.9.4",8    "@types/chai": "^4.3.3",9    "@types/chai-as-promised": "^7.1.5",10    "@types/chai-like": "^1.1.1",11    "@types/mocha": "^10.0.0",12    "@types/node": "^18.11.2",13    "@typescript-eslint/eslint-plugin": "^5.40.1",14    "@typescript-eslint/parser": "^5.40.1",15    "chai": "^4.3.6",16    "eslint": "^8.25.0",17    "eslint-plugin-mocha": "^10.1.0",18    "mocha": "^10.1.0",19    "ts-node": "^10.9.1",20    "typescript": "^4.8.4"21  },22  "mocha": {23    "timeout": 9999999,24    "require": [25      "ts-node/register"26    ]27  },28  "scripts": {29    "lint": "eslint --ext .ts,.js src/",30    "fix": "eslint --ext .ts,.js src/ --fix",31    "setup": "ts-node ./src/util/globalSetup.ts",32    "test": "yarn setup && mocha --timeout 9999999 -r ts-node/register './src/**/*.*test.ts'",33    "testParallelFull": "yarn testParallel && yarn testSequential",34    "testParallel": "yarn setup && mocha --parallel --timeout 9999999 -r ts-node/register './src/**/*.test.ts'",35    "testSequential": "yarn setup && mocha --timeout 9999999 -r ts-node/register './src/**/*.seqtest.ts'",36    "testStructure": "yarn setup && mocha --timeout 9999999 -r ts-node/register ./**/nesting/*.*test.ts",37    "testEth": "yarn setup && mocha --timeout 9999999 -r ts-node/register './**/eth/**/*.*test.ts'",38    "testEthNesting": "yarn setup && mocha --timeout 9999999 -r ts-node/register './**/eth/nesting/**/*.*test.ts'",39    "testEthFractionalizer": "yarn setup && mocha --timeout 9999999 -r ts-node/register './**/eth/fractionalizer/**/*.*test.ts'",40    "testEthMarketplace": "yarn setup && mocha --timeout 9999999 -r ts-node/register './**/eth/marketplace/**/*.*test.ts'",41    "testEvent": "yarn setup && mocha --timeout 9999999 -r ts-node/register ./src/check-event/*.*test.ts",42    "testRmrk": "yarn setup && mocha --timeout 9999999 -r ts-node/register ./**/rmrk/*.*test.ts",43    "testEthPayable": "mocha --timeout 9999999 -r ts-node/register './**/eth/payable.test.ts'",44    "testEvmCoder": "mocha --timeout 9999999 -r ts-node/register './**/eth/evmCoder.test.ts'",45    "testNesting": "mocha --timeout 9999999 -r ts-node/register ./**/nest.test.ts",46    "testUnnesting": "mocha --timeout 9999999 -r ts-node/register ./**/unnest.test.ts",47    "testProperties": "mocha --timeout 9999999 -r ts-node/register ./**/collectionProperties.test.ts ./**/tokenProperties.test.ts ./**/getPropertiesRpc.test.ts",48    "testCollectionProperties": "mocha --timeout 9999999 -r ts-node/register ./**/collectionProperties.test.ts",49    "testMigration": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/migration-check.test.ts",50    "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",51    "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",52    "testChangeCollectionOwner": "mocha --timeout 9999999 -r ts-node/register ./**/change-collection-owner.test.ts",53    "testSetCollectionSponsor": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionSponsor.test.ts",54    "testConfirmSponsorship": "mocha --timeout 9999999 --parallel -r ts-node/register ./**/confirmSponsorship.test.ts",55    "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts",56    "testRemoveCollectionSponsor": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionSponsor.test.ts",57    "testAllowLists": "mocha --timeout 9999999 -r ts-node/register ./**/allowLists.test.ts",58    "testConnection": "mocha --timeout 9999999 -r ts-node/register ./**/connection.test.ts",59    "testContracts": "mocha --timeout 9999999 -r ts-node/register ./**/contracts.test.ts",60    "testCreateItem": "mocha --timeout 9999999 -r ts-node/register ./**/createItem.test.ts",61    "testCreateMultipleItems": "mocha --timeout 9999999 -r ts-node/register ./**/createMultipleItems.test.ts",62    "testCreateMultipleItemsEx": "mocha --timeout 9999999 -r ts-node/register ./**/createMultipleItemsEx.test.ts",63    "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts",64    "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts",65    "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",66    "testDestroyCollection": "mocha --timeout 9999999 -r ts-node/register ./**/destroyCollection.test.ts",67    "testToggleContractAllowList": "mocha --timeout 9999999 -r ts-node/register ./**/toggleContractAllowList.test.ts",68    "testAddToContractAllowList": "mocha --timeout 9999999 -r ts-node/register ./**/addToContractAllowList.test.ts",69    "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts",70    "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts",71    "testAdminTransferAndBurn": "mocha --timeout 9999999 -r ts-node/register ./**/adminTransferAndBurn.test.ts",72    "testSetPermissions": "mocha --timeout 9999999 -r ts-node/register ./**/setPermissions.test.ts",73    "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.seqtest.ts",74    "testContractSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/eth/contractSponsoring.test.ts",75    "testEnableContractSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/enableContractSponsoring.test.ts",76    "testRemoveFromContractAllowList": "mocha --timeout 9999999 -r ts-node/register ./**/removeFromContractAllowList.test.ts",77    "testSetContractSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setContractSponsoringRateLimit.test.ts",78    "testSetOffchainSchema": "mocha --timeout 9999999 -r ts-node/register ./**/setOffchainSchema.test.ts",79    "testNextSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/nextSponsoring.test.ts",80    "testOverflow": "mocha --timeout 9999999 -r ts-node/register ./**/overflow.test.ts",81    "testMaintenance": "mocha --timeout 9999999 -r ts-node/register ./**/maintenanceMode.seqtest.ts",82    "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.seqtest.ts",83    "testScheduler": "mocha --timeout 9999999 -r ts-node/register ./**/scheduler.seqtest.ts",84    "testSchedulingEVM": "mocha --timeout 9999999 -r ts-node/register ./**/eth/scheduling.test.ts",85    "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",86    "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",87    "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts",88    "testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts",89    "testEthCreateNFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createNFTCollection.test.ts",90    "testEthCreateRFTCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createRFTCollection.test.ts",91    "testEthNFT": "mocha --timeout 9999999 -r ts-node/register ./**/eth/nonFungible.test.ts",92    "testRFT": "mocha --timeout 9999999 -r ts-node/register ./**/refungible.test.ts",93    "testEthRFT": "mocha --timeout 9999999 -r ts-node/register ./**/eth/reFungible.test.ts ./**/eth/reFungibleToken.test.ts",94    "testFT": "mocha --timeout 9999999 -r ts-node/register ./**/fungible.test.ts",95    "testEthFT": "mocha --timeout 9999999 -r ts-node/register ./**/eth/fungible.test.ts",96    "testRPC": "mocha --timeout 9999999 -r ts-node/register ./**/rpc.test.ts",97    "testPromotion": "yarn setup && mocha --timeout 9999999 -r ts-node/register ./**/app-promotion.*test.ts",98    "testXcmUnique": "RUN_XCM_TESTS=1 mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmUnique.test.ts",99    "testXcmQuartz": "RUN_XCM_TESTS=1 mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmQuartz.test.ts",100    "testXcmOpal": "RUN_XCM_TESTS=1 mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmOpal.test.ts",101    "testXcmTransferAcala": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferAcala.test.ts acalaId=2000 uniqueId=5000",102    "testXcmTransferStatemine": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferStatemine.test.ts statemineId=1000 uniqueId=5000",103    "testXcmTransferMoonbeam": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferMoonbeam.test.ts",104    "benchMintingFee": "ts-node src/benchmarks/mintFee/benchmark.ts",105    "testApiConsts": "mocha --timeout 9999999 -r ts-node/register ./**/apiConsts.test.ts",106    "load": "mocha --timeout 9999999 -r ts-node/register './**/*.load.ts'",107    "loadTransfer": "ts-node src/transfer.nload.ts",108    "polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json",109    "polkadot-types-from-defs": "ts-node ./node_modules/.bin/polkadot-types-from-defs --endpoint src/interfaces/metadata.json --input src/interfaces/ --package .",110    "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .",111    "polkadot-types": "echo \"export default {}\" > src/interfaces/lookup.ts && yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain"112  },113  "author": "",114  "license": "SEE LICENSE IN ../LICENSE",115  "homepage": "",116  "dependencies": {117    "@polkadot/api": "9.9.4",118    "@polkadot/util-crypto": "10.2.1",119    "chai-as-promised": "^7.1.1",120    "chai-like": "^1.1.1",121    "csv-writer": "^1.6.0",122    "find-process": "^1.4.7",123    "solc": "0.8.17",124    "web3": "^1.8.0"125  }126}
modifiedtests/src/nesting/collectionProperties.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/collectionProperties.test.ts
+++ b/tests/src/nesting/collectionProperties.test.ts
@@ -18,11 +18,13 @@
 import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip} from '../util';
 
 describe('Integration Test: Collection Properties', () => {
+  let superuser: IKeyringPair;
   let alice: IKeyringPair;
   let bob: IKeyringPair;
   
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
+      superuser = await privateKey('//Alice');
       const donor = await privateKey({filename: __filename});
       [alice, bob] = await helper.arrange.createAccounts([200n, 10n], donor);
     });
@@ -199,6 +201,23 @@
       expectedConsumedSpaceDiff = biggerPropDataSize - smallerPropDataSize;
       expect(consumedSpace).to.be.equal(biggerPropDataSize - expectedConsumedSpaceDiff);
     });
+
+    itSub('Modifying a collection property with different sizes correctly changes the consumed space', async({helper}) => {
+      const properties = [
+        {key: 'sea-creatures', value: 'mermaids'},
+        {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'},
+      ];
+      const collection = await helper[testSuite.mode].mintCollection(alice, {properties});
+
+      const newProperty = ' '.repeat(4096);
+      await collection.setProperties(alice, [{key: 'space', value: newProperty}]);
+      const originalSpace = await collection.getPropertiesConsumedSpace();
+      expect(originalSpace).to.be.equal(properties[0].value.length + properties[1].value.length + newProperty.length);
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true);
+      const recomputedSpace = await collection.getPropertiesConsumedSpace();
+      expect(recomputedSpace).to.be.equal(originalSpace);
+    });
   }));
 });
   
@@ -314,6 +333,16 @@
         ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
       }
     });
+
+    itSub('Modifying a collection property with different sizes correctly changes the consumed space', async({helper}) => {
+      const collection = await helper[testSuite.mode].mintCollection(alice, {properties: [
+        {key: 'sea-creatures', value: 'mermaids'},
+        {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'},
+      ]});
+
+      await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true))
+        .to.be.rejectedWith(/BadOrigin/);
+    });
   }));
 });
   
\ No newline at end of file
modifiedtests/src/nesting/tokenProperties.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/tokenProperties.test.ts
+++ b/tests/src/nesting/tokenProperties.test.ts
@@ -19,6 +19,7 @@
 import {UniqueHelper, UniqueNFToken, UniqueRFToken} from '../util/playgrounds/unique';
 
 describe('Integration Test: Token Properties', () => {
+  let superuser: IKeyringPair;
   let alice: IKeyringPair; // collection owner
   let bob: IKeyringPair; // collection admin
   let charlie: IKeyringPair; // token owner
@@ -27,6 +28,7 @@
 
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
+      superuser = await privateKey('//Alice');
       const donor = await privateKey({filename: __filename});
       [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 100n, 100n], donor);
     });
@@ -406,7 +408,7 @@
     {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
     {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, 
   ].map(testCase =>
-    itSub.ifWithPallets(`repair_item preserves valid consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
+    itSub.ifWithPallets(`force_repair_item preserves valid consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
       const propKey = 'tok-prop';
 
       const collection = await helper[testCase.mode].mintCollection(alice, {
@@ -430,7 +432,7 @@
       const originalSpace = await token.getTokenPropertiesConsumedSpace();
       expect(originalSpace).to.be.equal(propDataSize);
 
-      await helper.executeExtrinsic(alice, 'api.tx.unique.repairItem', [token.collectionId, token.tokenId], true);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true);
       const recomputedSpace = await token.getTokenPropertiesConsumedSpace();
       expect(recomputedSpace).to.be.equal(originalSpace);
     }));
@@ -697,6 +699,35 @@
         permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
       }])).to.be.rejectedWith(/common\.PropertyLimitReached/);
     }));
+
+  [
+    {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
+    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]}, 
+  ].map(testCase =>
+    itSub.ifWithPallets(`Forbids force_repair_item from non-sudo (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
+      const propKey = 'tok-prop';
+
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPropertyPermissions: [
+          {
+            key: propKey,
+            permission: {mutable: true, tokenOwner: true},
+          },
+        ],
+      });
+      const token = await (
+        testCase.pieces
+          ? collection.mintToken(alice, testCase.pieces)
+          : collection.mintToken(alice)
+      );
+
+      const propDataSize = 4096;
+      const propData = 'a'.repeat(propDataSize);
+      await token.setProperties(alice, [{key: propKey, value: propData}]);
+
+      await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true))
+        .to.be.rejectedWith(/BadOrigin/);
+    }));
 });
 
 describe('ReFungible token properties permissions tests', () => {