Skip to content

Commit

Permalink
Merge pull request #4 from BibliothecaDAO/tests
Browse files Browse the repository at this point in the history
Tests
  • Loading branch information
credence0x authored May 20, 2024
2 parents f3de614 + f3b50ee commit 05b1be2
Show file tree
Hide file tree
Showing 11 changed files with 1,085 additions and 11 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v3
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: "2.6.4"
tool-versions: .tool-versions
- run: cd contracts && scarb fmt --check

check-build:
Expand All @@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout@v3
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: "2.6.4"
tool-versions: .tool-versions
- run: cd contracts && scarb build

check-test:
Expand All @@ -31,5 +31,8 @@ jobs:
- uses: actions/checkout@v3
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: "2.6.4"
- run: cd contracts && scarb test
tool-versions: .tool-versions
- uses: foundry-rs/setup-snfoundry@v3
with:
tool-versions: .tool-versions
- run: cd contracts && snforge test
3 changes: 2 additions & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
scarb 2.6.3
scarb 2.6.3
starknet-foundry 0.23.0
Empty file.
16 changes: 16 additions & 0 deletions contracts/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,19 @@ mod components {
mod extensions;
}
}

mod tests {
mod unit {
#[cfg(test)]
mod test_strealm_component;
}
mod integration {
#[cfg(test)]
mod test_lordship;
}
mod mocks {
mod account_mock;
mod erc20_mock;
mod strealm_mock;
}
}
19 changes: 13 additions & 6 deletions contracts/src/lordship.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");
const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");

#[starknet::interface]
trait IERC721Minter<TState> {
fn safe_mint(
ref self: TState, recipient: starknet::ContractAddress, token_id: u256, data: Span<felt252>,
);
}


#[starknet::contract]
mod Lordship {
use openzeppelin::access::accesscontrol::AccessControlComponent;
Expand All @@ -33,6 +41,7 @@ mod Lordship {
use strealm::components::erc721::extensions::ERC721VotesComponent;
use strealm::components::strealm::StRealmComponent::InternalTrait as StRealmInternalTrait;
use strealm::components::strealm::StRealmComponent;
use super::{IERC721Minter};
use super::{MINTER_ROLE, UPGRADER_ROLE};

component!(path: ERC721Component, storage: erc721, event: ERC721Event);
Expand Down Expand Up @@ -139,10 +148,10 @@ mod Lordship {
token_id: u256,
auth: ContractAddress
) {
let owner_before_transfer: ContractAddress = self.owner_of(token_id);
let owner_before_transfer: ContractAddress = self._owner_of(token_id);
let owner_after_transfer: ContractAddress = to;

// claim stream for both sender and receiver
// updated streamed reward balance for both sender and receiver
let mut strealm_component = get_dep_component_mut!(ref self, StRealm);
strealm_component._update_stream_balance(owner_before_transfer);
strealm_component._update_stream_balance(owner_after_transfer);
Expand Down Expand Up @@ -208,10 +217,8 @@ mod Lordship {
}


#[generate_trait]
#[abi(per_item)]
impl ERC721MinterImpl of ERC721MinterTrait {
#[external(v0)]
#[abi(embed_v0)]
impl ERC721MinterImpl of IERC721Minter<ContractState> {
fn safe_mint(
ref self: ContractState,
recipient: ContractAddress,
Expand Down
1 change: 1 addition & 0 deletions contracts/src/tests/constants.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

208 changes: 208 additions & 0 deletions contracts/src/tests/integration/test_lordship.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
use core::debug::PrintTrait;
use core::integer::BoundedInt;
use core::serde::Serde;
use core::starknet::storage::StorageMapMemberAccessTrait;
use core::starknet::storage::StorageMemberAccessTrait;
use openzeppelin::access::accesscontrol::accesscontrol::AccessControlComponent::InternalTrait as AccessComponentInternalTrait;
use openzeppelin::access::accesscontrol::interface::{
AccessControlABIDispatcher, AccessControlABIDispatcherTrait
};
use openzeppelin::account::interface::{AccountABIDispatcher, AccountABIDispatcherTrait};
use openzeppelin::token::erc20::interface::{IERC20DispatcherTrait, IERC20Dispatcher};
use openzeppelin::token::erc721::erc721::ERC721Component::InternalTrait as ERC721InternalTrait;
use openzeppelin::token::erc721::interface::{
ERC721ABI, ERC721ABIDispatcher, ERC721ABIDispatcherTrait
};
use openzeppelin::upgrades::interface::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait};
use snforge_std::{
declare, ContractClassTrait, spy_events, SpyOn, EventSpy, EventAssertions, test_address,
start_roll, stop_roll, start_warp, stop_warp, CheatTarget, start_prank, stop_prank,
get_class_hash
};
use starknet::ContractAddress;
use starknet::contract_address_const;
use starknet::syscalls::call_contract_syscall;
use strealm::components::strealm::IStRealm;
use strealm::components::strealm::StRealmComponent::InternalTrait as StRealmInternalTrait;
use strealm::components::strealm::StRealmComponent::{Flow, Stream};
use strealm::components::strealm::StRealmComponent;
use strealm::components::strealm::{IStRealmDispatcher, IStRealmDispatcherTrait};
use strealm::lordship::Lordship;
use strealm::lordship::{IERC721MinterDispatcher, IERC721MinterDispatcherTrait};
use strealm::tests::mocks::account_mock::DualCaseAccountMock;
use strealm::tests::mocks::erc20_mock::DualCaseERC20Mock;


///
/// Constants
///
///
fn FLOW_RATE() -> u256 {
123
}

fn DEFAULT_ADMIN() -> ContractAddress {
contract_address_const::<'DEFAULT_ADMIN_ADDRESS'>()
}

fn MINTER() -> ContractAddress {
contract_address_const::<'MINTER_ADDRESS'>()
}

fn UPGRADER() -> ContractAddress {
contract_address_const::<'UPGRADER_ADDRESS'>()
}

fn REWARD_TOKEN() -> ContractAddress {
contract_address_const::<'REWARD_TOKEN_ADDRESS'>()
}

fn REWARD_PAYER() -> ContractAddress {
contract_address_const::<'REWARD_PAYER_ADDRESS'>()
}


fn ACCOUNT_MOCK_ADDRESS() -> ContractAddress {
let account_contract = declare("DualCaseAccountMock").unwrap();

let mut constructor_calldata = array![];
let public_key: felt252 = 123;
public_key.serialize(ref constructor_calldata);

let (contract_address, _) = account_contract.deploy(@constructor_calldata).unwrap();

contract_address
}


fn ERC20_MOCK() -> IERC20Dispatcher {
let erc20_contract = declare("DualCaseERC20Mock").unwrap();

let mut constructor_calldata = array![];
let name: ByteArray = "MockToken";
let symbol: ByteArray = "MToken";
let supply: u256 = 2000000000000000000000000;
let recipient: ContractAddress = REWARD_PAYER();
name.serialize(ref constructor_calldata);
symbol.serialize(ref constructor_calldata);
supply.serialize(ref constructor_calldata);
recipient.serialize(ref constructor_calldata);

let (contract_address, _) = erc20_contract.deploy(@constructor_calldata).unwrap();

let dispatcher = IERC20Dispatcher { contract_address };
return dispatcher;
}


fn DEPLOY_LORDSHIP_CONTRACT() -> ContractAddress {
let lordship_contract = declare("Lordship").unwrap();

let mut constructor_calldata = array![];
DEFAULT_ADMIN().serialize(ref constructor_calldata);
MINTER().serialize(ref constructor_calldata);
UPGRADER().serialize(ref constructor_calldata);
FLOW_RATE().serialize(ref constructor_calldata);
REWARD_TOKEN().serialize(ref constructor_calldata);
REWARD_PAYER().serialize(ref constructor_calldata);

let (contract_address, _) = lordship_contract.deploy(@constructor_calldata).unwrap();

contract_address
}


///
/// Tests
///

#[test]
fn test_constructor() {
let mut lordship_address = DEPLOY_LORDSHIP_CONTRACT();

// ensure roles are correct
let access_control_dispatcher = AccessControlABIDispatcher {
contract_address: lordship_address
};
assert!(access_control_dispatcher.has_role(Lordship::DEFAULT_ADMIN_ROLE, DEFAULT_ADMIN()));
assert!(access_control_dispatcher.has_role(Lordship::MINTER_ROLE, MINTER()));
assert!(access_control_dispatcher.has_role(Lordship::UPGRADER_ROLE, UPGRADER()));

// ensure erc721 was initialized properly
let erc721_dispatcher = ERC721ABIDispatcher { contract_address: lordship_address };
assert_eq!(erc721_dispatcher.name(), "Staked Realm");
assert_eq!(erc721_dispatcher.symbol(), "stREALM");

// ensure strealm component was initialized properly
let strealm_dispatcher = IStRealmDispatcher { contract_address: lordship_address };
assert_eq!(strealm_dispatcher.get_latest_flow_id(), 1);
assert_eq!(strealm_dispatcher.get_flow(1).rate, FLOW_RATE());
assert_eq!(strealm_dispatcher.get_flow(1).end_at, BoundedInt::max());
assert_eq!(strealm_dispatcher.get_reward_payer(), REWARD_PAYER());
assert_eq!(strealm_dispatcher.get_reward_token(), REWARD_TOKEN());
}


#[test]
fn test_upgrade() {
let mut lordship_address = DEPLOY_LORDSHIP_CONTRACT();

// change class hash to erc20 class hash
start_prank(CheatTarget::One(lordship_address), UPGRADER());
let new_class_hash = get_class_hash(ERC20_MOCK().contract_address);
IUpgradeableDispatcher { contract_address: lordship_address }.upgrade(new_class_hash);
stop_prank(CheatTarget::One(lordship_address));

let erc20_dispatcher = IERC20Dispatcher { contract_address: lordship_address };
assert_eq!(erc20_dispatcher.total_supply(), 0);
}


#[test]
#[should_panic(expected: ('Caller is missing role',))]
fn test_upgrade_no_permission() {
let mut lordship_address = DEPLOY_LORDSHIP_CONTRACT();

// change class hash to erc20 class hash
// start_prank(CheatTarget::One(lordship_address), UPGRADER());
let new_class_hash = get_class_hash(ERC20_MOCK().contract_address);
IUpgradeableDispatcher { contract_address: lordship_address }.upgrade(new_class_hash);
// stop_prank(CheatTarget::One(lordship_address));
}


#[test]
fn test_safe_mint() {
let mut lordship_address = DEPLOY_LORDSHIP_CONTRACT();

start_prank(CheatTarget::One(lordship_address), MINTER());

let erc721_minter_dispatcher = IERC721MinterDispatcher { contract_address: lordship_address };
let mint_token_id = 44_u256;
let mint_recipient = ACCOUNT_MOCK_ADDRESS();
let mint_data: Span<felt252> = array![].span();
erc721_minter_dispatcher.safe_mint(mint_recipient, mint_token_id, mint_data);
stop_prank(CheatTarget::One(lordship_address));

let erc721_dispatcher = ERC721ABIDispatcher { contract_address: lordship_address };
assert_eq!(erc721_dispatcher.balance_of(mint_recipient), 1);
assert_eq!(erc721_dispatcher.owner_of(mint_token_id), mint_recipient);
}


#[test]
#[should_panic(expected: ('Caller is missing role',))]
fn test_safe_mint_no_permission() {
let mut lordship_address = DEPLOY_LORDSHIP_CONTRACT();

// start_prank(CheatTarget::One(lordship_address), MINTER());

let erc721_minter_dispatcher = IERC721MinterDispatcher { contract_address: lordship_address };
let mint_token_id = 44_u256;
let mint_recipient = ACCOUNT_MOCK_ADDRESS();
let mint_data: Span<felt252> = array![].span();
erc721_minter_dispatcher.safe_mint(mint_recipient, mint_token_id, mint_data);
// stop_prank(CheatTarget::One(lordship_address));

}

45 changes: 45 additions & 0 deletions contracts/src/tests/mocks/account_mock.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#[starknet::contract(account)]
mod DualCaseAccountMock {
use openzeppelin::account::AccountComponent;
use openzeppelin::introspection::src5::SRC5Component;

component!(path: AccountComponent, storage: account, event: AccountEvent);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

// Account
#[abi(embed_v0)]
impl SRC6Impl = AccountComponent::SRC6Impl<ContractState>;
#[abi(embed_v0)]
impl SRC6CamelOnlyImpl = AccountComponent::SRC6CamelOnlyImpl<ContractState>;
#[abi(embed_v0)]
impl DeclarerImpl = AccountComponent::DeclarerImpl<ContractState>;
#[abi(embed_v0)]
impl DeployableImpl = AccountComponent::DeployableImpl<ContractState>;
impl AccountInternalImpl = AccountComponent::InternalImpl<ContractState>;

// SCR5
#[abi(embed_v0)]
impl SRC5Impl = SRC5Component::SRC5Impl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
account: AccountComponent::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
AccountEvent: AccountComponent::Event,
#[flat]
SRC5Event: SRC5Component::Event
}

#[constructor]
fn constructor(ref self: ContractState, public_key: felt252) {
self.account.initializer(public_key);
}
}
Loading

0 comments on commit 05b1be2

Please sign in to comment.