Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup e2e tests #325

Merged
merged 21 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ TENDERLY_URL =
TENDERLY_PRIVATEKEY =

# ETHERSCAN APIY KEY
ETHERSCAN_API_KEY = ETHERSCANAPIKEYETHERSCANAPIKEY
ETHERSCAN_API_KEY = ETHERSCANAPIKEYETHERSCANAPIKEY

# TESTNET
DEVNET_URL = http://
DEVNET_CHAINID = 1315
DEVNET_PRIVATEKEY =
DEVNET_USER1 =
DEVNET_ERC721 =
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ typechain
# converage
lcov.info

# test
mochawesome-report

16 changes: 14 additions & 2 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { HardhatConfig, HardhatUserConfig } from "hardhat/types"
import "hardhat-contract-sizer" // npx hardhat size-contracts
import "solidity-coverage"
import "solidity-docgen"
import "@nomicfoundation/hardhat-chai-matchers"

require("dotenv").config()

Expand All @@ -30,6 +31,11 @@ const USE_TENDERLY = process.env.USE_TENDERLY === "true"
const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY || "key"
const COINMARKETCAP_API_KEY = process.env.COINMARKETCAP_API_KEY || "key"

const DEVNET_URL = process.env.DEVNET_URL || "http://"
const DEVNET_CHAINID = Number(process.env.DEVNET_CHAINID) || 1513
const DEVNET_PRIVATEKEY = process.env.DEVNET_PRIVATEKEY || "0xkey"
const DEVNET_USER1 = process.env.DEVNET_USER1 || "0xkey"

if (USE_TENDERLY) {
tdly.setup({
automaticVerifications: true,
Expand All @@ -41,7 +47,7 @@ const config: HardhatUserConfig = {
solidity: {
compilers: [
{
version: "0.8.23",
version: "0.8.26",
},
],
settings: {
Expand All @@ -62,6 +68,11 @@ const config: HardhatUserConfig = {
hardhat: {
chainId: 31337,
},
odyssey: {
chainId: DEVNET_CHAINID,
url: DEVNET_URL,
accounts: [DEVNET_PRIVATEKEY, DEVNET_USER1],
},
localhost: {
chainId: 31337,
url: "http://127.0.0.1:8545/",
Expand Down Expand Up @@ -101,7 +112,8 @@ const config: HardhatUserConfig = {
coinmarketcap: COINMARKETCAP_API_KEY,
},
mocha: {
timeout: 20_000,
timeout: 60_000,
reporter: "mochawesome",
},
etherscan: {
apiKey: ETHERSCAN_API_KEY,
Expand Down
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@
"docgen": "hardhat docgen"
},
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^2.0.8",
"@nomicfoundation/hardhat-ethers": "^3.0.5",
"@nomicfoundation/hardhat-foundry": "^1.1.1",
"@nomicfoundation/hardhat-verify": "^2.0.3",
"@openzeppelin/hardhat-upgrades": "^3.0.2",
"@tenderly/hardhat-tenderly": "^2.2.1",
"@typechain/ethers-v6": "^0.5.1",
"@typechain/hardhat": "^9.1.0",
"@types/chai": "4",
"@types/mocha": "^10.0.10",
"@types/node": "^22.10.0",
"base64-sol": "^1.1.0",
"chai": "^5.0.3",
"chai": "4",
"dotenv": "^16.4.1",
"ds-test": "https://github.com/dapphub/ds-test",
"eslint": "^8.56.0",
Expand All @@ -45,6 +49,7 @@
"husky": "^8.0.0",
"minimatch": "^9.0.3",
"mocha": "^10.2.0",
"mochawesome": "^7.1.3",
"prettier": "^3.0.0",
"prettier-plugin-solidity": "^1.1.0",
"solhint": "^4.1.1",
Expand All @@ -53,7 +58,8 @@
"solidity-coverage": "^0.8.6",
"solidity-docgen": "^0.6.0-beta.36",
"ts-node": "^10.9.2",
"typechain": "^8.3.2"
"typechain": "^8.3.2",
"typescript": "^5.7.2"
},
"dependencies": {
"@openzeppelin/contracts": "5.0.2",
Expand Down
32 changes: 32 additions & 0 deletions test/hardhat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Story Protocol End-to-End Testing

This folder contains story protocol end-to-end test scripts.

## Requirements

Please install the following:

- [Foundry / Foundryup](https://github.com/gakonst/foundry)
- [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#overview)

## Quickstart

Install the dependencies: run yarn command at project root. If you encounter any issues, try to remove node-modules and yarn.lock then run yarn again.

```sh
yarn # this installs packages
limengformal marked this conversation as resolved.
Show resolved Hide resolved
```

You'll need to add the variables refer to the .env.example to a .env file at project root.

Then, at project root run the tests with command:

```sh
npx hardhat test --network odyssey
limengformal marked this conversation as resolved.
Show resolved Hide resolved
```

You can specify the file path if you want to run test on a specific file:

```sh
npx hardhat test [file-path] --network odyssey
```
28 changes: 28 additions & 0 deletions test/hardhat/e2e/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// This is the deployed protocol address constants file.

export const AccessController = "0xf709c8001E94e2ca6F98b7fFBCd5BD3943E46D81";
limengformal marked this conversation as resolved.
Show resolved Hide resolved
export const CoreMetadataModule = "0x89630Ccf23277417FBdfd3076C702F5248267e78";
export const CoreMetadataViewModule = "0x6839De4A647eE2311bd765f615E09f7bd930ed25";
export const DisputeModule = "0x692B47fa72eE7Ac0Ec617ea384a0cAD41098F712";
export const EvenSplitGroupPool = "0xC384B56fD62d6679Cd62A2fE0dA3fe4560f33391";
export const GroupNFT = "0x5d7C6e71290f034bED4C241eD78642204ad1178A";
export const GroupingModule = "0xa731948cfE05135ad77d48C71f75066333Da78Bf";
export const IPAccountImpl = "0x24F08796561d6E1AC08e82b68BF4d9500B374Af6";
export const IPAssetRegistry = "0x28E59E91C0467e89fd0f0438D47Ca839cDfEc095";
export const IPGraphACL = "0x680E66e4c7Df9133a7AFC1ed091089B32b89C4ae";
export const IpRoyaltyVaultBeacon = "0x1F9CEDe79f5Af0a0A8E527Ad84e6C74f57D5F926";
export const IpRoyaltyVaultImpl = "0x1081250219B16cc3903Aa2d2d1403A75c6A2F9f5";
export const LicenseRegistry = "0xBda3992c49E98392e75E78d82B934F3598bA495f";
export const LicenseToken = "0xB138aEd64814F2845554f9DBB116491a077eEB2D";
export const LicensingModule = "0x5a7D9Fa17DE09350F481A53B470D798c1c1aabae";
export const MockERC20 = "0x12A8b0DcC6e3bB0915638361D9D49942Da07F455";
export const ModuleRegistry = "0x9F18c5723BC4Ee447CF9B01a8543D3b08b7F09C7";
export const PILicenseTemplate = "0x58E2c909D557Cd23EF90D14f8fd21667A5Ae7a93";
export const ProtocolAccessManager = "0xD22ff1C7e88aF45166aEFe000C4C0c4873Afa284";
export const ProtocolPauseAdmin = "0x65C6Ec6Cc074eaf7ba3970C540b4379C9BcA8A67";
export const RoyaltyModule = "0xEa6eD700b11DfF703665CCAF55887ca56134Ae3B";
export const RoyaltyPolicyLAP = "0x28b4F70ffE5ba7A26aEF979226f77Eb57fb9Fdb6";
export const RoyaltyPolicyLRP = "0x7D2d9af4E4ab14Afcfd86436BC348928B40963Dd";

// Mock ERC721 contract address
export const MockERC721 = process.env.DEVNET_ERC721 as string;
24 changes: 24 additions & 0 deletions test/hardhat/e2e/grouping/group.ipa.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Test: Group IP Asset

import "../setup"
tyitang marked this conversation as resolved.
Show resolved Hide resolved
import { expect } from "chai"
import { EvenSplitGroupPool } from "../constants"

describe("Group IPA", function () {
it("Register Group IPA with whitelisted group pool", async function () {

const groupId = await expect(
this.groupingModule.registerGroup(EvenSplitGroupPool)
).not.to.be.rejectedWith(Error).then((tx) => tx.wait()).then((receipt) => receipt.logs[5].args[0]);

console.log("groupId", groupId)
expect(groupId).to.be.properHex(40);
});

it("Register Group IPA with non-whitelisted group pool", async function () {
const nonWhitelistedGroupPool = "0xC384B56fD62d6679Cd62A2fE0dA3fe4560f33300"
await expect(
this.groupingModule.registerGroup(nonWhitelistedGroupPool)
).to.be.rejectedWith(Error)
});
});
65 changes: 65 additions & 0 deletions test/hardhat/e2e/ipa/ipa.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Test: IP Asset

import "../setup"
tyitang marked this conversation as resolved.
Show resolved Hide resolved
import { expect } from "chai"
import { mintNFT } from "../utils/nftHelper"
import hre from "hardhat";
import { MockERC721 } from "../constants";

describe("IP Asset", function () {
let signers:any;

this.beforeAll("Get Signers", async function () {
// Get the signers
signers = await hre.ethers.getSigners();
})

it("NFT owner register IP Asset with an NFT token", async function () {
const tokenId = await mintNFT(signers[0]);
const connectedRegistry = this.ipAssetRegistry.connect(signers[0]);

const ipId = await expect(
connectedRegistry.register(this.chainId, MockERC721, tokenId)
).not.to.be.rejectedWith(Error).then((tx) => tx.wait()).then((receipt) => receipt.logs[2].args[0]);
console.log("ipId:", ipId);

expect(ipId).to.not.be.empty.and.to.be.a("HexString");

const isRegistered = await expect(
connectedRegistry.isRegistered(ipId)
).not.to.be.rejectedWith(Error);

expect(isRegistered).to.equal(true);
});

it("Non-NFT owner register IP asset with an NFT token", async function () {
const tokenId = await mintNFT(signers[0]);
const connectedRegistry = this.ipAssetRegistry.connect(signers[1]);

const ipId = await expect(
connectedRegistry.register(this.chainId, MockERC721, tokenId)
).not.to.be.rejectedWith(Error).then((tx) => tx.wait()).then((receipt) => receipt.logs[2].args[0]);
console.log("ipId:", ipId);

expect(ipId).to.not.be.empty.and.to.be.a("HexString");

const isRegistered = await expect(
connectedRegistry.isRegistered(ipId)
).not.to.be.rejectedWith(Error);

expect(isRegistered).to.equal(true);
});

it("Register IP asset, the caller doesn’t have enough IP token", async function () {
const tokenId = await mintNFT(signers[0]);

// generate random wallet
const randomWallet = hre.ethers.Wallet.createRandom();
const randomSigner = randomWallet.connect(hre.ethers.provider);
const connectedRegistry = this.ipAssetRegistry.connect(randomSigner);

await expect(
connectedRegistry.register(this.chainId, MockERC721, tokenId)
).to.be.rejectedWith(`insufficient funds`, `"code": -32000, "message": "insufficient funds for gas * price + value: balance 0`);
});
});
23 changes: 23 additions & 0 deletions test/hardhat/e2e/licenseTermsTemplate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// This file is used to define the terms of a license that will be used in the tests.

import hre from "hardhat";

export const terms = {
transferable: true,
royaltyPolicy: hre.ethers.ZeroAddress,
defaultMintingFee: 0,
expiration: 0,
commercialUse: false,
commercialAttribution: false,
commercializerChecker: hre.ethers.ZeroAddress,
commercializerCheckerData: hre.ethers.ZeroAddress,
commercialRevShare: 0,
commercialRevCeiling: 0,
derivativesAllowed: true,
derivativesAttribution: false,
derivativesApproval: false,
derivativesReciprocal: false,
derivativeRevCeiling: 0,
currency: hre.ethers.ZeroAddress,
uri: "",
};
56 changes: 56 additions & 0 deletions test/hardhat/e2e/permission/permission.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Test: Permission

import "../setup"
import { expect } from "chai"
import { mintNFT } from "../utils/nftHelper"
import hre from "hardhat";
import { LicensingModule, MockERC721 } from "../constants";

describe("Permission", function () {
let signers:any;

this.beforeAll("Get Signers", async function () {
// Get the signers
signers = await hre.ethers.getSigners();
console.log("signers:", signers[0].address);
})

it("Add a new ALLOW permission of IP asset for an signer and change the permission to DENY", async function () {
const tokenId = await mintNFT(signers[0].address);
const connectedRegistry = this.ipAssetRegistry.connect(signers[0]);
const func = hre.ethers.encodeBytes32String("attachLicenseTerms").slice(0, 10);
const ALLOW_permission = 1;
const DENY_permission = 2;
let permissionAfter: number;
let result: any;

const ipId = await expect(
connectedRegistry.register(this.chainId, MockERC721, tokenId)
).not.to.be.rejectedWith(Error).then((tx) => tx.wait()).then((receipt) => receipt.logs[2].args[0]);
console.log("ipId:", ipId);
expect(ipId).to.not.be.empty.and.to.be.a("HexString");

const connecedAccessController = this.accessController.connect(signers[0]);

const permissionBefore = await connecedAccessController.getPermission(ipId, signers[0].address, LicensingModule, func);
expect(permissionBefore).to.equal(0);

// add ALLOW permission
result = await connecedAccessController.setPermission(ipId, signers[0].address, LicensingModule, func, ALLOW_permission);
expect(result.hash).to.not.be.empty.and.to.be.a("HexString");
await result.wait();

// check the permission
permissionAfter = await connecedAccessController.getPermission(ipId, signers[0].address, LicensingModule, func);
expect(permissionAfter).to.equal(ALLOW_permission);

// Change to DENY permission
result = await connecedAccessController.setPermission(ipId, signers[0].address, LicensingModule, func, DENY_permission);
expect(result.hash).to.not.be.empty.and.to.be.a("HexString");
await result.wait();

// check the permission
permissionAfter = await connecedAccessController.getPermission(ipId, signers[0].address, LicensingModule, func);
expect(permissionAfter).to.equal(DENY_permission);
});
});
51 changes: 51 additions & 0 deletions test/hardhat/e2e/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// This file is a root hook used to setup preconditions before running the tests.

import hre from "hardhat";
import { network } from "hardhat";
import { GroupingModule, IPAssetRegistry, LicenseRegistry, LicenseToken, LicensingModule, PILicenseTemplate, RoyaltyPolicyLAP, MockERC20, RoyaltyPolicyLRP, AccessController } from "./constants";
import { terms } from "./licenseTermsTemplate";

before(async function () {
console.log(`================= Load Contract =================`);
this.ipAssetRegistry = await hre.ethers.getContractAt("IPAssetRegistry", IPAssetRegistry);
this.licenseRegistry = await hre.ethers.getContractAt("LicenseRegistry", LicenseRegistry);
this.licenseToken = await hre.ethers.getContractAt("LicenseToken", LicenseToken);
this.licensingModule = await hre.ethers.getContractAt("LicensingModule", LicensingModule);
this.groupingModule = await hre.ethers.getContractAt("GroupingModule", GroupingModule);
this.licenseTemplate = await hre.ethers.getContractAt("PILicenseTemplate", PILicenseTemplate);
this.accessController = await hre.ethers.getContractAt("AccessController", AccessController);

console.log(`================= Load Users =================`);
[this.owner, this.user1] = await hre.ethers.getSigners();

console.log(`================= Chain ID =================`);
const networkConfig = network.config;
this.chainId = networkConfig.chainId;
console.log("chainId: ", this.chainId);

console.log(`================= Register non-commercial PIL license terms =================`);
await this.licenseTemplate.registerLicenseTerms(terms);
this.nonCommericialLicenseId = await this.licenseTemplate.getLicenseTermsId(terms);
console.log("Non-commercial licenseTermsId: ", this.nonCommericialLicenseId);

console.log(`================= Register commercial-use PIL license terms =================`);
let testTerms = terms;
testTerms.royaltyPolicy = RoyaltyPolicyLAP;
testTerms.defaultMintingFee = 30;
testTerms.commercialUse = true;
testTerms.currency = MockERC20;
await this.licenseTemplate.registerLicenseTerms(testTerms);
this.commericialUseLicenseId = await this.licenseTemplate.getLicenseTermsId(testTerms);
console.log("Commercial-use licenseTermsId: ", this.commericialUseLicenseId);

console.log(`================= Register commercial-remix PIL license terms =================`);
testTerms = terms;
testTerms.royaltyPolicy = RoyaltyPolicyLRP;
testTerms.defaultMintingFee = 80;
testTerms.commercialUse = true;
testTerms.commercialRevShare = 100;
testTerms.currency = MockERC20;
await this.licenseTemplate.registerLicenseTerms(testTerms);
this.commericialRemixLicenseId = await this.licenseTemplate.getLicenseTermsId(testTerms);
console.log("Commercial-remix licenseTermsId: ", this.commericialRemixLicenseId);
});
Loading
Loading