From d20c843fc6e5e478b8b54a805453605854f03deb Mon Sep 17 00:00:00 2001 From: Daniel Akinsanya Date: Thu, 22 Jan 2026 15:45:49 +0100 Subject: [PATCH] feat: implement reschedule_event function for event_manager contract - Add Event struct with all required fields - Implement reschedule_event with organizer auth, existence check, and end date validation - Add get_event and get_event_count helper functions - Add unit tests for reschedule functionality Closes #6 --- .../contracts/event_manager/Cargo.toml | 14 ++ .../contracts/event_manager/src/lib.rs | 121 ++++++++++++++++++ .../contracts/event_manager/src/test.rs | 115 +++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 soroban-contract/contracts/event_manager/src/test.rs diff --git a/soroban-contract/contracts/event_manager/Cargo.toml b/soroban-contract/contracts/event_manager/Cargo.toml index e69de29b..e0f85c95 100644 --- a/soroban-contract/contracts/event_manager/Cargo.toml +++ b/soroban-contract/contracts/event_manager/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "event_manager" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/soroban-contract/contracts/event_manager/src/lib.rs b/soroban-contract/contracts/event_manager/src/lib.rs index e69de29b..ee7d10ab 100644 --- a/soroban-contract/contracts/event_manager/src/lib.rs +++ b/soroban-contract/contracts/event_manager/src/lib.rs @@ -0,0 +1,121 @@ +#![no_std] +use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Address, Env, Symbol}; + +/// Storage keys for the contract +#[derive(Clone)] +#[contracttype] +pub enum DataKey { + EventCount, + Event(u32), +} + +/// Event data structure +#[derive(Clone)] +#[contracttype] +pub struct Event { + pub id: u32, + pub theme: Symbol, + pub organizer: Address, + pub event_type: Symbol, + pub total_tickets: u128, + pub tickets_sold: u128, + pub ticket_price: u128, + pub start_date: u64, + pub end_date: u64, + pub is_canceled: bool, + pub event_ticket_addr: Address, +} + +#[contract] +pub struct EventManagerContract; + +#[contractimpl] +impl EventManagerContract { + /// Reschedule an existing event with new start and end dates. + /// + /// # Arguments + /// * `env` - The contract environment + /// * `event_id` - The ID of the event to reschedule + /// * `start_date` - The new start date (unix timestamp) + /// * `end_date` - The new end date (unix timestamp) + /// + /// # Panics + /// * If the event does not exist + /// * If the caller is not the event organizer + /// * If the event has already ended + pub fn reschedule_event(env: Env, event_id: u32, start_date: u64, end_date: u64) { + // Get the event count to verify event exists + let event_count: u32 = env + .storage() + .instance() + .get(&DataKey::EventCount) + .unwrap_or(0); + + // Check that event exists + if event_id == 0 || event_id > event_count { + panic!("Event does not exist"); + } + + // Get the event from storage + let event: Event = env + .storage() + .instance() + .get(&DataKey::Event(event_id)) + .expect("Event not found"); + + // Verify the organizer is authorized to reschedule + // require_auth ensures only the organizer can call this function + event.organizer.require_auth(); + + // Check that the event has not ended yet + let current_timestamp = env.ledger().timestamp(); + if event.end_date <= current_timestamp { + panic!("Event has already ended"); + } + + // Create updated event with new dates + let updated_event = Event { + id: event.id, + theme: event.theme, + organizer: event.organizer, + event_type: event.event_type, + total_tickets: event.total_tickets, + tickets_sold: event.tickets_sold, + ticket_price: event.ticket_price, + start_date, + end_date, + is_canceled: false, + event_ticket_addr: event.event_ticket_addr, + }; + + // Store the updated event + env.storage() + .instance() + .set(&DataKey::Event(event_id), &updated_event); + + // Emit EventRescheduled event + env.events().publish( + (symbol_short!("resched"), event_id), + (start_date, end_date), + ); + } + + /// Get an event by its ID + pub fn get_event(env: Env, event_id: u32) -> Event { + env.storage() + .instance() + .get(&DataKey::Event(event_id)) + .expect("Event not found") + } + + /// Get the total number of events + pub fn get_event_count(env: Env) -> u32 { + env.storage() + .instance() + .get(&DataKey::EventCount) + .unwrap_or(0) + } +} + +#[cfg(test)] +mod test; diff --git a/soroban-contract/contracts/event_manager/src/test.rs b/soroban-contract/contracts/event_manager/src/test.rs new file mode 100644 index 00000000..280324b6 --- /dev/null +++ b/soroban-contract/contracts/event_manager/src/test.rs @@ -0,0 +1,115 @@ +#![cfg(test)] + +use super::*; +use soroban_sdk::{testutils::Address as _, testutils::Ledger, Address, Env, Symbol}; + +/// Helper function to create a test event in storage +fn setup_test_event(env: &Env, organizer: &Address, event_id: u32, end_date: u64) { + let ticket_addr = Address::generate(env); + + let event = Event { + id: event_id, + theme: Symbol::new(env, "test_theme"), + organizer: organizer.clone(), + event_type: Symbol::new(env, "conference"), + total_tickets: 100, + tickets_sold: 0, + ticket_price: 1000, + start_date: 1000, + end_date, + is_canceled: false, + event_ticket_addr: ticket_addr, + }; + + env.storage() + .instance() + .set(&DataKey::EventCount, &event_id); + env.storage() + .instance() + .set(&DataKey::Event(event_id), &event); +} + +#[test] +fn test_reschedule_event_success() { + let env = Env::default(); + env.mock_all_auths(); + + let contract_id = env.register(EventManagerContract, ()); + let client = EventManagerContractClient::new(&env, &contract_id); + + let organizer = Address::generate(&env); + + // Set up a test event with end_date in the future + env.ledger().set_timestamp(500); + env.as_contract(&contract_id, || { + setup_test_event(&env, &organizer, 1, 2000); + }); + + // Reschedule the event + let new_start_date: u64 = 1500; + let new_end_date: u64 = 3000; + client.reschedule_event(&1, &new_start_date, &new_end_date); + + // Verify the event was updated + let updated_event = client.get_event(&1); + assert_eq!(updated_event.start_date, new_start_date); + assert_eq!(updated_event.end_date, new_end_date); + assert_eq!(updated_event.is_canceled, false); +} + +#[test] +#[should_panic(expected = "Event does not exist")] +fn test_reschedule_nonexistent_event() { + let env = Env::default(); + env.mock_all_auths(); + + let contract_id = env.register(EventManagerContract, ()); + let client = EventManagerContractClient::new(&env, &contract_id); + + // Try to reschedule an event that doesn't exist + client.reschedule_event(&999, &1500, &3000); +} + +#[test] +#[should_panic(expected = "Event has already ended")] +fn test_reschedule_ended_event() { + let env = Env::default(); + env.mock_all_auths(); + + let contract_id = env.register(EventManagerContract, ()); + let client = EventManagerContractClient::new(&env, &contract_id); + + let organizer = Address::generate(&env); + + // Set up a test event with end_date in the past + env.as_contract(&contract_id, || { + setup_test_event(&env, &organizer, 1, 1000); + }); + + // Set current timestamp after the event end + env.ledger().set_timestamp(2000); + + // Try to reschedule - should fail because event has ended + client.reschedule_event(&1, &2500, &3500); +} + +#[test] +fn test_get_event_count() { + let env = Env::default(); + + let contract_id = env.register(EventManagerContract, ()); + let client = EventManagerContractClient::new(&env, &contract_id); + + // Initially should be 0 + assert_eq!(client.get_event_count(), 0); + + let organizer = Address::generate(&env); + + // Add an event + env.as_contract(&contract_id, || { + setup_test_event(&env, &organizer, 1, 2000); + }); + + // Should now be 1 + assert_eq!(client.get_event_count(), 1); +}