Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
321 changes: 321 additions & 0 deletions test/access/Owner/LibOwner.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.30;

import {Test, console2} from "forge-std/Test.sol";
import {LibOwner} from "../../../src/access/Owner/LibOwner.sol";
import {LibOwnerHarness} from "./harnesses/LibOwnerHarness.sol";

contract LibOwnerTest is Test {
LibOwnerHarness public harness;

address INITIAL_OWNER = makeAddr("owner");
address NEW_OWNER = makeAddr("newOwner");
address ALICE = makeAddr("alice");
address BOB = makeAddr("bob");
address ZERO_ADDRESS = address(0);

// Events
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

function setUp() public {
harness = new LibOwnerHarness();
harness.initialize(INITIAL_OWNER);
}

// ============================================
// Storage Tests
// ============================================

function test_GetStorage_ReturnsCorrectOwner() public view {
assertEq(harness.owner(), INITIAL_OWNER);
assertEq(harness.getStorageOwner(), INITIAL_OWNER);
}

function test_StorageSlot_UsesCorrectPosition() public {
bytes32 expectedSlot = keccak256("compose.owner");

// Change owner
vm.prank(INITIAL_OWNER);
harness.transferOwnership(NEW_OWNER);

// Read directly from storage
bytes32 storedValue = vm.load(address(harness), expectedSlot);
address storedOwner = address(uint160(uint256(storedValue)));

assertEq(storedOwner, NEW_OWNER);
assertEq(harness.owner(), NEW_OWNER);
}

// ============================================
// Owner Getter Tests
// ============================================

function test_Owner_ReturnsCurrentOwner() public {
assertEq(harness.owner(), INITIAL_OWNER);

vm.prank(INITIAL_OWNER);
harness.transferOwnership(NEW_OWNER);
assertEq(harness.owner(), NEW_OWNER);
}

function test_Owner_ReturnsZeroAfterRenounce() public {
vm.prank(INITIAL_OWNER);
harness.transferOwnership(ZERO_ADDRESS);
assertEq(harness.owner(), ZERO_ADDRESS);
}

// ============================================
// Transfer Ownership Tests
// ============================================

function test_TransferOwnership_UpdatesOwner() public {
vm.prank(INITIAL_OWNER);
harness.transferOwnership(NEW_OWNER);
assertEq(harness.owner(), NEW_OWNER);
}

function test_TransferOwnership_EmitsOwnershipTransferredEvent() public {
vm.expectEmit(true, true, false, true);
emit OwnershipTransferred(INITIAL_OWNER, NEW_OWNER);

vm.prank(INITIAL_OWNER);
harness.transferOwnership(NEW_OWNER);
}

function test_TransferOwnership_AllowsTransferToZeroAddress() public {
vm.prank(INITIAL_OWNER);
harness.transferOwnership(ZERO_ADDRESS);
assertEq(harness.owner(), ZERO_ADDRESS);
}

function test_TransferOwnership_AllowsTransferToSelf() public {
vm.prank(INITIAL_OWNER);
harness.transferOwnership(INITIAL_OWNER);
assertEq(harness.owner(), INITIAL_OWNER);
}

// TODO: When LibOwner is fixed to make renouncement irreversible:
// 1. Rename this test to: test_RevertWhen_TransferOwnership_FromRenouncedOwner
// 2. Change logic to:
// vm.expectRevert(LibOwner.OwnerAlreadyRenounced.selector);
// harness.transferOwnership(NEW_OWNER);
// 3. Remove the assertions for successful transfer
function test_TransferOwnership_AfterRenounce_AllowsNewOwner() public {
// Force renounce
harness.forceRenounce();
assertEq(harness.owner(), ZERO_ADDRESS);

// CURRENT BEHAVIOR (BUG): Library allows transferOwnership after renouncement
// EXPECTED BEHAVIOR: Should revert with OwnerAlreadyRenounced error
harness.transferOwnership(NEW_OWNER);
assertEq(harness.owner(), NEW_OWNER);
}

// ============================================
// Sequential Transfer Tests
// ============================================

function test_MultipleTransfers() public {
// First transfer
vm.prank(INITIAL_OWNER);
harness.transferOwnership(ALICE);
assertEq(harness.owner(), ALICE);

// Second transfer
vm.prank(ALICE);
harness.transferOwnership(BOB);
assertEq(harness.owner(), BOB);

// Third transfer
vm.prank(BOB);
harness.transferOwnership(NEW_OWNER);
assertEq(harness.owner(), NEW_OWNER);
}

// ============================================
// Event Tests
// ============================================

function test_Events_CorrectPreviousOwner() public {
vm.expectEmit(true, true, false, true);
emit OwnershipTransferred(INITIAL_OWNER, ALICE);

vm.prank(INITIAL_OWNER);
harness.transferOwnership(ALICE);

vm.expectEmit(true, true, false, true);
emit OwnershipTransferred(ALICE, BOB);

vm.prank(ALICE);
harness.transferOwnership(BOB);
}

function test_Events_RenounceEmitsZeroAddress() public {
vm.expectEmit(true, true, false, true);
emit OwnershipTransferred(INITIAL_OWNER, ZERO_ADDRESS);

vm.prank(INITIAL_OWNER);
harness.transferOwnership(ZERO_ADDRESS);
}

// ============================================
// Edge Cases
// ============================================

// TODO: When LibOwner is fixed to make renouncement irreversible:
// 1. Rename this test to: test_RenounceOwnership_PermanentlyDisablesTransfers
// 2. Change logic to:
// vm.expectRevert(LibOwner.OwnerAlreadyRenounced.selector);
// harness.transferOwnership(ALICE);
// 3. Remove the assertion for successful transfer
function test_RenounceOwnership_AllowsRecovery() public {
// Renounce ownership
vm.prank(INITIAL_OWNER);
harness.transferOwnership(ZERO_ADDRESS);
assertEq(harness.owner(), ZERO_ADDRESS);

// CURRENT BEHAVIOR (BUG): Library allows recovery after renouncement
// EXPECTED BEHAVIOR: Should revert with OwnerAlreadyRenounced error
harness.transferOwnership(ALICE);
assertEq(harness.owner(), ALICE);
}

function test_LibraryDoesNotCheckMsgSender() public {
// The library doesn't check msg.sender - that's the facet's responsibility
// This test verifies the library works regardless of caller
// (In production, the facet should check permissions before calling the library)

vm.prank(ALICE); // Not the owner
harness.transferOwnership(BOB);
assertEq(harness.owner(), BOB);

// This shows the library itself doesn't enforce access control
// Access control should be implemented in the facet that uses the library
}

// ============================================
// Fuzz Tests
// ============================================

function test_Fuzz_TransferOwnership(address newOwner) public {
vm.prank(INITIAL_OWNER);
harness.transferOwnership(newOwner);
assertEq(harness.owner(), newOwner);
}

function test_Fuzz_MultipleTransfers(address owner1, address owner2, address owner3) public {
vm.assume(owner1 != address(0));
vm.assume(owner2 != address(0));

vm.prank(INITIAL_OWNER);
harness.transferOwnership(owner1);
assertEq(harness.owner(), owner1);

vm.prank(owner1);
harness.transferOwnership(owner2);
assertEq(harness.owner(), owner2);

vm.prank(owner2);
harness.transferOwnership(owner3);
assertEq(harness.owner(), owner3);
}

// TODO: When LibOwner is fixed to make renouncement irreversible:
// 1. Rename this test to: test_Fuzz_RevertWhen_RenouncedOwnerTransfers
// 2. Change logic to:
// vm.expectRevert(LibOwner.OwnerAlreadyRenounced.selector);
// harness.transferOwnership(target);
// 3. Remove the assertion for successful transfer
function test_Fuzz_TransferAfterRenounce_AllowsRecovery(address target) public {
vm.assume(target != address(0));

// Renounce
vm.prank(INITIAL_OWNER);
harness.transferOwnership(ZERO_ADDRESS);
assertEq(harness.owner(), ZERO_ADDRESS);

// CURRENT BEHAVIOR (BUG): Library allows recovery - can transfer to new owner
// EXPECTED BEHAVIOR: Should revert with OwnerAlreadyRenounced error
harness.transferOwnership(target);
assertEq(harness.owner(), target);
}

// ============================================
// Renounce Ownership Tests (New Function)
// ============================================

function test_RenounceOwnership_SetsOwnerToZero() public {
// Use the new renounceOwnership function
harness.renounceOwnership();
assertEq(harness.owner(), ZERO_ADDRESS);
}

function test_RenounceOwnership_EmitsCorrectEvent() public {
vm.expectEmit(true, true, false, true);
emit OwnershipTransferred(INITIAL_OWNER, ZERO_ADDRESS);

harness.renounceOwnership();
}

// ============================================
// Require Owner Tests (New Function)
// ============================================

function test_RequireOwner_PassesForOwner() public {
// Should not revert when called by owner
vm.prank(INITIAL_OWNER);
harness.requireOwner();
}

function test_RevertWhen_RequireOwner_CalledByNonOwner() public {
vm.expectRevert(LibOwner.OwnerUnauthorizedAccount.selector);
vm.prank(ALICE);
harness.requireOwner();
}

function test_Fuzz_RequireOwner(address caller) public {
if (caller == INITIAL_OWNER) {
// Should not revert for owner
vm.prank(caller);
harness.requireOwner();
} else {
// Should revert for non-owner
vm.expectRevert(LibOwner.OwnerUnauthorizedAccount.selector);
vm.prank(caller);
harness.requireOwner();
}
}

// ============================================
// Gas Tests
// ============================================

function test_Gas_Owner() public view {
uint256 gasBefore = gasleft();
harness.owner();
uint256 gasUsed = gasBefore - gasleft();

console2.log("Gas used for LibOwner.owner():", gasUsed);
assertTrue(gasUsed < 10000, "Owner getter uses too much gas");
}

function test_Gas_TransferOwnership() public {
uint256 gasBefore = gasleft();
vm.prank(INITIAL_OWNER);
harness.transferOwnership(NEW_OWNER);
uint256 gasUsed = gasBefore - gasleft();

console2.log("Gas used for LibOwner.transferOwnership():", gasUsed);
assertTrue(gasUsed < 50000, "Transfer ownership uses too much gas");
}

function test_Gas_TransferOwnership_Renounce() public {
uint256 gasBefore = gasleft();
vm.prank(INITIAL_OWNER);
harness.transferOwnership(ZERO_ADDRESS);
uint256 gasUsed = gasBefore - gasleft();

console2.log("Gas used for LibOwner renounce:", gasUsed);
assertTrue(gasUsed < 50000, "Renounce uses too much gas");
}
}
Loading
Loading