From 083a12276e0732383b7a57b143471b746c47730f Mon Sep 17 00:00:00 2001 From: zajck Date: Mon, 8 Jan 2024 11:47:10 +0100 Subject: [PATCH] Test revert reasons --- contracts/mock/MockDisputeHandlerFacet.sol | 23 +++++++++++ contracts/mock/MockExchangeHandlerFacet.sol | 22 ++++++++++ scripts/config/revert-reasons.js | 2 + test/protocol/DisputeHandlerTest.js | 45 ++++++++++++++++++++- test/protocol/ExchangeHandlerTest.js | 41 +++++++++++++++++++ 5 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 contracts/mock/MockDisputeHandlerFacet.sol diff --git a/contracts/mock/MockDisputeHandlerFacet.sol b/contracts/mock/MockDisputeHandlerFacet.sol new file mode 100644 index 000000000..00fc7068a --- /dev/null +++ b/contracts/mock/MockDisputeHandlerFacet.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.22; + +import { DisputeHandlerFacet } from "../protocol/facets/DisputeHandlerFacet.sol"; + +/** + * @title TestDisputeHandlerFacet + * + * @notice Extended DisputeHandlerFacet with additional external functions for testing + */ +contract TestDisputeHandlerFacet is DisputeHandlerFacet { + /** + * @notice Test function to test invalid final dispute state + * + * @param _exchangeId - the id of the associated exchange + * @param _targetState - target final state + */ + function finalizeDispute(uint256 _exchangeId, DisputeState _targetState) external { + (, Exchange storage exchange) = fetchExchange(_exchangeId); + (, Dispute storage dispute, DisputeDates storage disputeDates) = fetchDispute(_exchangeId); + finalizeDispute(_exchangeId, exchange, dispute, disputeDates, _targetState, 1000); + } +} diff --git a/contracts/mock/MockExchangeHandlerFacet.sol b/contracts/mock/MockExchangeHandlerFacet.sol index 289746124..a4e508745 100644 --- a/contracts/mock/MockExchangeHandlerFacet.sol +++ b/contracts/mock/MockExchangeHandlerFacet.sol @@ -7,6 +7,7 @@ import { DisputeBase } from "../protocol/bases/DisputeBase.sol"; import { FundsLib } from "../protocol/libs/FundsLib.sol"; import "../domain/BosonConstants.sol"; import "@openzeppelin/contracts/utils/Address.sol"; +import { ExchangeHandlerFacet } from "../protocol/facets/ExchangeHandlerFacet.sol"; /** * @title MockExchangeHandlerFacet @@ -537,3 +538,24 @@ contract MockExchangeHandlerFacetWithDefect is MockExchangeHandlerFacet { emit VoucherCanceled2(exchange.offerId, _exchangeId, msgSender()); } } + +/** + * @title TestExchangeHandlerFacet + * + * @notice Extended ExchangeHandlerFacet with additional external functions for testing + */ +contract TestExchangeHandlerFacet is ExchangeHandlerFacet { + //solhint-disable-next-line + constructor(uint256 _firstExchangeId2_2_0) ExchangeHandlerFacet(_firstExchangeId2_2_0) {} + + /** + * @notice Test function to test invalid final exchange state + * + * @param _exchangeId - the id of the exchange to finalize + * @param _targetState - the target state to which the exchange should be transitioned + */ + function finalizeExchange(uint256 _exchangeId, ExchangeState _targetState) external { + (, Exchange storage exchange) = fetchExchange(_exchangeId); + finalizeExchange(exchange, _targetState); + } +} diff --git a/scripts/config/revert-reasons.js b/scripts/config/revert-reasons.js index f8bb341a7..086a61bcd 100644 --- a/scripts/config/revert-reasons.js +++ b/scripts/config/revert-reasons.js @@ -134,6 +134,7 @@ exports.RevertReasons = { EXCHANGE_IS_NOT_IN_A_FINAL_STATE: "ExchangeIsNotInAFinalState", INVALID_RANGE_LENGTH: "InvalidRangeLength", EXCHANGE_ALREADY_EXISTS: "ExchangeAlreadyExists", + INVALID_TARGET_EXCHANGE_STATE: "InvalidTargeExchangeState", // Voucher related EXCHANGE_ID_IN_RESERVED_RANGE: "ExchangeIdInReservedRange", @@ -190,6 +191,7 @@ exports.RevertReasons = { DISPUTE_STILL_VALID: "DisputeStillValid", INVALID_DISPUTE_TIMEOUT: "InvalidDisputeTimeout", ESCALATION_NOT_ALLOWED: "EscalationNotAllowed", + INVALID_TARGET_DISPUTE_STATE: "InvalidTargeDisputeState", // Config related FEE_PERCENTAGE_INVALID: "InvalidFeePercentage", diff --git a/test/protocol/DisputeHandlerTest.js b/test/protocol/DisputeHandlerTest.js index abfe2c01d..105ec6e2a 100644 --- a/test/protocol/DisputeHandlerTest.js +++ b/test/protocol/DisputeHandlerTest.js @@ -1,5 +1,5 @@ const { ethers } = require("hardhat"); -const { ZeroAddress, provider, zeroPadBytes, MaxUint256, parseUnits, getContractAt } = ethers; +const { ZeroAddress, provider, zeroPadBytes, MaxUint256, parseUnits, getContractAt, id } = ethers; const { expect, assert } = require("chai"); const Exchange = require("../../scripts/domain/Exchange"); const Dispute = require("../../scripts/domain/Dispute"); @@ -29,6 +29,7 @@ const { mockBuyer, accountId, } = require("../util/mock"); +const { FacetCutAction } = require("../../scripts/util/diamond-utils.js"); /** * Test the Boson Dispute Handler interface @@ -2580,4 +2581,46 @@ describe("IBosonDisputeHandler", function () { }); }); }); + + // Internal functions, tested with TestDisputeHandlerFacet + context("📋 Internal Dispute Handler Methods", async function () { + let testDisputeHandler; + beforeEach(async function () { + // Deploy test facet and cut the test functions + const TestDisputeHandlerFacet = await ethers.getContractFactory("TestDisputeHandlerFacet"); + const testDisputeHandlerFacet = await TestDisputeHandlerFacet.deploy(); + await testDisputeHandlerFacet.waitForDeployment(); + + const protocolDiamondAddress = await disputeHandler.getAddress(); + const cutFacetViaDiamond = await getContractAt("DiamondCutFacet", protocolDiamondAddress); + + // Define the facet cut + const facetCuts = [ + { + facetAddress: await testDisputeHandlerFacet.getAddress(), + action: FacetCutAction.Add, + functionSelectors: [id("finalizeDispute(uint256,uint8)").slice(0, 10)], + }, + ]; + + // Send the DiamondCut transaction + await cutFacetViaDiamond.diamondCut(facetCuts, ZeroAddress, "0x"); + + testDisputeHandler = await getContractAt("TestDisputeHandlerFacet", protocolDiamondAddress); + }); + + context("👉 finalizeDispute()", async function () { + const invalidFinalStates = ["Resolving", "Escalated"]; + + invalidFinalStates.forEach((finalState) => { + it(`final state is ${finalState}`, async function () { + const exchangeId = 1; + + await expect( + testDisputeHandler.finalizeDispute(exchangeId, DisputeState[finalState]) + ).to.revertedWithCustomError(bosonErrors, RevertReasons.INVALID_TARGET_DISPUTE_STATE); + }); + }); + }); + }); }); diff --git a/test/protocol/ExchangeHandlerTest.js b/test/protocol/ExchangeHandlerTest.js index 613a053e6..595dbba01 100644 --- a/test/protocol/ExchangeHandlerTest.js +++ b/test/protocol/ExchangeHandlerTest.js @@ -8132,4 +8132,45 @@ describe("IBosonExchangeHandler", function () { }); }); }); + + // Internal functions, tested with TestExchangeHandlerFacet + context("📋 Internal Exchange Handler Methods", async function () { + let testExchangeHandler; + beforeEach(async function () { + // Deploy test facet and cut the test functions + const TestExchangeHandlerFacet = await ethers.getContractFactory("TestExchangeHandlerFacet"); + const testExchangeHandlerFacet = await TestExchangeHandlerFacet.deploy(0); + await testExchangeHandlerFacet.waitForDeployment(); + + const cutFacetViaDiamond = await getContractAt("DiamondCutFacet", protocolDiamondAddress); + + // Define the facet cut + const facetCuts = [ + { + facetAddress: await testExchangeHandlerFacet.getAddress(), + action: FacetCutAction.Add, + functionSelectors: [id("finalizeExchange(uint256,uint8)").slice(0, 10)], + }, + ]; + + // Send the DiamondCut transaction + await cutFacetViaDiamond.connect(deployer).diamondCut(facetCuts, ZeroAddress, "0x"); + + testExchangeHandler = await getContractAt("TestExchangeHandlerFacet", protocolDiamondAddress); + }); + + context("👉 finalizeExchange()", async function () { + const invalidFinalStates = ["Committed", "Redeemed", "Disputed"]; + + invalidFinalStates.forEach((finalState) => { + it(`final state is ${finalState}`, async function () { + const exchangeId = 1; + + await expect( + testExchangeHandler.finalizeExchange(exchangeId, ExchangeState[finalState]) + ).to.revertedWithCustomError(bosonErrors, RevertReasons.INVALID_TARGET_EXCHANGE_STATE); + }); + }); + }); + }); });