-
-
Notifications
You must be signed in to change notification settings - Fork 37
test: add failure-case coverage for Chainvoice invoices #142
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
base: main
Are you sure you want to change the base?
Changes from all commits
cf616d6
fcd723f
60512ff
10b959e
c23f639
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ import {Test} from "forge-std/Test.sol"; | |
| import {console} from "forge-std/console.sol"; | ||
| import "../src/Chainvoice.sol"; | ||
|
|
||
|
|
||
| contract ChainvoiceTest is Test { | ||
| Chainvoice chainvoice; | ||
|
|
||
|
|
@@ -51,6 +52,7 @@ contract ChainvoiceTest is Test { | |
| assertFalse(inv.isCancelled); | ||
| } | ||
|
|
||
|
|
||
| /* ------------------------------------------------------------ */ | ||
| /* PAY INVOICE */ | ||
| /* ------------------------------------------------------------ */ | ||
|
|
@@ -93,95 +95,90 @@ contract ChainvoiceTest is Test { | |
| } | ||
|
|
||
| /* ------------------------------------------------------------ */ | ||
| /* FAILURE CASES */ | ||
| /* FAILURE CASES - CREATE INVOICE */ | ||
| /* ------------------------------------------------------------ */ | ||
|
|
||
| function testCreateInvoiceRevertIfInvalidAddress() public{ | ||
| vm.prank(alice); | ||
| vm.expectRevert("Recipient address is zero"); | ||
| chainvoice.createInvoice(address(0), 1 ether, address(0), "data", "hash"); | ||
| } | ||
|
|
||
| function testCreateInvoiceRevertIfSelfInvoicing() public{ | ||
| vm.prank(alice); | ||
| vm.expectRevert("Self-invoicing not allowed"); | ||
| chainvoice.createInvoice(alice, 1 ether, address(0), "data", "hash"); | ||
| } | ||
|
|
||
| /* ------------------------------------------------------------ */ | ||
| /* FAILURE CASES - PAY INVOICE */ | ||
| /* ------------------------------------------------------------ */ | ||
|
|
||
| function testPayInvoice_RevertIfWrongPayer() public { | ||
| modifier createInvoice(){ | ||
| vm.prank(alice); | ||
| chainvoice.createInvoice(bob, 1 ether, address(0), "data", "hash"); | ||
| _; | ||
| } | ||
|
|
||
| function testPayInvoice_RevertIfWrongPayer() public createInvoice { | ||
| uint256 fee = chainvoice.fee(); | ||
| vm.expectRevert(Chainvoice.NotAuthorizedPayer.selector); | ||
| vm.prank(alice); | ||
| chainvoice.payInvoice{value: 1 ether + fee}(0); | ||
| } | ||
|
|
||
| function testPayInvoice_RevertIfIncorrectValue() public { | ||
| vm.prank(alice); | ||
| chainvoice.createInvoice(bob, 1 ether, address(0), "data", "hash"); | ||
|
|
||
| vm.expectRevert(Chainvoice.IncorrectPaymentAmount.selector); | ||
| function testPayInvoice_RevertIfIncorrectValue() public createInvoice { | ||
| uint256 fee = chainvoice.fee(); | ||
| vm.expectRevert("Incorrect payment amount"); | ||
| vm.prank(bob); | ||
| chainvoice.payInvoice{value: 1 ether}(0); | ||
| chainvoice.payInvoice{value: 1 ether + fee + 1}(0); | ||
| } | ||
|
tani404 marked this conversation as resolved.
|
||
|
|
||
| /* ------------------------------------------------------------ */ | ||
| /* OWNERSHIP MANAGEMENT */ | ||
| /* ------------------------------------------------------------ */ | ||
| function testPayInvoice_RevertIfInvalidInvoiceId() public createInvoice{ | ||
| uint256 fee = chainvoice.fee(); | ||
|
|
||
| function testInitiateOwnershipTransfer() public { | ||
| address newOwner = address(0xC0FFEE); | ||
|
|
||
| vm.prank(alice); // alice is not the owner | ||
| vm.expectRevert("Only owner can call"); | ||
| chainvoice.initiateOwnershipTransfer(newOwner); | ||
|
|
||
| vm.prank(address(this)); // this is the owner (from setUp) | ||
| chainvoice.initiateOwnershipTransfer(newOwner); | ||
|
|
||
| assertEq(chainvoice.pendingOwner(), newOwner); | ||
| vm.expectRevert("Invalid invoice ID"); | ||
| vm.prank(bob); | ||
| chainvoice.payInvoice{value: 1 ether + fee}(type(uint256).max); | ||
| } | ||
|
tani404 marked this conversation as resolved.
|
||
|
|
||
| function testInitiateOwnershipTransferInvalidAddress() public { | ||
| vm.expectRevert(Chainvoice.InvalidNewOwner.selector); | ||
| chainvoice.initiateOwnershipTransfer(address(0)); | ||
| function testPayInvoice_RevertIfInvoiceAlreadyPaid() public createInvoice{ | ||
| uint256 fee = chainvoice.fee(); | ||
|
|
||
| // Try to transfer to self | ||
| vm.expectRevert(Chainvoice.InvalidNewOwner.selector); | ||
| chainvoice.initiateOwnershipTransfer(address(this)); | ||
| } | ||
| vm.prank(bob); | ||
| chainvoice.payInvoice{value: 1 ether + fee}(0); | ||
|
|
||
| function testAcceptOwnership() public { | ||
| address newOwner = address(0xC0FFEE); | ||
|
|
||
| chainvoice.initiateOwnershipTransfer(newOwner); | ||
|
|
||
| vm.prank(newOwner); | ||
| chainvoice.acceptOwnership(); | ||
|
|
||
| assertEq(chainvoice.owner(), newOwner); | ||
| assertEq(chainvoice.pendingOwner(), address(0)); | ||
| vm.expectRevert("Already paid"); | ||
| vm.prank(bob); | ||
| chainvoice.payInvoice{value: 1 ether + fee}(0); | ||
| } | ||
|
Comment on lines
+130
to
154
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests use string reverts but contract throws custom errors — tests will fail. The
🛠️ Proposed fix: use custom error selectors function testPayInvoice_RevertIfIncorrectValue() public createInvoice {
uint256 fee = chainvoice.fee();
- vm.expectRevert("Incorrect payment amount");
+ vm.expectRevert(Chainvoice.IncorrectPaymentAmount.selector);
vm.prank(bob);
chainvoice.payInvoice{value: 1 ether + fee + 1}(0);
}
function testPayInvoice_RevertIfInvalidInvoiceId() public createInvoice{
uint256 fee = chainvoice.fee();
- vm.expectRevert("Invalid invoice ID");
+ vm.expectRevert(Chainvoice.InvalidInvoiceId.selector);
vm.prank(bob);
chainvoice.payInvoice{value: 1 ether + fee}(type(uint256).max);
}
function testPayInvoice_RevertIfInvoiceAlreadyPaid() public createInvoice{
uint256 fee = chainvoice.fee();
vm.prank(bob);
chainvoice.payInvoice{value: 1 ether + fee}(0);
- vm.expectRevert("Already paid");
+ vm.expectRevert(Chainvoice.InvoiceAlreadyPaid.selector);
vm.prank(bob);
chainvoice.payInvoice{value: 1 ether + fee}(0);
}As per coding guidelines, "Ensure failure paths and revert scenarios are explicitly handled and validated." 🤖 Prompt for AI Agents |
||
|
|
||
|
tani404 marked this conversation as resolved.
|
||
| function testAcceptOwnershipNotPending() public { | ||
| vm.prank(address(0xDEADBEEF)); | ||
| vm.expectRevert(Chainvoice.OwnershipNotPending.selector); | ||
| chainvoice.acceptOwnership(); | ||
| } | ||
| /* ------------------------------------------------------------ */ | ||
| /* FAILURE CASES - CANCEL INVOICE */ | ||
| /* ------------------------------------------------------------ */ | ||
|
|
||
| function testCancelOwnershipTransfer() public { | ||
| address newOwner = address(0xC0FFEE); | ||
|
|
||
| chainvoice.initiateOwnershipTransfer(newOwner); | ||
| assertEq(chainvoice.pendingOwner(), newOwner); | ||
|
|
||
| chainvoice.cancelOwnershipTransfer(); | ||
| assertEq(chainvoice.pendingOwner(), address(0)); | ||
| function testCancelInvoiceRevertIfNotTheOwner() public createInvoice{ | ||
| vm.expectRevert("Only invoice creator can cancel"); | ||
| vm.prank(bob); | ||
| chainvoice.cancelInvoice(0); | ||
| } | ||
|
|
||
| function testCancelOwnershipTransferNoPending() public { | ||
| vm.expectRevert(Chainvoice.OwnershipNotPending.selector); | ||
| chainvoice.cancelOwnershipTransfer(); | ||
| } | ||
| function testCancelInvoiceRevertIfItIsAlreadyPaid() public createInvoice{ | ||
| uint256 fee = chainvoice.fee(); | ||
| vm.prank(bob); | ||
| chainvoice.payInvoice{value: 1 ether + fee}(0); | ||
|
|
||
| function testFeeUpdateEvent() public { | ||
| uint256 newFee = 0.001 ether; | ||
| chainvoice.setFeeAmount(newFee); | ||
| assertEq(chainvoice.fee(), newFee); | ||
| vm.expectRevert("Invoice not cancellable"); | ||
| vm.prank(alice); | ||
| chainvoice.cancelInvoice(0); | ||
| } | ||
|
|
||
| function testTreasuryAddressUpdateEvent() public { | ||
| address newTreasury = address(0xdead); | ||
| chainvoice.setTreasuryAddress(newTreasury); | ||
| assertEq(chainvoice.treasuryAddress(), newTreasury); | ||
| function testCancelInvoiceRevertIfItIsAlreadyCancelled() public createInvoice{ | ||
| vm.prank(alice); | ||
| chainvoice.cancelInvoice(0); | ||
|
|
||
| vm.expectRevert("Invoice not cancellable"); | ||
| vm.prank(alice); | ||
| chainvoice.cancelInvoice(0); | ||
| } | ||
|
tani404 marked this conversation as resolved.
Comment on lines
+160
to
183
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests use string reverts but contract throws custom errors — tests will fail. The
🛠️ Proposed fix: use custom error selectors function testCancelInvoiceRevertIfNotTheOwner() public createInvoice{
- vm.expectRevert("Only invoice creator can cancel");
+ vm.expectRevert(Chainvoice.NotInvoiceCreator.selector);
vm.prank(bob);
chainvoice.cancelInvoice(0);
}
function testCancelInvoiceRevertIfItIsAlreadyPaid() public createInvoice{
uint256 fee = chainvoice.fee();
vm.prank(bob);
chainvoice.payInvoice{value: 1 ether + fee}(0);
- vm.expectRevert("Invoice not cancellable");
+ vm.expectRevert(Chainvoice.InvoiceNotCancellable.selector);
vm.prank(alice);
chainvoice.cancelInvoice(0);
}
function testCancelInvoiceRevertIfItIsAlreadyCancelled() public createInvoice{
vm.prank(alice);
chainvoice.cancelInvoice(0);
- vm.expectRevert("Invoice not cancellable");
+ vm.expectRevert(Chainvoice.InvoiceNotCancellable.selector);
vm.prank(alice);
chainvoice.cancelInvoice(0);
}As per coding guidelines, "Ensure failure paths and revert scenarios are explicitly handled and validated." 🤖 Prompt for AI Agents |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests use string reverts but contract throws custom errors — tests will fail.
The
createInvoicefunction inChainvoice.soluses custom errors (revert ZeroAddress()at line 124,revert SelfInvoicing()at line 125), not string reverts. Thesevm.expectRevert("...")calls will not match the actual revert data.🛠️ Proposed fix: use custom error selectors
function testCreateInvoiceRevertIfInvalidAddress() public{ vm.prank(alice); - vm.expectRevert("Recipient address is zero"); + vm.expectRevert(Chainvoice.ZeroAddress.selector); chainvoice.createInvoice(address(0), 1 ether, address(0), "data", "hash"); } function testCreateInvoiceRevertIfSelfInvoicing() public{ vm.prank(alice); - vm.expectRevert("Self-invoicing not allowed"); + vm.expectRevert(Chainvoice.SelfInvoicing.selector); chainvoice.createInvoice(alice, 1 ether, address(0), "data", "hash"); }As per coding guidelines, "Ensure failure paths and revert scenarios are explicitly handled and validated."
📝 Committable suggestion
🤖 Prompt for AI Agents