Skip to content
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

feat: Make it possible during tests to access internal state for components #5862

Closed
1 of 2 tasks
0xNeshi opened this issue Jun 24, 2024 · 2 comments
Closed
1 of 2 tasks
Labels
enhancement New feature or request

Comments

@0xNeshi
Copy link

0xNeshi commented Jun 24, 2024

Feature Request

Describe the Feature Request

When testing contracts that include components, it is currently impossible to use Contract::contract_state_for_testing to access state variables that defined inside the component itself.

It would be great if it were possible to access internal state of components inside tests.

Describe Preferred Solution

Describe Alternatives

Related Code

  • working example using Contracts (without components):
#[starknet::interface]
pub trait ICounter<TContractState> {
    fn increment(ref self: TContractState);
}

#[starknet::contract]
pub mod Counter {
    #[storage]
    struct Storage {
        value: u32
    }

    #[abi(embed_v0)]
    impl Counter of super::ICounter<ContractState> {
        fn increment(ref self: ContractState) {
            self.value.write(self.value.read() + 1);
        }
    }
}

#[cfg(test)]
mod tests {
    use core::starknet::storage::StorageMemberAccessTrait;
    use starknet::syscalls::deploy_syscall;
    use starknet::SyscallResultTrait;
    use super::Counter;
    use super::{ICounterDispatcher, ICounterDispatcherTrait};
    use starknet::{ContractAddress, testing::set_contract_address};

    fn setup_counter() -> ICounterDispatcher {
        let (address, _) = deploy_syscall(
            Counter::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false
        )
            .unwrap_syscall();
        ICounterDispatcher { contract_address: address }
    }

    #[test]
    fn test_increment_event_contract() {
        let counter = setup_counter();
        set_contract_address(counter.contract_address);
        let mut state = Counter::contract_state_for_testing();
        assert_eq!(state.value.read(), 0);
        counter.increment();
        assert_eq!(state.value.read(), 1);
    }
}
  • the same example refactored to use a component, which should work the same way as the previous example:
#[starknet::interface]
pub trait ICounter<TContractState> {
    fn increment(ref self: TContractState);
}

#[starknet::component]
pub mod countable_component {
    #[storage]
    struct Storage {
        value: u32
    }

    #[embeddable_as(CounterImpl)]
    impl Counter<
        TContractState, +HasComponent<TContractState>
    > of super::ICounter<ComponentState<TContractState>> {
        fn increment(ref self: ComponentState<TContractState>) {
            self.value.write(self.value.read() + 1);
        }
    }
}

#[starknet::contract]
mod Counter {
    use super::countable_component;

    component!(path: countable_component, storage: countable, event: CountableEvent);
    #[storage]
    struct Storage {
        #[substorage(v0)]
        countable: countable_component::Storage,
    }

    #[event]
    #[derive(Drop, starknet::Event)]
    enum Event {
        CountableEvent: countable_component::Event
    }

    #[abi(embed_v0)]
    impl CounterImpl = countable_component::CounterImpl<ContractState>;
}


#[cfg(test)]
mod tests {
    use core::starknet::storage::StorageMemberAccessTrait;
    use starknet::syscalls::deploy_syscall;
    use starknet::SyscallResultTrait;
    use super::Counter;
    use super::{ICounterDispatcher, ICounterDispatcherTrait};
    use starknet::{ContractAddress, testing::set_contract_address};

    fn setup_counter() -> ICounterDispatcher {
        let (address, _) = deploy_syscall(
            Counter::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false
        )
            .unwrap_syscall();
        ICounterDispatcher { contract_address: address }
    }

    #[test]
    fn test_increment_event_contract() {
        let counter = setup_counter();
        set_contract_address(counter.contract_address);
        let mut state = Counter::contract_state_for_testing();
        assert_eq!(state.value.read(), 0);
        counter.increment();
        assert_eq!(state.value.read(), 1);
    }
}

Additional Context

#5044

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

Don't know enough Rust.

@0xNeshi 0xNeshi added the enhancement New feature or request label Jun 24, 2024
@orizi
Copy link
Collaborator

orizi commented Jun 25, 2024

have you tried using state.countable.value? - as .value is not a member of the contract state.

@0xNeshi
Copy link
Author

0xNeshi commented Jun 25, 2024

have you tried using state.countable.value? - as .value is not a member of the contract state.

That solved the issue, thanks!

For some reason I didn't realize that you could access the countable substorage, I guess this is an issue with autocomplete dialog (see #5660 (comment)).

Closing the issue.

@0xNeshi 0xNeshi closed this as completed Jun 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants