From 4887cb99f7793af7bf148a3b9ad6f8b1c86ac01b Mon Sep 17 00:00:00 2001 From: daniella-techie Date: Sat, 30 May 2026 13:01:32 +0100 Subject: [PATCH] feat: implement list_event_matches function (#802) Add list_event_matches to match.rs that retrieves all matches for a given event, sorted by match_time ascending. Exposes the function via the contract's public interface and validates that the event exists before reading its match list. Unit tests cover the full result set, empty events, sort order, and the non-existent event error path. --- contracts/creator-event-manager/src/lib.rs | 18 +++- contracts/creator-event-manager/src/match.rs | 49 ++++++++++- .../tests/match_tests.rs | 88 +++++++++++++++++++ 3 files changed, 153 insertions(+), 2 deletions(-) diff --git a/contracts/creator-event-manager/src/lib.rs b/contracts/creator-event-manager/src/lib.rs index aab3a405..efe5f425 100644 --- a/contracts/creator-event-manager/src/lib.rs +++ b/contracts/creator-event-manager/src/lib.rs @@ -17,7 +17,7 @@ use soroban_sdk::{contract, contractimpl, Address, Env, String, Symbol, Vec}; use admin::AdminError; use event::EventError; -use storage_types::{Event, Prediction, Winner}; +use storage_types::{Event, Match, Prediction, Winner}; use verification::VerificationError; use views::EventStatistics; @@ -328,6 +328,22 @@ impl CreatorEventManagerContract { } } + /// Retrieve all matches for an event, sorted by `match_time` ascending. + /// + /// Returns a `Vec` containing every match that belongs to the given + /// event, ordered from earliest to latest scheduled start time. Returns an + /// empty `Vec` when the event exists but has no matches. + /// + /// # Panics + /// * `"event_not_found"` — no event exists with the given ID. + pub fn list_event_matches(env: Env, event_id: u64) -> Vec { + match r#match::list_event_matches(&env, event_id) { + Ok(matches) => matches, + Err(EventError::EventNotFound) => panic!("event_not_found"), + Err(_) => panic!("unexpected_error"), + } + } + /// Return a snapshot of the contract configuration. pub fn get_config(env: Env) -> views::Config { match views::get_config(&env) { diff --git a/contracts/creator-event-manager/src/match.rs b/contracts/creator-event-manager/src/match.rs index 9078838c..289beaca 100644 --- a/contracts/creator-event-manager/src/match.rs +++ b/contracts/creator-event-manager/src/match.rs @@ -1,6 +1,8 @@ -use soroban_sdk::Env; +use soroban_sdk::{Env, Vec}; use crate::event::{self, EventError}; +use crate::storage::{self}; +use crate::storage_types::Match; /// Return the number of matches currently stored for an event. /// @@ -10,3 +12,48 @@ pub fn get_match_count(env: &Env, event_id: u64) -> Result { let event = event::get_event(env, event_id)?; Ok(event.match_count) } + +/// Retrieve all matches for a specific event, sorted by `match_time` ascending. +/// +/// Looks up the `EventMatches(event_id)` index, fetches each [`Match`] struct, +/// and returns them in chronological order (earliest match first). +/// +/// # Sorting behaviour +/// Results are sorted by `match_time` ascending using an insertion sort. +/// Matches are appended in creation order, which may differ from schedule +/// order; the explicit sort guarantees correct ordering regardless. +/// +/// # Errors +/// Returns [`EventError::EventNotFound`] when `event_id` does not exist. +pub fn list_event_matches(env: &Env, event_id: u64) -> Result, EventError> { + // Verify the event exists before reading its match list. + event::get_event(env, event_id)?; + + let match_ids = storage::get_event_matches(env, event_id); + + let mut matches: Vec = Vec::new(env); + for match_id in match_ids.iter() { + if let Ok(m) = storage::get_match(env, match_id) { + matches.push_back(m); + } + } + + // Sort by match_time ascending (insertion sort — list is typically small). + let len = matches.len(); + for i in 1..len { + let mut j = i; + while j > 0 { + let prev = matches.get(j - 1).unwrap(); + let curr = matches.get(j).unwrap(); + if prev.match_time > curr.match_time { + matches.set(j - 1, curr); + matches.set(j, prev); + j -= 1; + } else { + break; + } + } + } + + Ok(matches) +} diff --git a/contracts/creator-event-manager/tests/match_tests.rs b/contracts/creator-event-manager/tests/match_tests.rs index c89a5952..7442600c 100644 --- a/contracts/creator-event-manager/tests/match_tests.rs +++ b/contracts/creator-event-manager/tests/match_tests.rs @@ -93,3 +93,91 @@ fn test_get_match_count_missing_event_panics() { let (_env, client, _contract_id, _admin, _xlm_token) = setup(); client.get_match_count(&999u64); } + +// --------------------------------------------------------------------------- +// list_event_matches tests +// --------------------------------------------------------------------------- + +fn add_match( + env: &Env, + contract_id: &Address, + event_id: u64, + team_a: &str, + team_b: &str, + match_time: u64, +) -> u64 { + env.as_contract(contract_id, || { + let mut event = storage::get_event(env, event_id).expect("event exists"); + event.add_match(); + storage::set_event(env, event_id, &event); + + let match_id = storage::next_match_id(env); + let match_record = creator_event_manager::storage_types::Match::new( + match_id, + event_id, + String::from_str(env, team_a), + String::from_str(env, team_b), + match_time, + ); + storage::set_match(env, match_id, &match_record); + storage::add_event_match(env, event_id, match_id); + match_id + }) +} + +#[test] +fn test_list_event_matches_returns_all_matches() { + let (env, client, contract_id, _admin, xlm_token) = setup(); + let creator = Address::generate(&env); + fund(&env, &xlm_token, &creator, FEE); + + let (event_id, _) = client.create_event(&creator, &title(&env), &desc(&env), &5u32); + + let base_time = 1_000_000u64; + add_match(&env, &contract_id, event_id, "Team A", "Team B", base_time + 3000); + add_match(&env, &contract_id, event_id, "Team C", "Team D", base_time + 1000); + add_match(&env, &contract_id, event_id, "Team E", "Team F", base_time + 2000); + + let matches = client.list_event_matches(&event_id); + assert_eq!(matches.len(), 3); +} + +#[test] +fn test_list_event_matches_empty_for_new_event() { + let (env, client, _contract_id, _admin, xlm_token) = setup(); + let creator = Address::generate(&env); + fund(&env, &xlm_token, &creator, FEE); + + let (event_id, _) = client.create_event(&creator, &title(&env), &desc(&env), &5u32); + + let matches = client.list_event_matches(&event_id); + assert_eq!(matches.len(), 0); +} + +#[test] +fn test_list_event_matches_sorted_by_match_time_ascending() { + let (env, client, contract_id, _admin, xlm_token) = setup(); + let creator = Address::generate(&env); + fund(&env, &xlm_token, &creator, FEE); + + let (event_id, _) = client.create_event(&creator, &title(&env), &desc(&env), &5u32); + + let base_time = 2_000_000u64; + // Insert in reverse order to ensure sort is applied. + add_match(&env, &contract_id, event_id, "Team A", "Team B", base_time + 3000); + add_match(&env, &contract_id, event_id, "Team C", "Team D", base_time + 1000); + add_match(&env, &contract_id, event_id, "Team E", "Team F", base_time + 2000); + + let matches = client.list_event_matches(&event_id); + assert_eq!(matches.len(), 3); + assert_eq!(matches.get(0).unwrap().match_time, base_time + 1000); + assert_eq!(matches.get(1).unwrap().match_time, base_time + 2000); + assert_eq!(matches.get(2).unwrap().match_time, base_time + 3000); +} + +#[test] +#[should_panic(expected = "event_not_found")] +fn test_list_event_matches_nonexistent_event_panics() { + let (_env, client, _contract_id, _admin, _xlm_token) = setup(); + client.list_event_matches(&999u64); +}