Skip to content

Commit

Permalink
test(cancel-invoice): add basic integration 'cancelInvoice' tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielstoica committed Jul 19, 2024
1 parent bdd8015 commit b2594c5
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { CancelInvoice_Integration_Shared_Test } from "../../../shared/cancelInvoice.t.sol";
import { Types } from "./../../../../../src/modules/invoice-module/libraries/Types.sol";
import { Events } from "../../../../utils/Events.sol";
import { Errors } from "../../../../utils/Errors.sol";

contract CancelInvoice_Integration_Concret_Test is CancelInvoice_Integration_Shared_Test {
function setUp() public virtual override {
CancelInvoice_Integration_Shared_Test.setUp();
}

function test_RevertWhen_InvoiceIsPaid() external {
// Set the one-off ETH transfer invoice as current one
uint256 invoiceId = 1;

// Make Bob the payer for the default invoice
vm.startPrank({ msgSender: users.bob });

// Pay the invoice first
invoiceModule.payInvoice{ value: invoices[invoiceId].payment.amount }({ id: invoiceId });

// Make Eve the caller who is the recipient of the invoice
vm.startPrank({ msgSender: users.eve });

// Expect the call to revert with the {CannotCancelPaidInvoice} error
vm.expectRevert(Errors.CannotCancelPaidInvoice.selector);

// Run the test
invoiceModule.cancelInvoice({ id: invoiceId });
}

function test_RevertWhen_InvoiceIsCanceled() external whenInvoiceNotAlreadyPaid {
// Set the one-off ETH transfer invoice as current one
uint256 invoiceId = 1;

// Make Eve the caller who is the recipient of the invoice
vm.startPrank({ msgSender: users.eve });

// Cancel the invoice first
invoiceModule.cancelInvoice({ id: invoiceId });

// Expect the call to revert with the {InvoiceAlreadyCanceled} error
vm.expectRevert(Errors.InvoiceAlreadyCanceled.selector);

// Run the test
invoiceModule.cancelInvoice({ id: invoiceId });
}

function test_RevertWhen_PaymentMethodTransfer_SenderNotInvoiceRecipient()
external
whenInvoiceNotAlreadyPaid
whenInvoiceNotCanceled
givenPaymentMethodTransfer
{
// Set the one-off ETH transfer invoice as current one
uint256 invoiceId = 1;

// Make Bob the caller who IS NOT the recipient of the invoice
vm.startPrank({ msgSender: users.bob });

// Expect the call to revert with the {InvoiceOwnerUnauthorized} error
vm.expectRevert(Errors.InvoiceOwnerUnauthorized.selector);

// Run the test
invoiceModule.cancelInvoice({ id: invoiceId });
}

function test_CancelInvoice_PaymentMethodTransfer()
external
whenInvoiceNotAlreadyPaid
whenInvoiceNotCanceled
givenPaymentMethodTransfer
whenSenderInvoiceRecipient
{
// Set the one-off ETH transfer invoice as current one
uint256 invoiceId = 1;

// Make Eve the caller who is the recipient of the invoice
vm.startPrank({ msgSender: users.eve });

// Expect the {InvoiceCanceled} event to be emitted
vm.expectEmit();
emit Events.InvoiceCanceled({ id: invoiceId });

// Run the test
invoiceModule.cancelInvoice({ id: invoiceId });

// Assert the actual and expected invoice status
Types.Invoice memory invoice = invoiceModule.getInvoice({ id: invoiceId });
assertEq(uint8(invoice.status), uint8(Types.Status.Canceled));
}

function test_RevertWhen_PaymentMethodLinearStream_StatusPending_SenderNotInvoiceRecipient()
external
whenInvoiceNotAlreadyPaid
whenInvoiceNotCanceled
givenPaymentMethodLinearStream
givenInvoiceStatusPending
{
// Set current invoice as a linear stream-based one
uint256 invoiceId = 3;

// Make Bob the caller who IS NOT the recipient of the invoice
vm.startPrank({ msgSender: users.bob });

// Expect the call to revert with the {InvoiceOwnerUnauthorized} error
vm.expectRevert(Errors.InvoiceOwnerUnauthorized.selector);

// Run the test
invoiceModule.cancelInvoice({ id: invoiceId });
}

function test_CancelInvoice_PaymentMethodLinearStream_StatusPending()
external
whenInvoiceNotAlreadyPaid
whenInvoiceNotCanceled
givenPaymentMethodLinearStream
givenInvoiceStatusPending
whenSenderInvoiceRecipient
{
// Set current invoice as a linear stream-based one
uint256 invoiceId = 3;

// Make Eve the caller who is the recipient of the invoice
vm.startPrank({ msgSender: users.eve });

// Expect the {InvoiceCanceled} event to be emitted
vm.expectEmit();
emit Events.InvoiceCanceled({ id: invoiceId });

// Run the test
invoiceModule.cancelInvoice({ id: invoiceId });

// Assert the actual and expected invoice status
Types.Invoice memory invoice = invoiceModule.getInvoice({ id: invoiceId });
assertEq(uint8(invoice.status), uint8(Types.Status.Canceled));
}

function test_RevertWhen_PaymentMethodLinearStream_StatusOngoing_SenderNotStreamSender()
external
whenInvoiceNotAlreadyPaid
whenInvoiceNotCanceled
givenPaymentMethodLinearStream
givenInvoiceStatusOngoing
{
// Set current invoice as a linear stream-based one
uint256 invoiceId = 3;

// The invoice must be paid for its status to be updated to `Ongoing`
// Make Bob the payer of the invoice (also Bob will be the stream sender)
vm.startPrank({ msgSender: users.bob });

// Approve the {InvoiceModule} to transfer the USDT tokens on Bob's behalf
usdt.approve({ spender: address(invoiceModule), amount: invoices[invoiceId].payment.amount });

// Pay the invoice first (status will be updated to `Ongoing`)
invoiceModule.payInvoice{ value: invoices[invoiceId].payment.amount }({ id: invoiceId });

// Make Eve the caller who IS NOT the stream sender
vm.startPrank({ msgSender: users.eve });

// Expect the call to revert with the {SablierV2Lockup_Unauthorized} error
vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2Lockup_Unauthorized.selector, 1, users.bob));

// Run the test
invoiceModule.cancelInvoice({ id: invoiceId });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
cancelInvoice.t.sol
├── when the invoice status IS Paid
│ └── it should revert with the {CannotCancelPaidInvoice} error
└── when the invoice status IS NOT Paid
├── when the invoice status IS Canceled
│ └── it should revert with the {InvoiceAlreadyCanceled} error
└── when the invoice status IS NOT Canceled
├── given the payment method is transfer
│ ├── when the sender IS NOT the invoice recipient
│ │ └── it should revert with the {InvoiceOwnerUnauthorized}
│ └── when the sender IS the invoice recipient
│ ├── it should mark the invoice as Canceled
│ └── it should emit an {InvoiceCanceled} event
├── given the payment method is linear stream-based
│ ├── given the invoice status is Pending
│ │ ├── when the sender IS NOT the invoice recipient
│ │ │ └── it should revert with the {InvoiceOwnerUnauthorized}
│ │ └── when the sender IS the invoice recipient
│ │ ├── it should mark the invoice as Canceled
│ │ └── it should emit an {InvoiceCanceled} event
│ └── given the invoice status is Ongoing
│ ├── when the sender IS NOT the stream's sender
│ │ └── it should revert with the {SablierV2Lockup_Unauthorized} error
│ └── when the sender IS the stream's sender
│ ├── it should mark the invoice as Canceled
│ └── it should emit an {InvoiceCanceled} event
└── given the payment method is tranched stream-based
├── given the invoice status is Pending
│ ├── when the sender IS NOT the invoice recipient
│ │ └── it should revert with the {InvoiceOwnerUnauthorized}
│ └── when the sender IS the invoice recipient
│ ├── it should mark the invoice as Canceled
│ └── it should emit an {InvoiceCanceled} event
└── given the invoice status is Ongoing
├── when the sender IS NOT the stream's sender
│ └──it should revert with the {SablierV2Lockup_Unauthorized} error
└── when the sender IS the stream's sender
├── it should mark the invoice as Canceled
└── it should emit an {InvoiceCanceled} event

35 changes: 35 additions & 0 deletions test/integration/shared/cancelInvoice.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { Integration_Test } from "../Integration.t.sol";
import { PayInvoice_Integration_Shared_Test } from "./payInvoice.t.sol";

abstract contract CancelInvoice_Integration_Shared_Test is Integration_Test, PayInvoice_Integration_Shared_Test {
function setUp() public virtual override(Integration_Test, PayInvoice_Integration_Shared_Test) {
PayInvoice_Integration_Shared_Test.setUp();
}

modifier whenInvoiceStatusNotPaid() {
_;
}

modifier whenInvoiceStatusNotCanceled() {
_;
}

modifier whenSenderInvoiceRecipient() {
_;
}

modifier givenInvoiceStatusPending() {
_;
}

modifier givenInvoiceStatusOngoing() {
_;
}

modifier whenSenderStreamSender() {
_;
}
}
9 changes: 9 additions & 0 deletions test/utils/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,19 @@ library Errors {
/// @notice Thrown when a tranched stream has a one-off recurrence type
error TranchedStreamInvalidOneOffRecurence();

/// @notice Thrown when an attempt is made to cancel an already paid invoice
error CannotCancelPaidInvoice();

/// @notice Thrown when an attempt is made to cancel an already canceled invoice
error InvoiceAlreadyCanceled();

/*//////////////////////////////////////////////////////////////////////////
STREAM-MANAGER
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the caller is not the broker admin
error OnlyBrokerAdmin();

/// @notice Thrown when `msg.sender` is not the stream's sender
error SablierV2Lockup_Unauthorized(uint256 streamId, address caller);
}

0 comments on commit b2594c5

Please sign in to comment.