From 56485eba640c52144ac427b393b1d561db5842fa Mon Sep 17 00:00:00 2001 From: Jongwon Park Date: Wed, 6 Dec 2023 16:18:24 -0800 Subject: [PATCH] Updated tests for e2e and modules --- test/foundry/e2e/e2e.t.sol | 453 +++++++--------- test/foundry/interfaces/IE2ETest.sol | 81 ++- test/foundry/modules/ModuleRegistry.t.sol | 35 ++ .../licensing/LicensingFrameworkRepo.t.sol | 3 +- .../licensing/LicensingModule.Licensing.sol | 512 ++++++++++++++---- test/foundry/utils/BaseTest.sol | 1 - 6 files changed, 716 insertions(+), 369 deletions(-) diff --git a/test/foundry/e2e/e2e.t.sol b/test/foundry/e2e/e2e.t.sol index f136f170..b1081358 100644 --- a/test/foundry/e2e/e2e.t.sol +++ b/test/foundry/e2e/e2e.t.sol @@ -1,4 +1,4 @@ -/* solhint-disable contract-name-camelcase, func-name-mixedcase */ +/* solhint-disable contract-name-camelcase, func-name-mixedcase, var-name-mixedcase */ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; @@ -14,60 +14,80 @@ import { HookResult, IHook } from "contracts/interfaces/hooks/base/IHook.sol"; import { Hook } from "contracts/lib/hooks/Hook.sol"; import { TokenGated } from "contracts/lib/hooks/TokenGated.sol"; import { MockERC721 } from "test/foundry/mocks/MockERC721.sol"; -import { TermsRepository } from "contracts/modules/licensing/TermsRepository.sol"; import { Licensing } from "contracts/lib/modules/Licensing.sol"; -import { TermCategories, TermIds } from "contracts/lib/modules/ProtocolLicensingTerms.sol"; import { BaseTest } from "test/foundry/utils/BaseTest.sol"; import { LibRelationship } from "contracts/lib/modules/LibRelationship.sol"; import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol"; -import { TermsData } from "contracts/lib/modules/ProtocolLicensingTerms.sol"; import { Registration } from "contracts/lib/modules/Registration.sol"; import { IE2ETest } from "test/foundry/interfaces/IE2ETest.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; contract E2ETest is IE2ETest, BaseTest { - using ShortStrings for string; - -// address public tokenGatedHook; -// MockERC721 public mockNFT; - -// // create 3 roles: protocol admin, ip org owner, ip asset owner -// address public ipOrgOwner1 = address(1234); -// address public ipOrgOwner2 = address(4567); -// address public ipAssetOwner1 = address(6789); -// address public ipAssetOwner2 = address(9876); -// address public ipAssetOwner3 = address(9876); - -// address public ipOrg1; -// address public ipOrg2; - -// function setUp() public virtual override { -// super.setUp(); -// _grantRole(vm, AccessControl.RELATIONSHIP_MANAGER_ROLE, admin); -// _grantRole(vm, AccessControl.LICENSING_MANAGER, admin); -// _grantRole( -// vm, -// AccessControl.HOOK_CALLER_ROLE, -// address(registrationModule) -// ); -// _grantRole( -// vm, -// AccessControl.HOOK_CALLER_ROLE, -// address(relationshipModule) -// ); -// _grantRole( -// vm, -// AccessControl.HOOK_CALLER_ROLE, -// address(licensingModule) -// ); - -// /// TOKEN_GATED_HOOK -// tokenGatedHook = address(new TokenGatedHook(address(accessControl))); -// /// MOCK_ERC_721 -// mockNFT = new MockERC721(); -// mockNFT.mint(ipAssetOwner1, 1); -// mockNFT.mint(ipAssetOwner2, 2); -// } + using ShortStrings for *; + + address public tokenGatedHook; + MockERC721 public mockNFT; + + // create 3 roles: protocol admin, ip org owner, ip asset owner + address public ipOrgOwner1 = address(1234); + address public ipOrgOwner2 = address(4567); + address public ipAssetOwner1 = address(6789); + address public ipAssetOwner2 = address(9876); + address public ipAssetOwner3 = address(9876); + + address public ipOrg1; + address public ipOrg2; + + function setUp() public virtual override { + super.setUp(); + _grantRole(vm, AccessControl.RELATIONSHIP_MANAGER_ROLE, admin); + _grantRole(vm, AccessControl.LICENSING_MANAGER, admin); + _grantRole( + vm, + AccessControl.HOOK_CALLER_ROLE, + address(registrationModule) + ); + _grantRole( + vm, + AccessControl.HOOK_CALLER_ROLE, + address(relationshipModule) + ); + _grantRole( + vm, + AccessControl.HOOK_CALLER_ROLE, + address(licensingModule) + ); + + /// TOKEN_GATED_HOOK + tokenGatedHook = address(new TokenGatedHook(address(accessControl))); + /// MOCK_ERC_721 + mockNFT = new MockERC721(); + mockNFT.mint(ipAssetOwner1, 1); + mockNFT.mint(ipAssetOwner2, 2); + + // framework + Licensing.ParamDefinition[] + memory fParams = new Licensing.ParamDefinition[](3); + fParams[0] = Licensing.ParamDefinition({ + tag: "TEST_TAG_1".toShortString(), + paramType: Licensing.ParameterType.Bool + }); + fParams[1] = Licensing.ParamDefinition({ + tag: "TEST_TAG_2".toShortString(), + paramType: Licensing.ParameterType.Number + }); + fParams[2] = Licensing.ParamDefinition({ + tag: "TEST_TAG_3".toShortString(), + paramType: Licensing.ParameterType.MultipleChoice + }); + Licensing.SetFramework memory framework = Licensing.SetFramework({ + id: "test_framework", + textUrl: "text_url", + paramDefs: fParams + }); + vm.prank(licensingManager); + licensingFrameworkRepo.addFramework(framework); + } function test_e2e() public { // @@ -176,78 +196,6 @@ contract E2ETest is IE2ETest, BaseTest { hooksConfig ); - // configure license terms - vm.startPrank(admin); - Licensing.CommercialStatus comStatus = Licensing.CommercialStatus.Both; - - vm.expectEmit(address(termsRepository)); - emit TermCategoryAdded(TermCategories.SHARE_ALIKE); - termsRepository.addCategory(TermCategories.SHARE_ALIKE); - Licensing.LicensingTerm memory term = _getTerm( - TermIds.NFT_SHARE_ALIKE, - comStatus - ); - - vm.expectEmit(address(termsRepository)); - emit TermAdded(TermCategories.SHARE_ALIKE, TermIds.NFT_SHARE_ALIKE); - termsRepository.addTerm( - TermCategories.SHARE_ALIKE, - TermIds.NFT_SHARE_ALIKE, - term - ); - - vm.expectEmit(address(termsRepository)); - emit TermCategoryAdded(TermCategories.LICENSOR); - termsRepository.addCategory(TermCategories.LICENSOR); - term = _getTerm(TermIds.LICENSOR_APPROVAL, comStatus); - - vm.expectEmit(address(termsRepository)); - emit TermAdded(TermCategories.LICENSOR, TermIds.LICENSOR_APPROVAL); - termsRepository.addTerm( - TermCategories.LICENSOR, - TermIds.LICENSOR_APPROVAL, - term - ); - - vm.expectEmit(true, true, true, true); - emit TermCategoryAdded(TermCategories.CATEGORIZATION); - termsRepository.addCategory(TermCategories.CATEGORIZATION); - term = _getTerm(TermIds.FORMAT_CATEGORY, comStatus); - vm.expectEmit(true, true, true, true); - emit TermAdded(TermCategories.CATEGORIZATION, TermIds.FORMAT_CATEGORY); - termsRepository.addTerm( - TermCategories.CATEGORIZATION, - TermIds.FORMAT_CATEGORY, - term - ); - - vm.expectEmit(true, true, true, true); - emit TermCategoryAdded(TermCategories.ACTIVATION); - termsRepository.addCategory(TermCategories.ACTIVATION); - term = _getTerm(TermIds.LICENSOR_IPORG_OR_PARENT, comStatus); - vm.expectEmit(true, true, true, true); - emit TermAdded( - TermCategories.ACTIVATION, - TermIds.LICENSOR_IPORG_OR_PARENT - ); - termsRepository.addTerm( - TermCategories.ACTIVATION, - TermIds.LICENSOR_IPORG_OR_PARENT, - term - ); - - // assertTrue( - // Strings.equal( - // termsRepository.getTerm(TermIds.LICENSOR_IPORG_OR_PARENT).url, - // "" - // ) - // ); - - // vm.expectEmit(address(termsRepository)); - // emit TermCategoryRemoved(TermCategories.ACTIVATION); - // termsRepository.removeCategory(TermCategories.ACTIVATION); - - vm.stopPrank(); // protocol admin add relationship type LibRelationship.RelatedElements memory allowedElements = LibRelationship .RelatedElements({ @@ -268,44 +216,35 @@ contract E2ETest is IE2ETest, BaseTest { vm.prank(ipOrgOwner1); spg.addRelationshipType(relTypeParams); -// // ip org owner configure IpOrg Licensing -// // no commercial terms -// Licensing.TermsConfig memory comTermsConfig = Licensing.TermsConfig({ -// termIds: new ShortString[](0), -// termData: new bytes[](0) -// }); - -// // non commercial terms -// // all licensors would be ipOrg -// // all licenses begin as Pending, isLicenseActive == false, -// // need approval from licensor to activate them -// ShortString[] memory termIds_ = new ShortString[](3); -// bytes[] memory termsData_ = new bytes[](3); -// termIds_[0] = TermIds.NFT_SHARE_ALIKE.toShortString(); -// termsData_[0] = abi.encode(true); -// termIds_[1] = TermIds.LICENSOR_APPROVAL.toShortString(); -// termsData_[1] = abi.encode(true); -// termIds_[2] = TermIds.LICENSOR_IPORG_OR_PARENT.toShortString(); -// termsData_[2] = abi.encode(TermsData.LicensorConfig.IpOrg); - -// Licensing.TermsConfig memory nonComTermsConfig = Licensing.TermsConfig({ -// termIds: termIds_, -// termData: termsData_ -// }); - -// Licensing.FrameworkConfig memory frameworkConfig = Licensing -// .FrameworkConfig({ -// comTermsConfig: comTermsConfig, -// nonComTermsConfig: nonComTermsConfig -// }); + Licensing.ParamValue[] memory lParams = new Licensing.ParamValue[](3); + lParams[0] = Licensing.ParamValue({ + tag: "TEST_TAG_1".toShortString(), + value: abi.encode(true) + }); + lParams[1] = Licensing.ParamValue({ + tag: "TEST_TAG_2".toShortString(), + value: abi.encode(222) + }); + ShortString[] memory ssValue = new ShortString[](2); + ssValue[0] = "test1".toShortString(); + ssValue[1] = "test2".toShortString(); + lParams[2] = Licensing.ParamValue({ + tag: "TEST_TAG_3".toShortString(), + value: abi.encode(ssValue) + }); + + Licensing.LicensingConfig memory licensingConfig = Licensing + .LicensingConfig({ + frameworkId: "test_framework", + params: lParams, + licensor: Licensing.LicensorConfig.IpOrgOwnerAlways + }); // TODO: event check for `configureIpOrgLicensing` (event `IpOrgTermsSet` emitted twice) vm.prank(ipOrgOwner1); - spg.configureIpOrgLicensing(ipOrg1, frameworkConfig); + spg.configureIpOrgLicensing(ipOrg1, licensingConfig); // ip asset owner register IP Asset - uint ipAssetId; - uint ipOrgAssetId; Registration.RegisterIPAssetParams memory registerIpAssetParamsCharacter = Registration .RegisterIPAssetParams({ @@ -329,100 +268,100 @@ contract E2ETest is IE2ETest, BaseTest { // "" // ); vm.prank(ipAssetOwner1); - (ipAssetId, ipOrgAssetId) = spg.registerIPAsset( + (uint256 ipAssetId_1, uint256 ipOrg1_AssetId_1) = spg.registerIPAsset( ipOrg1, registerIpAssetParamsCharacter, preHooksDataCharacter, new bytes[](0) ); - assertEq(ipAssetId, 1); - assertEq(ipOrgAssetId, 1); - -// Registration.RegisterIPAssetParams -// memory registerIpAssetParamsStory = Registration -// .RegisterIPAssetParams({ -// owner: ipAssetOwner2, -// ipOrgAssetType: 1, -// name: "Story IPA", -// hash: 0x558b44f88e5959cec9c7836078a53ff4d6432142a9d5caa6f3a6eb7c83931111, -// mediaUrl: "https://arweave.net/story" -// }); -// TokenGated.Params memory tokenGatedHookDataStory = TokenGated.Params({ -// tokenOwner: ipAssetOwner2 -// }); -// bytes[] memory preHooksDataStory = new bytes[](1); -// preHooksDataStory[0] = abi.encode(tokenGatedHookDataStory); -// vm.prank(ipAssetOwner2); -// (ipAssetId, ipOrgAssetId) = spg.registerIPAsset( -// ipOrg1, -// registerIpAssetParamsStory, -// preHooksDataStory, -// new bytes[](0) -// ); -// assertEq(ipAssetId, 2); -// assertEq(ipOrgAssetId, 2); - -// Registration.RegisterIPAssetParams -// memory registerIpAssetParamsOrg2 = Registration -// .RegisterIPAssetParams({ -// owner: ipAssetOwner3, -// ipOrgAssetType: 1, -// name: "Story IPA Org2", -// hash: 0x558b44f88e5959cec9c7836078a53ff4d6432142a9d5caa6f3a6eb7c83933333, -// mediaUrl: "https://arweave.net/story2" -// }); -// vm.prank(ipAssetOwner3); -// (ipAssetId, ipOrgAssetId) = spg.registerIPAsset( -// ipOrg2, -// registerIpAssetParamsOrg2, -// new bytes[](0), -// new bytes[](0) -// ); -// assertEq(ipAssetId, 3); -// assertEq(ipOrgAssetId, 1); - -// // ip asset owner transfer IP Asset -// // ip asset owner create relationship -// LibRelationship.CreateRelationshipParams memory params = LibRelationship -// .CreateRelationshipParams({ -// relType: "APPEAR_IN", -// srcAddress: ipOrg1, -// srcId: 1, -// dstAddress: ipOrg1, -// dstId: 2 -// }); -// bytes[] memory preHooksDataRel = new bytes[](0); -// bytes[] memory postHooksDataRel = new bytes[](0); -// vm.prank(ipOrg1); -// uint256 id = spg.createRelationship( -// ipOrg1, -// params, -// preHooksDataRel, -// postHooksDataRel -// ); -// assertEq(id, 1); -// LibRelationship.Relationship memory rel = relationshipModule -// .getRelationship(1); -// assertEq(rel.relType, "APPEAR_IN"); -// assertEq(rel.srcAddress, ipOrg1); -// assertEq(rel.dstAddress, ipOrg1); -// assertEq(rel.srcId, 1); -// assertEq(rel.dstId, 2); + assertEq(ipAssetId_1, 1); + assertEq(ipOrg1_AssetId_1, 1); - vm.prank(ipOrgOwner1); - uint256 lId = spg.createIpaBoundLicense( + Registration.RegisterIPAssetParams + memory registerIpAssetParamsStory = Registration + .RegisterIPAssetParams({ + owner: ipAssetOwner2, + ipOrgAssetType: 1, + name: "Story IPA", + hash: 0x558b44f88e5959cec9c7836078a53ff4d6432142a9d5caa6f3a6eb7c83931111, + mediaUrl: "https://arweave.net/story" + }); + TokenGated.Params memory tokenGatedHookDataStory = TokenGated.Params({ + tokenOwner: ipAssetOwner2 + }); + bytes[] memory preHooksDataStory = new bytes[](1); + preHooksDataStory[0] = abi.encode(tokenGatedHookDataStory); + vm.prank(ipAssetOwner2); + (uint256 ipAssetId_2, uint256 ipOrg1_AssetId_2) = spg.registerIPAsset( ipOrg1, - Licensing.LicenseCreation({ - parentLicenseId: 0, - isCommercial: false - }), - 1, + registerIpAssetParamsStory, + preHooksDataStory, + new bytes[](0) + ); + assertEq(ipAssetId_2, 2); + assertEq(ipOrg1_AssetId_2, 2); + + Registration.RegisterIPAssetParams + memory registerIpAssetParamsOrg2 = Registration + .RegisterIPAssetParams({ + owner: ipAssetOwner3, + ipOrgAssetType: 1, + name: "Story IPA Org2", + hash: 0x558b44f88e5959cec9c7836078a53ff4d6432142a9d5caa6f3a6eb7c83933333, + mediaUrl: "https://arweave.net/story2" + }); + vm.prank(ipAssetOwner3); + (uint256 ipAssetId_3, uint256 ipOrg2_AssetId_1) = spg.registerIPAsset( + ipOrg2, + registerIpAssetParamsOrg2, new bytes[](0), new bytes[](0) ); - Licensing.License memory license = licenseRegistry.getLicense(lId); - assertFalse(license.isCommercial, "commercial"); - assertEq(license.ipaId, 1); + assertEq(ipAssetId_3, 3); + assertEq(ipOrg2_AssetId_1, 1); + + // ip asset owner transfer IP Asset + // ip asset owner create relationship + LibRelationship.CreateRelationshipParams + memory crParams = LibRelationship.CreateRelationshipParams({ + relType: "APPEAR_IN", + srcAddress: ipOrg1, + srcId: 1, + dstAddress: ipOrg1, + dstId: 2 + }); + bytes[] memory preHooksDataRel = new bytes[](0); + bytes[] memory postHooksDataRel = new bytes[](0); + vm.prank(ipOrg1); + uint256 id = spg.createRelationship( + ipOrg1, + crParams, + preHooksDataRel, + postHooksDataRel + ); + assertEq(id, 1); + LibRelationship.Relationship memory rel = relationshipModule + .getRelationship(1); + assertEq(rel.relType, "APPEAR_IN"); + assertEq(rel.srcAddress, ipOrg1); + assertEq(rel.dstAddress, ipOrg1); + assertEq(rel.srcId, 1); + assertEq(rel.dstId, 2); + + // vm.prank(ipOrgOwner1); + // uint256 lId = spg.createIpaBoundLicense( + // ipOrg1, + // Licensing.LicenseCreation({ + // parentLicenseId: 0, + // isCommercial: false + // }), + // 1, + // new bytes[](0), + // new bytes[](0) + // ); + // Licensing.LicenseData memory license = licenseRegistry.getLicenseData(licenseId); + // assertFalse(license.isCommercial, "commercial"); + // assertEq(license.ipaId, 1); bytes[] memory hooksTransferIPAsset = new bytes[](1); hooksTransferIPAsset[0] = abi.encode(ipAssetOwner1); @@ -445,30 +384,30 @@ contract E2ETest is IE2ETest, BaseTest { hooksTransferIPAsset, new bytes[](0) ); - } - function _getTerm( - string memory termId, - Licensing.CommercialStatus comStatus_ - ) internal pure returns (Licensing.LicensingTerm memory) { - return - Licensing.LicensingTerm({ - comStatus: comStatus_, - url: string(abi.encodePacked("https://", termId, ".com")), - hash: "qwertyu", - algorithm: "sha256", - hook: IHook(address(0)) - }); + vm.prank(address(registrationModule)); + vm.expectEmit(address(registry)); + emit IPOrgTransferred(ipAssetId_2, ipOrg1, ipOrg2); + registry.transferIPOrg(ipAssetId_2, ipOrg2); + assertEq(registry.ipAssetOrg(ipAssetId_2), ipOrg2); + + vm.prank(address(0)); // TODO: modify when `onlyDisputer` is complete + emit StatusChanged(ipAssetId_2, 1, 0); // 0 means unset, 1 means set (change when status is converted to ENUM) + registry.setStatus(ipAssetId_2, 0); + assertEq(registry.status(ipAssetId_2), 0); } - // function _getExecutionContext( - // bytes memory hookConfig_, - // bytes memory hookParams_ - // ) internal pure returns (bytes memory) { - // Hook.ExecutionContext memory context = Hook.ExecutionContext({ - // config: hookConfig_, - // params: hookParams_ - // }); - // return abi.encode(context); + // function _getTerm( + // string memory termId, + // Licensing.CommercialStatus comStatus_ + // ) internal pure returns (Licensing.LicensingTerm memory) { + // return + // Licensing.LicensingTerm({ + // comStatus: comStatus_, + // url: string(abi.encodePacked("https://", termId, ".com")), + // hash: "qwertyu", + // algorithm: "sha256", + // hook: IHook(address(0)) + // }); // } } diff --git a/test/foundry/interfaces/IE2ETest.sol b/test/foundry/interfaces/IE2ETest.sol index 0f655650..6184f801 100644 --- a/test/foundry/interfaces/IE2ETest.sol +++ b/test/foundry/interfaces/IE2ETest.sol @@ -3,11 +3,13 @@ pragma solidity ^0.8.19; import { HookResult } from "contracts/interfaces/hooks/base/IHook.sol"; import { LibRelationship } from "contracts/lib/modules/LibRelationship.sol"; +import { Licensing } from "contracts/lib/modules/Licensing.sol"; import { HookRegistry } from "contracts/modules/base/HookRegistry.sol"; +import { ShortString } from "@openzeppelin/contracts/utils/ShortStrings.sol"; interface IE2ETest { // - // RegistrationModule events + // Registration Module // event MetadataUpdated( @@ -36,7 +38,7 @@ interface IE2ETest { ); // - // RelationshipModule events + // Relationship Module // event RelationshipTypeSet( @@ -50,10 +52,7 @@ interface IE2ETest { uint256 dstSubtypesMask ); - event RelationshipTypeUnset( - string relType, - address ipOrg - ); + event RelationshipTypeUnset(string relType, address ipOrg); event RelationshipCreated( uint256 indexed relationshipId, @@ -65,7 +64,40 @@ interface IE2ETest { ); // - // HookRegistry events + // Licensing + // + + event IpOrgLicensingFrameworkSet( + address indexed ipOrg, + string frameworkId, + string url, + Licensing.LicensorConfig licensorConfig + ); + + event ParameterSet( + address indexed ipOrg, + string paramTag, + bytes defaultValue + ); + + event FrameworkAdded(string frameworkId, string textUrl); + + event ParamDefinitionAdded( + string frameworkId, + ShortString tag, + Licensing.ParameterType paramType + ); + + event LicenseRegistered(uint256 indexed id); + event LicenseNftLinkedToIpa( + uint256 indexed licenseId, + uint256 indexed ipAssetId + ); + event LicenseActivated(uint256 indexed licenseId); + event LicenseRevoked(uint256 indexed licenseId); + + // + // HookRegistry // event HooksRegistered( @@ -79,16 +111,7 @@ interface IE2ETest { ); // - // TermsRepository events - // - - event TermCategoryAdded(string category); - event TermCategoryRemoved(string category); - event TermAdded(string category, string termId); - event TermDisabled(string category, string termId); - - // - // SyncHook events + // SyncHook // event SyncHookExecuted( @@ -97,4 +120,28 @@ interface IE2ETest { bytes contextData, bytes returnData ); + + // + // IPAssetRegistry + // + + event Registered( + uint256 ipAssetId_, + string name_, + address indexed ipOrg_, + address indexed registrant_, + bytes32 hash_ + ); + + event IPOrgTransferred( + uint256 indexed ipAssetId_, + address indexed oldIPOrg_, + address indexed newIPOrg_ + ); + + event StatusChanged( + uint256 indexed ipAssetId_, + uint8 oldStatus_, + uint8 newStatus_ + ); } diff --git a/test/foundry/modules/ModuleRegistry.t.sol b/test/foundry/modules/ModuleRegistry.t.sol index 74acef93..d32fb51d 100644 --- a/test/foundry/modules/ModuleRegistry.t.sol +++ b/test/foundry/modules/ModuleRegistry.t.sol @@ -56,6 +56,14 @@ contract ModuleRegistryTest is Test, AccessControlHelper { assertEq(address(registry.moduleForKey("test")), address(module)); } + + function test_moduleRegistry_revert_addProtocolModuleZeroAddress() public { + vm.expectRevert(Errors.ZeroAddress.selector); + vm.prank(admin); + registry.registerProtocolModule("test", BaseModule(address(0))); + + assertEq(address(registry.moduleForKey("test")), address(0)); + } function test_moduleRegistry_removeProtocolModule() public { BaseModule.ModuleConstruction memory moduleConstruction = BaseModule.ModuleConstruction( @@ -79,4 +87,31 @@ contract ModuleRegistryTest is Test, AccessControlHelper { vm.stopPrank(); } + + function test_moduleRegistry_revert_removeProtocolModuleModuleNotRegistered() public { + vm.expectRevert( + abi.encodeWithSelector( + Errors.ModuleRegistry_ModuleNotRegistered.selector, + "unregistered_module_key" + ) + ); + vm.prank(admin); + registry.removeProtocolModule("unregistered_module_key"); + assertEq(address(registry.moduleForKey("unregistered_module_key")), address(0)); + } + + function test_moduleRegistry_revert_configureModuleNotRegistered() public { + bytes memory encodedParams = abi.encode("test"); + vm.expectRevert( + abi.encodeWithSelector( + Errors.ModuleRegistry_ModuleNotRegistered.selector, + "unregistered_module_key" + ) + ); + registry.configure( + IIPOrg(address(0x123)), + "unregistered_module_key", + encodedParams + ); + } } diff --git a/test/foundry/modules/licensing/LicensingFrameworkRepo.t.sol b/test/foundry/modules/licensing/LicensingFrameworkRepo.t.sol index 590e36c5..d25892af 100644 --- a/test/foundry/modules/licensing/LicensingFrameworkRepo.t.sol +++ b/test/foundry/modules/licensing/LicensingFrameworkRepo.t.sol @@ -1,3 +1,4 @@ +/* solhint-disable contract-name-camelcase, func-name-mixedcase */ // SPDX-License-Identifier: BUSDL-1.1 pragma solidity ^0.8.13; @@ -14,7 +15,7 @@ import { Errors } from "contracts/lib/Errors.sol"; contract LicensingFrameworkRepoTest is Test, AccessControlHelper { using ShortStrings for *; - LicensingFrameworkRepo repo; + LicensingFrameworkRepo internal repo; event RequestPending(address indexed sender); event RequestCompleted(address indexed sender); diff --git a/test/foundry/modules/licensing/LicensingModule.Licensing.sol b/test/foundry/modules/licensing/LicensingModule.Licensing.sol index 95581bb3..1022a13e 100644 --- a/test/foundry/modules/licensing/LicensingModule.Licensing.sol +++ b/test/foundry/modules/licensing/LicensingModule.Licensing.sol @@ -1,66 +1,97 @@ +/* solhint-disable contract-name-camelcase, func-name-mixedcase */ // SPDX-License-Identifier: BUSDL-1.1 pragma solidity ^0.8.13; import "forge-std/Test.sol"; -import "test/foundry/utils/BaseTest.sol"; + import { AccessControl } from "contracts/lib/AccessControl.sol"; import { Licensing } from "contracts/lib/modules/Licensing.sol"; import { IPAsset } from "contracts/lib/IPAsset.sol"; import { BaseTest } from "test/foundry/utils/BaseTest.sol"; import { Errors } from "contracts/lib/Errors.sol"; import { PIPLicensingTerms } from "contracts/lib/modules/PIPLicensingTerms.sol"; +import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol"; +// TODO: test on derivativeNeedsApproval = false contract LicensingModuleLicensingTest is BaseTest { using ShortStrings for *; - address ipaOwner = address(0x13336); - Licensing.ParamValue[] params; + event LicenseRegistered(uint256 indexed id); + event LicenseNftLinkedToIpa( + uint256 indexed licenseId, + uint256 indexed ipAssetId + ); + event LicenseActivated(uint256 indexed licenseId); + event LicenseRevoked(uint256 indexed licenseId); + + address internal ipaOwner = address(0x13336); + Licensing.ParamValue[] internal params; - uint256 ipaId; + uint256 internal ipaId_1; + uint256 internal ipaId_2; - modifier withFrameworkConfig(bool derivativesWithApproval, bool reciprocal, Licensing.LicensorConfig licensorConfig) { + modifier withFrameworkConfig( + bool derivativesWithApproval, + bool reciprocal, + Licensing.LicensorConfig licensorConfig + ) { ShortString[] memory channels = new ShortString[](2); channels[0] = "test1".toShortString(); channels[1] = "test2".toShortString(); - params.push(Licensing.ParamValue({ - tag: PIPLicensingTerms.CHANNELS_OF_DISTRIBUTION.toShortString(), - value: abi.encode(channels) - })); - params.push(Licensing.ParamValue({ - tag: PIPLicensingTerms.ATTRIBUTION.toShortString(), - value: ""// unset - })); - params.push(Licensing.ParamValue({ - tag: PIPLicensingTerms.DERIVATIVES_WITH_ATTRIBUTION.toShortString(), - value: abi.encode(true) - })); - params.push(Licensing.ParamValue({ - tag: PIPLicensingTerms.DERIVATIVES_WITH_APPROVAL.toShortString(), - value: abi.encode(derivativesWithApproval) - })); - params.push(Licensing.ParamValue({ - tag: PIPLicensingTerms.DERIVATIVES_WITH_RECIPROCAL_LICENSE.toShortString(), - value: abi.encode(reciprocal) - })); - + params.push( + Licensing.ParamValue({ + tag: PIPLicensingTerms.CHANNELS_OF_DISTRIBUTION.toShortString(), + value: abi.encode(channels) + }) + ); + params.push( + Licensing.ParamValue({ + tag: PIPLicensingTerms.ATTRIBUTION.toShortString(), + value: "" // unset + }) + ); + params.push( + Licensing.ParamValue({ + tag: PIPLicensingTerms + .DERIVATIVES_WITH_ATTRIBUTION + .toShortString(), + value: abi.encode(true) + }) + ); + params.push( + Licensing.ParamValue({ + tag: PIPLicensingTerms + .DERIVATIVES_WITH_APPROVAL + .toShortString(), + value: abi.encode(derivativesWithApproval) + }) + ); + params.push( + Licensing.ParamValue({ + tag: PIPLicensingTerms + .DERIVATIVES_WITH_RECIPROCAL_LICENSE + .toShortString(), + value: abi.encode(reciprocal) + }) + ); + Licensing.LicensingConfig memory config = Licensing.LicensingConfig({ frameworkId: PIPLicensingTerms.FRAMEWORK_ID, params: params, licensor: licensorConfig }); vm.prank(ipOrg.owner()); - spg.configureIpOrgLicensing( - address(ipOrg), - config - ); + spg.configureIpOrgLicensing(address(ipOrg), config); _; } function setUp() public override { super.setUp(); - (ipaId, ) = _createIpAsset(ipaOwner, 1, bytes("")); + (ipaId_1, ) = _createIpAsset(ipaOwner, 1, bytes("")); + (ipaId_2, ) = _createIpAsset(ipaOwner, 1, bytes("")); - Licensing.ParamDefinition[] memory paramDefs = PIPLicensingTerms._getParamDefs(); + Licensing.ParamDefinition[] memory paramDefs = PIPLicensingTerms + ._getParamDefs(); Licensing.SetFramework memory framework = Licensing.SetFramework({ id: PIPLicensingTerms.FRAMEWORK_ID, textUrl: "text_url", @@ -69,20 +100,22 @@ contract LicensingModuleLicensingTest is BaseTest { vm.prank(licensingManager); licensingFrameworkRepo.addFramework(framework); } - - function test_LicensingModule_createLicense_noParent_ipa_userSetsParam() - withFrameworkConfig(true, true, Licensing.LicensorConfig.IpOrgOwnerAlways) - public returns (uint256) { - Licensing.ParamValue[] memory inputParams = new Licensing.ParamValue[](1); - inputParams[0] = Licensing.ParamValue({ - tag: PIPLicensingTerms.ATTRIBUTION.toShortString(), - value: abi.encode(true) - }); + function test_LicensingModule_createLicense_noParent_ipa_userSetsParam() + public + withFrameworkConfig( + true, + true, + Licensing.LicensorConfig.IpOrgOwnerAlways + ) + returns (uint256) + { + uint256 _parentLicenseId = 0; // no parent + Licensing.ParamValue[] memory inputParams = _constructInputParams(); Licensing.LicenseCreation memory creation = Licensing.LicenseCreation({ params: inputParams, - parentLicenseId: 0, - ipaId: ipaId + parentLicenseId: _parentLicenseId, + ipaId: ipaId_1 }); vm.prank(ipOrg.owner()); uint256 licenseId = spg.createLicense( @@ -91,79 +124,372 @@ contract LicensingModuleLicensingTest is BaseTest { new bytes[](0), new bytes[](0) ); - Licensing.LicenseData memory license = licenseRegistry.getLicenseData(licenseId); - assertEq(uint8(license.status), uint8(Licensing.LicenseStatus.Active)); - assertEq(license.isReciprocal, true, "isReciprocal"); - assertEq(license.derivativeNeedsApproval, true, "derivativeNeedsApproval"); - assertEq(license.revoker, licensingModule.DEFAULT_REVOKER()); - assertEq(license.licensor, ipOrg.owner()); - assertEq(license.ipOrg, address(ipOrg)); - assertEq(license.frameworkId.toString(), PIPLicensingTerms.FRAMEWORK_ID); - assertEq(license.ipaId, ipaId); - assertEq(license.parentLicenseId, 0); - Licensing.ParamValue[] memory lParams = licenseRegistry.getParams(licenseId); - assertEq(lParams[0].tag.toString(), params[0].tag.toString(), "channel of distribution"); - assertEq(lParams[0].value, params[0].value); - assertEq(lParams[1].tag.toString(), params[1].tag.toString(), "attribution"); - assertEq(lParams[1].value, inputParams[0].value); // Set by user - assertEq(lParams[2].tag.toString(), params[2].tag.toString(), "derivatives with attribution"); - assertEq(lParams[2].value, params[2].value); - assertEq(lParams[3].tag.toString(), params[3].tag.toString(), "derivatives with approval"); - assertEq(lParams[3].value, params[3].value); return licenseId; } function test_LicensingModule_createLicense_parent_noIpa_reciprocal() - public returns (uint256) { + public + returns (uint256 parentLicenseId, uint256 childLicenseId) + { + parentLicenseId = test_LicensingModule_createLicense_noParent_ipa_userSetsParam(); + uint256 _ipaId = 0; // no ipa + Licensing.LicenseCreation memory creation = Licensing.LicenseCreation({ + params: new Licensing.ParamValue[](0), + parentLicenseId: parentLicenseId, + ipaId: _ipaId + }); + vm.prank(ipOrg.owner()); + childLicenseId = spg.createLicense( + address(ipOrg), + creation, + new bytes[](0), + new bytes[](0) + ); + assertEq(childLicenseId, 2, "childLicenseId"); + } + + function test_LicensingModule_revert_addReciprocalLicense_ParentLicenseNotActive() + public + { uint256 parentLicenseId = test_LicensingModule_createLicense_noParent_ipa_userSetsParam(); Licensing.LicenseCreation memory creation = Licensing.LicenseCreation({ params: new Licensing.ParamValue[](0), parentLicenseId: parentLicenseId, ipaId: 0 }); + vm.prank(ipOrg.owner()); - uint256 licenseId = spg.createLicense( + uint256 childLicenseId = spg.createLicense( address(ipOrg), creation, new bytes[](0), new bytes[](0) ); - assertEq(licenseId, 2); - Licensing.LicenseData memory license = licenseRegistry.getLicenseData(licenseId); - assertEq(uint8(license.status), uint8(Licensing.LicenseStatus.PendingLicensorApproval)); - assertEq(license.isReciprocal, true, "isReciprocal"); - assertEq(license.derivativeNeedsApproval, true, "derivativeNeedsApproval"); - assertEq(license.revoker, licensingModule.DEFAULT_REVOKER()); - assertEq(license.licensor, ipOrg.owner()); - assertEq(license.ipOrg, address(ipOrg)); - assertEq(license.frameworkId.toString(), PIPLicensingTerms.FRAMEWORK_ID); - assertEq(license.ipaId, 0, "ipaId"); - assertEq(license.parentLicenseId, parentLicenseId); - Licensing.ParamValue[] memory parentParams = licenseRegistry.getParams(parentLicenseId); - Licensing.ParamValue[] memory childParams = licenseRegistry.getParams(licenseId); - assertEq(parentParams[0].tag.toString(), childParams[0].tag.toString(), "channel of distribution"); - assertEq(parentParams[0].value, childParams[0].value, "channel of distribution"); - assertEq(parentParams[1].tag.toString(), childParams[1].tag.toString(), "attribution"); - assertEq(parentParams[1].value, childParams[1].value, "attribution"); - assertEq(parentParams[2].tag.toString(), childParams[2].tag.toString(), "derivatives with attribution"); - assertEq(parentParams[2].value, childParams[2].value, "derivatives with attribution"); - assertEq(parentParams[3].tag.toString(), childParams[3].tag.toString(), "derivatives with approval"); - assertEq(parentParams[3].value, childParams[3].value, "derivatives with approval"); - return licenseId; + assertEq(childLicenseId, 2); } function test_LicensingModule_activateLicense() - public returns (uint256) { - uint256 licenseId = test_LicensingModule_createLicense_parent_noIpa_reciprocal(); + public + returns (uint256 licenseId) + { + ( + , + licenseId + ) = test_LicensingModule_createLicense_parent_noIpa_reciprocal(); vm.prank(ipOrg.owner()); - spg.activateLicense( - address(ipOrg), + vm.expectEmit(address(licenseRegistry)); + emit LicenseActivated(licenseId); + spg.activateLicense(address(ipOrg), licenseId); + Licensing.LicenseData memory license = licenseRegistry.getLicenseData( licenseId ); - Licensing.LicenseData memory license = licenseRegistry.getLicenseData(licenseId); - assertEq(uint8(license.status), uint8(Licensing.LicenseStatus.Active)); - return licenseId; + assertEq( + uint8(license.status), + uint8(Licensing.LicenseStatus.Active), + "license status" + ); + } + + function test_LicensingModule_revokeLicense() + public + returns (uint256 licenseId) + { + licenseId = test_LicensingModule_activateLicense(); + + vm.prank(licenseRegistry.getRevoker(licenseId)); + vm.expectEmit(address(licenseRegistry)); + emit LicenseRevoked(licenseId); + licenseRegistry.revokeLicense(licenseId); + + // TODO: also check for change IPA status once implemented + Licensing.LicenseData memory license = licenseRegistry.getLicenseData( + licenseId + ); + assertEq( + uint8(license.status), + uint8(Licensing.LicenseStatus.Revoked), + "license status" + ); + } + + function test_LicensingModule_revert_revokeLicense_CallerNotRevoker() external { + uint256 licenseId = test_LicensingModule_activateLicense(); + vm.expectRevert(Errors.LicenseRegistry_CallerNotRevoker.selector); + licenseRegistry.revokeLicense(licenseId); + } + + + function test_LicenseRegistry_revert_CallerNotLicensingModule_noParent_ipa() + public + { + uint256 licenseId = test_LicensingModule_createLicense_noParent_ipa_userSetsParam(); + Licensing.LicenseData memory license = licenseRegistry.getLicenseData( + licenseId + ); + vm.expectRevert( + Errors.LicenseRegistry_CallerNotLicensingModule.selector + ); + licenseRegistry.addLicense(license, msg.sender, params); + } + + function test_LicenseRegistry_revert_CallerNotLicensingModule_parent_noIpa() + public + { + ( + , + uint256 licenseId + ) = test_LicensingModule_createLicense_parent_noIpa_reciprocal(); + Licensing.LicenseData memory license = licenseRegistry.getLicenseData( + licenseId + ); + vm.expectRevert( + Errors.LicenseRegistry_CallerNotLicensingModule.selector + ); + licenseRegistry.addLicense(license, msg.sender, params); + } + + function test_LicenseRegistry_revert_CallerNotLicensor_noParent_ipa() + public + { + uint256 licenseId = test_LicensingModule_createLicense_noParent_ipa_userSetsParam(); + vm.expectRevert(Errors.LicenseRegistry_CallerNotLicensor.selector); + spg.activateLicense(address(ipOrg), licenseId); + } + function test_LicenseRegistry_revert_CallerNotLicensor_parent_noIpa() + public + { + ( + , + uint256 licenseId + ) = test_LicensingModule_createLicense_parent_noIpa_reciprocal(); + vm.expectRevert(Errors.LicenseRegistry_CallerNotLicensor.selector); + spg.activateLicense(address(ipOrg), licenseId); } + function test_LicenseRegistry_getLicenseData_noParent_ipa() public { + uint256 licenseId = test_LicensingModule_createLicense_noParent_ipa_userSetsParam(); + // Licensing.ParamValue[] memory inputParams = _constructInputParams(); + _assertLicenseData( + licenseRegistry.getLicenseData(licenseId), + licenseId, + Licensing.LicenseStatus.Unset, + true, + true, + 0, // no parent + ipaId_1 + ); + _assertLicenseParams(licenseRegistry.getParams(licenseId), params); + } + + function test_LicenseRegistry_getLicenseData_parent_noIpa() public { + ( + uint256 parentLicenseId, + uint256 childLicenseId + ) = test_LicensingModule_createLicense_parent_noIpa_reciprocal(); + _assertLicenseData( + licenseRegistry.getLicenseData(childLicenseId), + childLicenseId, + Licensing.LicenseStatus.Unset, + true, + true, + parentLicenseId, + 0 // no ipa + ); + + Licensing.ParamValue[] memory parentParams = licenseRegistry.getParams( + parentLicenseId + ); + Licensing.ParamValue[] memory childParams = licenseRegistry.getParams( + childLicenseId + ); + + _assertLicenseParams(parentParams, childParams); + // additional for license params + assertEq(parentParams[1].value, childParams[1].value, "attribution"); + } + + function test_LicenseRegistry_linkLnftToIpa() public { + ( + , + uint256 childLicenseId + ) = test_LicensingModule_createLicense_parent_noIpa_reciprocal(); + + vm.prank(ipOrg.owner()); + spg.activateLicense(address(ipOrg), childLicenseId); + + vm.expectEmit(address(licenseRegistry)); + emit LicenseNftLinkedToIpa(childLicenseId, ipaId_2); + vm.prank(address(licensingModule)); + licenseRegistry.linkLnftToIpa(childLicenseId, ipaId_2); + } + + function test_LicenseRegistry_revert_linkLnftToIpa_LicenseAlreadyLinkedToIpa() + public + { + ( + , + uint256 licenseId + ) = test_LicensingModule_createLicense_parent_noIpa_reciprocal(); + + vm.prank(ipOrg.owner()); + spg.activateLicense(address(ipOrg), licenseId); + + vm.prank(ipOrg.owner()); + licenseRegistry.linkLnftToIpa(licenseId, ipaId_1); + + vm.prank(ipOrg.owner()); + vm.expectRevert( + Errors.LicenseRegistry_LicenseAlreadyLinkedToIpa.selector + ); + licenseRegistry.linkLnftToIpa(licenseId, ipaId_1); + } + + function test_LicenseRegistry_revert_linkLnftToIpa_LicenseRegistry_IPANotActive() + public + { + ( + , + uint256 licenseId + ) = test_LicensingModule_createLicense_parent_noIpa_reciprocal(); + + vm.prank(ipOrg.owner()); + spg.activateLicense(address(ipOrg), licenseId); + + uint256 _ipaId = 123_789; // some id that's not active + + vm.prank(ipOrg.owner()); + vm.expectRevert(Errors.LicenseRegistry_IPANotActive.selector); + licenseRegistry.linkLnftToIpa(licenseId, _ipaId); + } + + function test_LicenseRegistry_revert_linkLnftToIpa_LicenseNotActive() + public + { + ( + , + uint256 childLicenseId + ) = test_LicensingModule_createLicense_parent_noIpa_reciprocal(); + vm.prank(ipOrg.owner()); + vm.expectRevert(Errors.LicenseRegistry_LicenseNotActive.selector); + licenseRegistry.linkLnftToIpa(childLicenseId, ipaId_1); + } + + function _constructInputParams() + internal + returns (Licensing.ParamValue[] memory) + { + Licensing.ParamValue[] memory inputParams = new Licensing.ParamValue[]( + 1 + ); + inputParams[0] = Licensing.ParamValue({ + tag: PIPLicensingTerms.ATTRIBUTION.toShortString(), + value: abi.encode(true) + }); + return inputParams; + } + + function _assertLicenseData( + Licensing.LicenseData memory license, + uint256 licenseId, + Licensing.LicenseStatus expectedLicenseStatus, + bool expectedIsReciprocal, + bool expectedDerivativeNeedsApproval, + uint256 expectedParentLicenseId, + uint256 expectedIpaId + ) internal { + assertEq( + uint8(license.status), + uint8(expectedLicenseStatus), + "licenseStatus" + ); + assertEq( + license.isReciprocal, + licenseRegistry.isReciprocal(licenseId), + "isReciprocal A" + ); + assertEq(license.isReciprocal, expectedIsReciprocal, "isReciprocal B"); + assertEq( + license.derivativeNeedsApproval, + licenseRegistry.derivativeNeedsApproval(licenseId), + "derivativeNeedsApproval A" + ); + assertEq( + license.derivativeNeedsApproval, + expectedDerivativeNeedsApproval, + "derivativeNeedsApproval B" + ); + assertEq( + license.revoker, + licenseRegistry.getRevoker(licenseId), + "revoker A" + ); + assertEq( + license.revoker, + licensingModule.DEFAULT_REVOKER(), + "revoker B" + ); + assertEq( + license.licensor, + licenseRegistry.getLicensor(licenseId), + "licensor A" + ); + assertEq(license.licensor, ipOrg.owner(), "licensor B"); + assertEq(license.ipOrg, licenseRegistry.getIPOrg(licenseId), "ipOrg A"); + assertEq(license.ipOrg, address(ipOrg), "ipOrg B"); + assertEq( + license.frameworkId.toString(), + PIPLicensingTerms.FRAMEWORK_ID + ); + assertEq(license.ipaId, licenseRegistry.getIpaId(licenseId), "ipaId A"); + assertEq(license.ipaId, expectedIpaId, "ipaId B"); + assertEq( + license.parentLicenseId, + licenseRegistry.getParentLicenseId(licenseId), + "parentLicenseId A" + ); + assertEq( + license.parentLicenseId, + expectedParentLicenseId, + "parentLicenseId B" + ); + } + + function _assertLicenseParams( + Licensing.ParamValue[] memory lParams, + Licensing.ParamValue[] memory rParams + ) internal { + assertEq( + lParams[0].tag.toString(), + rParams[0].tag.toString(), + "channel of distribution" + ); + assertEq(lParams[0].value, rParams[0].value, "channel of distribution"); + assertEq( + lParams[1].tag.toString(), + rParams[1].tag.toString(), + "attribution" + ); + // assertEq(lParams[1].value, inputParams[0].value); // Set by user + assertEq( + lParams[2].tag.toString(), + rParams[2].tag.toString(), + "derivatives with attribution" + ); + assertEq( + lParams[2].value, + rParams[2].value, + "derivatives with attribution" + ); + assertEq( + lParams[3].tag.toString(), + rParams[3].tag.toString(), + "derivatives with approval" + ); + assertEq( + lParams[3].value, + rParams[3].value, + "derivatives with approval" + ); + } } diff --git a/test/foundry/utils/BaseTest.sol b/test/foundry/utils/BaseTest.sol index f6048d90..c19cb3c3 100644 --- a/test/foundry/utils/BaseTest.sol +++ b/test/foundry/utils/BaseTest.sol @@ -165,5 +165,4 @@ contract BaseTest is BaseTestUtils, ProxyHelper, AccessControlHelper { bytes[] memory hooks = new bytes[](0); return spg.registerIPAsset(address(ipOrg), params, hooks, hooks); } - }