Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions contracts/escrow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,37 @@ impl EscrowContract {
env.storage().instance().set(&DataKey::MatchCount, &next_id);
env.storage().persistent().set(&DataKey::GameId(m.game_id.clone()), &true);

// Add match ID to both players' match lists
let mut player1_matches: soroban_sdk::Vec<u64> = env
.storage()
.persistent()
.get(&DataKey::PlayerMatches(player1.clone()))
.unwrap_or_else(|| soroban_sdk::vec![&env]);
player1_matches.push_back(id);
env.storage()
.persistent()
.set(&DataKey::PlayerMatches(player1.clone()), &player1_matches);
env.storage().persistent().extend_ttl(
&DataKey::PlayerMatches(player1),
MATCH_TTL_LEDGERS,
MATCH_TTL_LEDGERS,
);

let mut player2_matches: soroban_sdk::Vec<u64> = env
.storage()
.persistent()
.get(&DataKey::PlayerMatches(player2.clone()))
.unwrap_or_else(|| soroban_sdk::vec![&env]);
player2_matches.push_back(id);
env.storage()
.persistent()
.set(&DataKey::PlayerMatches(player2.clone()), &player2_matches);
env.storage().persistent().extend_ttl(
&DataKey::PlayerMatches(player2),
MATCH_TTL_LEDGERS,
MATCH_TTL_LEDGERS,
);

env.events().publish(
(Symbol::new(&env, "match"), symbol_short!("created")),
(id, m.player1, m.player2, stake_amount),
Expand Down Expand Up @@ -595,6 +626,24 @@ impl EscrowContract {

Ok(live_matches)
}

/// Return the total number of matches created.
pub fn get_match_count(env: Env) -> Result<u64, Error> {
Ok(env
.storage()
.instance()
.get(&DataKey::MatchCount)
.unwrap_or(0))
}

/// Return all match IDs for a given player (past and present).
pub fn get_player_matches(env: Env, player: Address) -> Result<soroban_sdk::Vec<u64>, Error> {
Ok(env
.storage()
.persistent()
.get(&DataKey::PlayerMatches(player))
.unwrap_or_else(|| soroban_sdk::vec![&env]))
}
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions contracts/escrow/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod admin;
mod events;
mod index;
mod lifecycle;
mod pagination;
mod ttl;
mod token_allowlist;

Expand Down
228 changes: 228 additions & 0 deletions contracts/escrow/src/tests/pagination.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
use super::*;

/// Test #580: player-match pagination handles empty and partial pages
#[test]
fn test_player_match_pagination_handles_empty_and_partial_pages() {
let (env, contract_id, _oracle, player1, player2, token, _admin) = setup();
let client = EscrowContractClient::new(&env, &contract_id);

// Create 25 matches for player1
let mut match_ids = Vec::new();
for i in 0..25 {
let match_id = client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, &format!("game_{}", i)),
&Platform::Lichess,
);
match_ids.push(match_id);
}

// Query player1's matches
let player1_matches = client.get_player_matches(&player1);
assert_eq!(player1_matches.len(), 25);

// Verify all match IDs are present in order
for (i, match_id) in match_ids.iter().enumerate() {
assert_eq!(player1_matches.get(i as u32).unwrap(), *match_id);
}

// Query player2's matches (should have 25 as well)
let player2_matches = client.get_player_matches(&player2);
assert_eq!(player2_matches.len(), 25);

// Query a player with no matches
let player3 = Address::generate(&env);
let player3_matches = client.get_player_matches(&player3);
assert_eq!(player3_matches.len(), 0);
}

/// Test #579: player history index excludes unrelated matches for other players
#[test]
fn test_player_history_index_excludes_unrelated_matches() {
let (env, contract_id, _oracle, player1, player2, token, _admin) = setup();
let client = EscrowContractClient::new(&env, &contract_id);

let player3 = Address::generate(&env);
let player4 = Address::generate(&env);

// Mint tokens for player3 and player4
let token_client = TokenClient::new(&env, &token);
token_client.mint(&player3, &1000);
token_client.mint(&player4, &1000);

// Create matches for player1 and player2
let match_id_1 = client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_1"),
&Platform::Lichess,
);

let match_id_2 = client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_2"),
&Platform::Lichess,
);

// Create matches for player3 and player4
let match_id_3 = client.create_match(
&player3,
&player4,
&100,
&token,
&String::from_str(&env, "game_3"),
&Platform::Lichess,
);

let match_id_4 = client.create_match(
&player3,
&player4,
&100,
&token,
&String::from_str(&env, "game_4"),
&Platform::Lichess,
);

// Assert player1 only receives their own match IDs
let player1_matches = client.get_player_matches(&player1);
assert_eq!(player1_matches.len(), 2);
assert_eq!(player1_matches.get(0).unwrap(), match_id_1);
assert_eq!(player1_matches.get(1).unwrap(), match_id_2);

// Assert player2 only receives their own match IDs
let player2_matches = client.get_player_matches(&player2);
assert_eq!(player2_matches.len(), 2);
assert_eq!(player2_matches.get(0).unwrap(), match_id_1);
assert_eq!(player2_matches.get(1).unwrap(), match_id_2);

// Assert player3 only receives their own match IDs
let player3_matches = client.get_player_matches(&player3);
assert_eq!(player3_matches.len(), 2);
assert_eq!(player3_matches.get(0).unwrap(), match_id_3);
assert_eq!(player3_matches.get(1).unwrap(), match_id_4);

// Assert player4 only receives their own match IDs
let player4_matches = client.get_player_matches(&player4);
assert_eq!(player4_matches.len(), 2);
assert_eq!(player4_matches.get(0).unwrap(), match_id_3);
assert_eq!(player4_matches.get(1).unwrap(), match_id_4);
}

/// Test #578: get_player_matches preserves insertion order
#[test]
fn test_get_player_matches_preserves_insertion_order() {
let (env, contract_id, _oracle, player1, player2, token, _admin) = setup();
let client = EscrowContractClient::new(&env, &contract_id);

// Create multiple matches for the same player
let match_id_1 = client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_1"),
&Platform::Lichess,
);

let match_id_2 = client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_2"),
&Platform::Lichess,
);

let match_id_3 = client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_3"),
&Platform::Lichess,
);

let match_id_4 = client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_4"),
&Platform::Lichess,
);

// Assert returned IDs are in expected order
let player1_matches = client.get_player_matches(&player1);
assert_eq!(player1_matches.len(), 4);
assert_eq!(player1_matches.get(0).unwrap(), match_id_1);
assert_eq!(player1_matches.get(1).unwrap(), match_id_2);
assert_eq!(player1_matches.get(2).unwrap(), match_id_3);
assert_eq!(player1_matches.get(3).unwrap(), match_id_4);
}

/// Test #577: get_match_count increments correctly
#[test]
fn test_get_match_count_increments_correctly() {
let (env, contract_id, _oracle, player1, player2, token, _admin) = setup();
let client = EscrowContractClient::new(&env, &contract_id);

// Initial count should be 0
let count = client.get_match_count();
assert_eq!(count, 0);

// Create first match
client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_1"),
&Platform::Lichess,
);
let count = client.get_match_count();
assert_eq!(count, 1);

// Create second match
client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_2"),
&Platform::Lichess,
);
let count = client.get_match_count();
assert_eq!(count, 2);

// Create third match
client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_3"),
&Platform::Lichess,
);
let count = client.get_match_count();
assert_eq!(count, 3);

// Create fourth match
client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "game_4"),
&Platform::Lichess,
);
let count = client.get_match_count();
assert_eq!(count, 4);
}
Loading