Skip to content

Commit

Permalink
test(ens-domains): add integration test for 'reserve' method
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielstoica committed Jan 15, 2025
1 parent d1934aa commit 6b26662
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 2 deletions.
19 changes: 17 additions & 2 deletions test/integration/Integration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ pragma solidity ^0.8.26;
import { Base_Test } from "../Base.t.sol";
import { PaymentModule } from "./../../src/modules/payment-module/PaymentModule.sol";
import { InvoiceCollection } from "./../../src/peripherals/invoice-collection/InvoiceCollection.sol";
import { WerkSubdomainRegistrar } from "./../../src/peripherals/ens-domains/WerkSubdomainRegistrar.sol";
import { WerkSubdomainRegistry } from "./../../src/peripherals/ens-domains/WerkSubdomainRegistry.sol";
import { IWerkSubdomainRegistry } from "./../../src/peripherals/ens-domains/interfaces/IWerkSubdomainRegistry.sol";
import { SablierV2LockupLinear } from "@sablier/v2-core/src/SablierV2LockupLinear.sol";
import { SablierV2LockupTranched } from "@sablier/v2-core/src/SablierV2LockupTranched.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
Expand All @@ -26,6 +29,7 @@ abstract contract Integration_Test is Base_Test {
SablierV2LockupTranched internal sablierV2LockupTranched;
MockStreamManager internal mockStreamManager;
MockBadSpace internal badSpace;
WerkSubdomainRegistrar internal werkSubdomainRegistrar;

/*//////////////////////////////////////////////////////////////////////////
SET-UP FUNCTION
Expand All @@ -37,9 +41,10 @@ abstract contract Integration_Test is Base_Test {
// Deploy corect contracts
deployCoreContracts();

// Enable the {PaymentModule} module on the {Space} contract
address[] memory modules = new address[](1);
// Enable the {PaymentModule} and {WerkSubdomainRegistrar} modules on the {Space} contract
address[] memory modules = new address[](2);
modules[0] = address(paymentModule);
modules[1] = address(werkSubdomainRegistrar);

// Deploy the {Space} contract with the {PaymentModule} enabled by default
space = deploySpace({ _owner: users.eve, _stationId: 0, _initialModules: modules });
Expand All @@ -63,6 +68,7 @@ abstract contract Integration_Test is Base_Test {
function deployCoreContracts() internal {
deployPaymentModule();
deployInvoiceCollection();
deployWerkSubdomainRegistrar();
}

/// @dev Deploys the {PaymentModule} module
Expand Down Expand Up @@ -91,4 +97,13 @@ abstract contract Integration_Test is Base_Test {
maxTrancheCount: 1000
});
}

/// @dev Deploys the {WerkSubdomainRegistrar} peripheral
function deployWerkSubdomainRegistrar() internal {
address registry = address(new WerkSubdomainRegistry());
WerkSubdomainRegistry(registry).initialize("werk.eth", "werk.eth", "https://werk.com/");

werkSubdomainRegistrar =
new WerkSubdomainRegistrar({ _registry: IWerkSubdomainRegistry(registry), _owner: users.admin });
}
}
96 changes: 96 additions & 0 deletions test/integration/concrete/ens-domains/reserve/reserve.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

import { Integration_Test } from "../../../Integration.t.sol";
import { Errors } from "../../../../utils/Errors.sol";
import { Events } from "../../../../utils/Events.sol";

contract Reserve_Integration_Concret_Test is Integration_Test {
function setUp() public virtual override {
Integration_Test.setUp();
}

function test_RevertWhen_CallerNotContract() external {
// Make Bob the caller in this test suite which is an EOA
vm.startPrank({ msgSender: users.bob });

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

// Run the test
werkSubdomainRegistrar.reserve({ label: "name" });
}

modifier whenCallerContract() {
_;
}

function test_RevertWhen_NonCompliantSpace() external whenCallerContract {
// Make Eve the caller in this test suite as she's the owner of the {Space} contract
vm.startPrank({ msgSender: users.eve });

// Create the calldata for the reserve method execution
bytes memory data = abi.encodeWithSignature("reserve(string)", "test");

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

// Run the test
mockNonCompliantSpace.execute({ module: address(werkSubdomainRegistrar), value: 0, data: data });
}

modifier whenCompliantSpace() {
_;
}

function test_RevertWhen_SubdomainAlreadyReserved() external whenCallerContract whenCompliantSpace {
// Make Eve the caller in this test suite as she's the owner of the {Space} contract
vm.startPrank({ msgSender: users.eve });

// Create the calldata for the reserve method execution
bytes memory data = abi.encodeWithSignature("reserve(string)", "test");

// Compute the expiration timestamp
uint40 expiresAt = uint40(block.timestamp + 30 minutes);

// Reserve the subdomain
space.execute({ module: address(werkSubdomainRegistrar), value: 0, data: data });

// Expect the call to revert with the {AlreadyReserved} error
vm.expectRevert(abi.encodeWithSelector(Errors.AlreadyReserved.selector, expiresAt));

// Run the test
space.execute({ module: address(werkSubdomainRegistrar), value: 0, data: data });
}

modifier whenSubdomainNotReserved() {
_;
}

function test_Reserve() external whenCallerContract whenCompliantSpace whenSubdomainNotReserved {
// Make Eve the caller in this test suite as she's the owner of the {Space} contract
vm.startPrank({ msgSender: users.eve });

// Create the calldata for the reserve method execution
bytes memory data = abi.encodeWithSignature("reserve(string)", "test");

// Compute the expiration timestamp
uint40 expectedExpiresAt = uint40(block.timestamp + 30 minutes);

// Expect the reservation call to emit a {SubdomainReserved} event
vm.expectEmit();
emit Events.SubdomainReserved({ label: "test", owner: address(space), expiresAt: expectedExpiresAt });

// Run the test
space.execute({ module: address(werkSubdomainRegistrar), value: 0, data: data });

// Get the reservation
(address owner, uint40 actualExpiresAt) = werkSubdomainRegistrar.reservations(keccak256(bytes("test")));

// Assert that actual and expected owner are the same
assertEq(owner, address(space));

// Assert that actual and expected expiration timestamp are the same
assertEq(actualExpiresAt, expectedExpiresAt);
}
}
12 changes: 12 additions & 0 deletions test/integration/concrete/ens-domains/reserve/reserve.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
reserve.t.sol
├── when the caller IS NOT a contract
│ └── it should revert with the {SpaceZeroCodeSize} error
└── when the caller IS a contract
├── when the caller is NOT a compliant Space
│ └── it should revert with the {SpaceUnsupportedInterface} error
└── when the caller is a compliant Space
├── when subdomain is already reserved
│ └── it should revert with the {AlreadyReserved} error
└── when subdomain is NOT reserved
├── it should emit a {SubdomainReserved} event
└── it should create a new reservation for the label
7 changes: 7 additions & 0 deletions test/utils/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,11 @@ library Errors {

/// @notice The `account` is missing a role.
error PermissionsUnauthorizedAccount(address account, bytes32 neededRole);

/*//////////////////////////////////////////////////////////////////////////
ENS-DOMAINS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the subdomain has already been reserved
error AlreadyReserved(uint40 expiresAt);
}
15 changes: 15 additions & 0 deletions test/utils/Events.sol
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,19 @@ abstract contract Events {
/// @param tokenId The ID of the NFT representing the invoice
/// @param paymentRequestId The ID of the payment request associated with the invoice
event InvoiceMinted(address to, uint256 tokenId, string paymentRequestId);

/*//////////////////////////////////////////////////////////////////////////
ENS-DOMAINS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Emitted when a new name is registered
/// @param label The registered label (e.g. "name" in "name.werk.eth")
/// @param owner The owner of the newly registered name
event NameRegistered(string indexed label, address indexed owner);

/// @notice Emitted when a subdomain is reserved
/// @param label The reserved label (e.g. "name" in "name.werk.eth")
/// @param owner The owner of the reserved subdomain
/// @param expiresAt The timestamp at which the reservation expires
event SubdomainReserved(string indexed label, address indexed owner, uint40 expiresAt);
}

0 comments on commit 6b26662

Please sign in to comment.