diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..fb8ad4e Binary files /dev/null and b/.DS_Store differ diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..451d64f --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +starknet-foundry 0.31.0 +scarb 2.8.3 diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..1af4d87 Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/base/errors.cairo b/src/base/errors.cairo index f96c9c7..9bcb067 100644 --- a/src/base/errors.cairo +++ b/src/base/errors.cairo @@ -4,4 +4,6 @@ pub mod Errors { pub const NOT_OWNER: felt252 = 'Caller Not Owner'; pub const CLOSED_EVENT: felt252 = 'Event is closed'; pub const ALREADY_REGISTERED: felt252 = 'Caller already registered'; + pub const NOT_REGISTERED: felt252 = 'rsvp only for registered event'; + pub const ALREADY_RSVP: felt252 = 'rsvp already exist'; } diff --git a/src/events/events.cairo b/src/events/events.cairo index e6967ba..178074d 100644 --- a/src/events/events.cairo +++ b/src/events/events.cairo @@ -3,7 +3,8 @@ pub mod Events { use core::num::traits::zero::Zero; use chainevents_contracts::base::types::{EventDetails, EventRegistration, EventType}; use chainevents_contracts::base::errors::Errors::{ - ZERO_ADDRESS_OWNER, ZERO_ADDRESS_CALLER, NOT_OWNER, CLOSED_EVENT, ALREADY_REGISTERED + ZERO_ADDRESS_OWNER, ZERO_ADDRESS_CALLER, NOT_OWNER, CLOSED_EVENT, ALREADY_REGISTERED, + NOT_REGISTERED, ALREADY_RSVP }; use chainevents_contracts::interfaces::IEvent::IEvent; use core::starknet::{ @@ -36,7 +37,7 @@ pub mod Events { // event #[event] #[derive(Drop, starknet::Event)] - enum Event { + pub enum Event { NewEventAdded: NewEventAdded, RegisteredForEvent: RegisteredForEvent, EventAttendanceMark: EventAttendanceMark, @@ -70,7 +71,6 @@ pub mod Events { #[derive(Drop, starknet::Event)] pub struct RSVPForEvent { pub event_id: u256, - pub event_name: felt252, pub attendee_address: ContractAddress } @@ -99,6 +99,7 @@ pub mod Events { fn add_event(ref self: ContractState, name: ByteArray, location: ByteArray) -> u256 { let event_owner = get_caller_address(); let event_id = self.event_counts.read() + 1; + self.event_counts.write(event_id); let event_name = name.clone(); let event_location = location.clone(); @@ -174,7 +175,23 @@ pub mod Events { fn end_event_registration( ref self: ContractState, event_id: u256 ) {} // only owner can closed an event - fn rsvp_for_event(ref self: ContractState, event_id: u256) {} + + fn rsvp_for_event(ref self: ContractState, event_id: u256) { + let caller = get_caller_address(); + + let attendee_event_details = self + .attendee_event_details + .entry((event_id, caller)) + .read(); + + assert(attendee_event_details.attendee_address == caller, NOT_REGISTERED); + assert(attendee_event_details.has_rsvp == false, ALREADY_RSVP); + + self.attendee_event_details.entry((event_id, caller)).has_rsvp.write(true); + + self.emit(RSVPForEvent { event_id, attendee_address: caller, }); + } + fn upgrade_event(ref self: ContractState, event_id: u256, paid_amount: u256) {} // GETTER FUNCTION diff --git a/tests/test_contract.cairo b/tests/test_contract.cairo index 8da5deb..6418b86 100644 --- a/tests/test_contract.cairo +++ b/tests/test_contract.cairo @@ -9,10 +9,11 @@ use starknet::{ContractAddress}; use snforge_std::{ declare, start_cheat_caller_address, stop_cheat_caller_address, ContractClassTrait, - DeclareResultTrait, + DeclareResultTrait, spy_events, EventSpyAssertionsTrait, }; use chainevents_contracts::interfaces::IEvent::{IEventDispatcher, IEventDispatcherTrait}; +use chainevents_contracts::events::events::Events; const USER_ONE: felt252 = 'JOE'; @@ -65,6 +66,127 @@ fn test_register_for_event() { stop_cheat_caller_address(event_contract_address); } +#[test] +#[should_panic(expected: 'rsvp only for registered event')] +fn test_should_panic_on_rsvp_for_event_that_was_not_registered_for() { + let event_contract_address = __setup__(); + + let event_dispatcher = IEventDispatcher { contract_address: event_contract_address }; + + let caller: ContractAddress = starknet::contract_address_const::<0x123626789>(); + + start_cheat_caller_address(event_contract_address, caller); + + let event_id: u256 = 1; + + event_dispatcher.rsvp_for_event(event_id); + + stop_cheat_caller_address(event_contract_address); +} + +#[test] +fn test_rsvp_for_event_should_emit_event_on_success() { + let event_contract_address = __setup__(); + + let event_dispatcher = IEventDispatcher { contract_address: event_contract_address }; + + // USER_ONE adds event + start_cheat_caller_address(event_contract_address, USER_ONE.try_into().unwrap()); + let event_id = event_dispatcher.add_event("bitcoin dev meetup", "Dan Marna road"); + assert(event_id == 1, 'Event was not created'); + stop_cheat_caller_address(event_contract_address); + + // Use a new user(caller) to register for event & rsvp for event + let caller: ContractAddress = starknet::contract_address_const::<0x123626789>(); + + start_cheat_caller_address(event_contract_address, caller); + + event_dispatcher.register_for_event(event_id); + + let mut spy = spy_events(); + + event_dispatcher.rsvp_for_event(event_id); + + let expected_event = Events::Event::RSVPForEvent( + Events::RSVPForEvent { event_id: 1, attendee_address: caller } + ); + spy.assert_emitted(@array![(event_contract_address, expected_event)]); + + stop_cheat_caller_address(event_contract_address); +} + +#[test] +#[should_panic(expected: 'rsvp already exist')] +fn test_should_panic_on_rsvp_for_event_twice() { + let event_contract_address = __setup__(); + + let event_dispatcher = IEventDispatcher { contract_address: event_contract_address }; + + // USER_ONE adds event + start_cheat_caller_address(event_contract_address, USER_ONE.try_into().unwrap()); + let event_id = event_dispatcher.add_event("bitcoin dev meetup", "Dan Marna road"); + assert(event_id == 1, 'Event was not created'); + stop_cheat_caller_address(event_contract_address); + + // Use a new user(caller) to register for event & rsvp for event + let caller: ContractAddress = starknet::contract_address_const::<0x123626789>(); + + start_cheat_caller_address(event_contract_address, caller); + + event_dispatcher.register_for_event(event_id); + + // first rsvp for event + event_dispatcher.rsvp_for_event(event_id); + + // second rsvp for the same event: should panic + event_dispatcher.rsvp_for_event(event_id); + + stop_cheat_caller_address(event_contract_address); +} + + +#[test] +fn test_event_count_increase() { + let event_contract_address = __setup__(); + let event_dispatcher = IEventDispatcher { contract_address: event_contract_address }; + + start_cheat_caller_address(event_contract_address, USER_ONE.try_into().unwrap()); + + let initial_event_id = event_dispatcher.add_event("Blockchain Conference", "Tech Park"); + assert(initial_event_id == 1, 'First event ID incorrect'); + + let second_event_id = event_dispatcher.add_event("Ethereum Workshop", "Innovation Hub"); + assert(second_event_id == 2, 'Second event ID incorrect'); + + stop_cheat_caller_address(event_contract_address); +} + +#[test] +fn test_event_emission() { + let event_contract_address = __setup__(); + let event_dispatcher = IEventDispatcher { contract_address: event_contract_address }; + + start_cheat_caller_address(event_contract_address, USER_ONE.try_into().unwrap()); + + // Add event with string literals + let event_id = event_dispatcher.add_event("Devcon", "Barcelona"); + assert(event_id == 1, 'Event ID mismatch'); + + // Get event details and compare them separately + let event_details = event_dispatcher.event_details(event_id); + + // Compare each field independently + let name_matches = event_details.name == "Devcon"; + let location_matches = event_details.location == "Barcelona"; + + assert(name_matches, 'Event name mismatch'); + assert(location_matches, 'Event location mismatch'); + assert(event_details.event_id == event_id, 'Event ID mismatch in details'); + assert(!event_details.is_closed, 'Event should not be closed'); + + stop_cheat_caller_address(event_contract_address); +} + #[test] #[available_gas(2000000)] fn test_event_owner() {