Skip to content

Commit

Permalink
generic tests
Browse files Browse the repository at this point in the history
  • Loading branch information
zajck committed Jun 12, 2023
1 parent 3967780 commit 77538a9
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 1 deletion.
12 changes: 12 additions & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ module.exports = {
},
solidity: {
compilers: [
{
version: "0.8.9",
settings: {
optimizer: {
enabled: true,
runs: 200,
details: {
yul: true,
},
},
},
},
{
version: "0.8.18",
settings: {
Expand Down
8 changes: 7 additions & 1 deletion test/upgrade/00_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ async function getFacets() {
"v2.0.0": v2_0_0,
"v2.1.0": v2_0_0, // same as v2.0.0
"v2.2.0": v2_2_0,
"v2.2.1": v2_2_0, // same as v2.2.0
},
upgrade: {
"v2.1.0": {
Expand Down Expand Up @@ -190,6 +191,7 @@ async function getFacets() {
},
initializationData: "0x0000000000000000000000000000000000000000000000000000000000002710", // input for initV2_2_0, representing maxPremintedVoucher (0x2710=10000)
},
// POST 2.2.0 upgrade configs are part of respective migration script
},
};

Expand All @@ -210,7 +212,11 @@ const tagsByVersion = {
},
"2.2.1": {
oldVersion: "v2.2.0",
newVersion: "v2.2.1-rc.1",
newVersion: "v2.2.1",
},
"2.3.0": {
oldVersion: "v2.2.1",
newVersion: "v2.3.0",
},
};

Expand Down
280 changes: 280 additions & 0 deletions test/upgrade/2.2.1-2.3.0.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
const hre = require("hardhat");
const { RevertReasons } = require("../../scripts/config/revert-reasons.js");
const ethers = hre.ethers;
const { getSnapshot, revertToSnapshot } = require("../util/utils");

// const { getStateModifyingFunctionsHashes } = require("../../scripts/util/diamond-utils.js");
const { assert, expect } = require("chai");
const DisputeResolver = require("../../scripts/domain/DisputeResolver");
const Seller = require("../../scripts/domain/Seller");
const { calculateContractAddress } = require("../util/utils.js");
const { mockSeller, mockAuthToken, mockVoucherInitValues } = require("../util/mock");

const { deploySuite, populateProtocolContract, getProtocolContractState, revertState } = require("../util/upgrade");
const { getGenericContext } = require("./01_generic");

const version = "2.3.0";
const { migrate } = require(`../../scripts/migrations/migrate_${version}.js`);

/**
* Upgrade test case - After upgrade from 2.2.1 to 2.3.0 everything is still operational
*/
describe("[@skip-on-coverage] After facet upgrade, everything is still operational", function () {
// Common vars
let deployer, rando;
let accountHandler;
let snapshot;
let protocolDiamondAddress, mockContracts;
let contractsAfter;
let protocolContractStateBefore, protocolContractStateAfter;
let removedFunctionHashes, addedFunctionHashes;

// reference protocol state
let accountContractState;
let preUpgradeEntities;

before(async function () {
try {
// Make accounts available
[deployer, rando] = await ethers.getSigners();

let contractsBefore;

({
protocolDiamondAddress,
protocolContracts: contractsBefore,
mockContracts,
} = await deploySuite(deployer, version));

// Populate protocol with data
preUpgradeEntities = await populateProtocolContract(
deployer,
protocolDiamondAddress,
contractsBefore,
mockContracts,
true
);

// Get current protocol state, which serves as the reference
// We assume that this state is a true one, relying on our unit and integration tests
protocolContractStateBefore = await getProtocolContractState(
protocolDiamondAddress,
contractsBefore,
mockContracts,
preUpgradeEntities,
true
);

({ accountContractState } = protocolContractStateBefore);

// const getFunctionHashesClosure = getStateModifyingFunctionsHashes(
// ["SellerHandlerFacet", "OrchestrationHandlerFacet1"],
// undefined,
// ["createSeller", "updateSeller"]
// );

// removedFunctionHashes = await getFunctionHashesClosure();

await migrate("upgrade-test");

// Cast to updated interface
let newHandlers = {
accountHandler: "IBosonAccountHandler",
orchestrationHandler: "IBosonOrchestrationHandler",
};

contractsAfter = { ...contractsBefore };

for (const [handlerName, interfaceName] of Object.entries(newHandlers)) {
contractsAfter[handlerName] = await ethers.getContractAt(interfaceName, protocolDiamondAddress);
}

({ accountHandler } = contractsAfter);

// addedFunctionHashes = await getFunctionHashesClosure();

snapshot = await getSnapshot();

// const includeTests = [
// "accountContractState",
// "offerContractState",
// "exchangeContractState",
// "bundleContractState",
// "configContractState",
// "disputeContractState",
// "fundsContractState",
// "groupContractState",
// "twinContractState",
// "protocolStatusPrivateContractState",
// "protocolLookupsPrivateContractState",
// ];

// Get protocol state after the upgrade
protocolContractStateAfter = await getProtocolContractState(
protocolDiamondAddress,
contractsAfter,
mockContracts,
preUpgradeEntities
);

// This context is placed in an uncommon place due to order of test execution.
// Generic context needs values that are set in "before", however "before" is executed before tests, not before suites
// and those values are undefined if this is placed outside "before".
// Normally, this would be solved with mocha's --delay option, but it does not behave as expected when running with hardhat.
context(
"Generic tests",
getGenericContext(
deployer,
protocolDiamondAddress,
contractsBefore,
contractsAfter,
mockContracts,
protocolContractStateBefore,
protocolContractStateAfter,
preUpgradeEntities,
snapshot
// includeTests
)
);
} catch (err) {
// revert to latest version of scripts and contracts
revertState();
// stop execution
assert(false, `Before all reverts with: ${err}`);
}
});

afterEach(async function () {
// Revert to state right after the upgrade.
// This is used so the lengthy setup (deploy+upgrade) is done only once.
await revertToSnapshot(snapshot);
snapshot = await getSnapshot();
});

after(async function () {
revertState();
});

// To this test pass package.json version must be set
it(`Protocol status version is updated to ${version}`, async function () {
// Slice because of unicode escape notation
expect((await contractsAfter.protocolInitializationHandler.getVersion()).replace(/\0/g, "")).to.equal(version);
});

// Test actions that worked in previous version, but should not work anymore, or work differently
// Test methods that were added to see that upgrade was succesful
context.skip("📋 Breaking changes, new methods and bug fixes", async function () {
context("Breaking changes", async function () {
context("DisputeResolverHandlerFacet", async function () {
it("updateDisputeResolver reverts if no update field has been updated or requested to be updated", async function () {
const { DRs } = preUpgradeEntities;
const { wallet, id, disputeResolver } = DRs[0];

// Try to update with same values, should revert
await expect(accountHandler.connect(wallet).updateDisputeResolver(disputeResolver)).to.be.revertedWith(
RevertReasons.NO_UPDATE_APPLIED
);

// Validate if DR data is still the same
let [, disputeResolverAfter] = await accountHandler.getDisputeResolver(id);
disputeResolverAfter = DisputeResolver.fromStruct(disputeResolverAfter);
expect(disputeResolverAfter).to.deep.equal(disputeResolver);
});
});

context("SellerHandlerFacet", async function () {
it("updateSeller reverts if no update field has been updated or requested to be updated", async function () {
const { sellers } = preUpgradeEntities;
const { wallet, id, seller, authToken } = sellers[0];

// Try to update with same values, should revert
await expect(accountHandler.connect(wallet).updateSeller(seller, authToken)).to.be.revertedWith(
RevertReasons.NO_UPDATE_APPLIED
);

// Validate if seller data is still the same
let [, sellerAfter] = await accountHandler.getSeller(id);
sellerAfter = Seller.fromStruct(sellerAfter);
expect(sellerAfter).to.deep.equal(seller);
});

it("Old seller can update and add metadataUri field", async function () {
const { sellers } = preUpgradeEntities;
const { wallet, id, seller, authToken } = sellers[0];

seller.metadataUri = "metadata";

const tx = await accountHandler.connect(wallet).updateSeller(seller, authToken);
expect(tx).to.emit("SellerUpdateApplied");

// Validate if seller now has metadataUri
let [, sellerAfter] = await accountHandler.getSeller(id);
sellerAfter = DisputeResolver.fromStruct(sellerAfter);
expect(sellerAfter.metadataUri).to.equal(seller.metadataUri);
});

it("New seller has metadataUri field", async function () {
const { nextAccountId } = accountContractState;
const seller = mockSeller(rando.address, rando.address, rando.address, rando.address, true, "metadata");
const authToken = mockAuthToken();
const voucherInitValues = mockVoucherInitValues();

const tx = await accountHandler.connect(rando).createSeller(seller, authToken, voucherInitValues);
expect(tx)
.to.emit("SellerCreated")
.withArgs(
nextAccountId,
seller,
calculateContractAddress(accountHandler.address, nextAccountId),
authToken,
rando.address
);
});
});

context("MetaTransactionHandlerfacet", async function () {
it("Function hashes from removedFunctionsHashes list should not be allowlisted", async function () {
for (const hash of removedFunctionHashes) {
const [isAllowed] = await contractsAfter.metaTransactionsHandler.functions[
"isFunctionAllowlisted(bytes32)"
](hash);
expect(isAllowed).to.be.false;
}
});

it("Function hashes from from addedFunctionsHashes list should be allowlisted", async function () {
for (const hash of addedFunctionHashes) {
const [isAllowed] = await contractsAfter.metaTransactionsHandler.functions[
"isFunctionAllowlisted(bytes32)"
](hash);
expect(isAllowed).to.be.true;
}
});

it("State of metaTxPrivateContractState is not affected besides isAllowlistedState mapping", async function () {
// make a shallow copy to not modify original protocolContractState as it's used on getGenericContext
const metaTxPrivateContractStateBefore = { ...protocolContractStateBefore.metaTxPrivateContractState };
const metaTxPrivateContractStateAfter = { ...protocolContractStateAfter.metaTxPrivateContractState };

const { isAllowlistedState: isAllowlistedStateBefore } = metaTxPrivateContractStateBefore;
removedFunctionHashes.forEach((hash) => {
delete isAllowlistedStateBefore[hash];
});

const { isAllowlistedState: isAllowlistedStateAfter } = metaTxPrivateContractStateAfter;
addedFunctionHashes.forEach((hash) => {
delete isAllowlistedStateAfter[hash];
});

delete metaTxPrivateContractStateBefore.isAllowlistedState;
delete metaTxPrivateContractStateAfter.isAllowlistedState;

expect(isAllowlistedStateAfter).to.deep.equal(isAllowlistedStateBefore);
expect(protocolContractStateAfter.metaTxPrivateContractState).to.deep.equal(
protocolContractStateBefore.metaTxPrivateContractState
);
});
});
});
});
});
11 changes: 11 additions & 0 deletions test/util/upgrade.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ async function deploySuite(deployer, newVersion) {
shell.exec(`rm -rf scripts/*`);
shell.exec(`git checkout ${scriptsTag} scripts`);
}
const isOldOZVersion = ["v2.0", "v2.1", "v2.2"].some((v) => tag.startsWith(v));
if (isOldOZVersion) {
// Temporary install old OZ contracts
shell.exec("npm i @openzeppelin/contracts-upgradeable@4.7.1");
}

const deployConfig = facets.deploy[tag];

Expand Down Expand Up @@ -148,6 +153,12 @@ async function deploySuite(deployer, newVersion) {
await deployMockTokens(["Foreign20", "Foreign20", "Foreign721", "Foreign721", "Foreign20", "Foreign1155"]);
const mockTwinTokens = [mockTwin721_1, mockTwin721_2];

if (isOldOZVersion) {
// If reference commit is old version, we need to revert to target version
shell.exec(`git checkout ${versionTags.newVersion} package.json package-lock.json`);
shell.exec("npm i");
}

return {
protocolDiamondAddress,
protocolContracts: {
Expand Down

0 comments on commit 77538a9

Please sign in to comment.