From e50c23ad17171af06607fb52288977543cbe5c27 Mon Sep 17 00:00:00 2001 From: NEVEROV iaroslav Date: Fri, 29 Nov 2024 07:34:14 +0100 Subject: [PATCH] getter functions and necessary tests with negative scenarious as well --- .../bytebeasts-Leaderboard-7e680376.json | 467 ++++++++++++++++++ .../bytebeasts-LeaderboardEntry-7237950c.json | 441 +++++++++++++++++ .../bytebeasts-Leaderboard-7e680376.toml | 31 ++ .../bytebeasts-LeaderboardEntry-7237950c.toml | 41 ++ src/models/leaderboard.cairo | 165 ++++++- 5 files changed, 1139 insertions(+), 6 deletions(-) create mode 100644 manifests/dev/base/abis/models/bytebeasts-Leaderboard-7e680376.json create mode 100644 manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json create mode 100644 manifests/dev/base/models/bytebeasts-Leaderboard-7e680376.toml create mode 100644 manifests/dev/base/models/bytebeasts-LeaderboardEntry-7237950c.toml diff --git a/manifests/dev/base/abis/models/bytebeasts-Leaderboard-7e680376.json b/manifests/dev/base/abis/models/bytebeasts-Leaderboard-7e680376.json new file mode 100644 index 0000000..462aba6 --- /dev/null +++ b/manifests/dev/base/abis/models/bytebeasts-Leaderboard-7e680376.json @@ -0,0 +1,467 @@ +[ + { + "type": "impl", + "name": "DojoModelImpl", + "interface_name": "dojo::model::model::IModel" + }, + { + "type": "struct", + "name": "core::byte_array::ByteArray", + "members": [ + { + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" + } + ] + }, + { + "type": "enum", + "name": "core::option::Option::", + "variants": [ + { + "name": "Some", + "type": "core::integer::u32" + }, + { + "name": "None", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::layout::FieldLayout", + "members": [ + { + "name": "selector", + "type": "core::felt252" + }, + { + "name": "layout", + "type": "dojo::model::layout::Layout" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::model::layout::Layout", + "variants": [ + { + "name": "Fixed", + "type": "core::array::Span::" + }, + { + "name": "Struct", + "type": "core::array::Span::" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + }, + { + "name": "Enum", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::introspect::Member", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "ty", + "type": "dojo::model::introspect::Ty" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::introspect::Struct", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::<(core::felt252, dojo::model::introspect::Ty)>", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::<(core::felt252, dojo::model::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::introspect::Enum", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::<(core::felt252, dojo::model::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::model::introspect::Ty", + "variants": [ + { + "name": "Primitive", + "type": "core::felt252" + }, + { + "name": "Struct", + "type": "dojo::model::introspect::Struct" + }, + { + "name": "Enum", + "type": "dojo::model::introspect::Enum" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + } + ] + }, + { + "type": "interface", + "name": "dojo::model::model::IModel", + "items": [ + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "version", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u8" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "name_hash", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_hash", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "unpacked_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "packed_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "layout", + "inputs": [], + "outputs": [ + { + "type": "dojo::model::layout::Layout" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "schema", + "inputs": [], + "outputs": [ + { + "type": "dojo::model::introspect::Ty" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "leaderboardImpl", + "interface_name": "bytebeasts::models::leaderboard::Ileaderboard" + }, + { + "type": "enum", + "name": "core::bool", + "variants": [ + { + "name": "False", + "type": "()" + }, + { + "name": "True", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "bytebeasts::models::leaderboard::LeaderboardEntry", + "members": [ + { + "name": "player_id", + "type": "core::integer::u32" + }, + { + "name": "player_name", + "type": "core::felt252" + }, + { + "name": "score", + "type": "core::integer::u32" + }, + { + "name": "wins", + "type": "core::integer::u32" + }, + { + "name": "losses", + "type": "core::integer::u32" + }, + { + "name": "highest_score", + "type": "core::integer::u32" + }, + { + "name": "is_active", + "type": "core::bool" + } + ] + }, + { + "type": "struct", + "name": "bytebeasts::models::leaderboard::Leaderboard", + "members": [ + { + "name": "leaderboard_id", + "type": "core::integer::u64" + }, + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "description", + "type": "core::felt252" + }, + { + "name": "entries", + "type": "core::array::Array::" + }, + { + "name": "last_updated", + "type": "core::integer::u64" + } + ] + }, + { + "type": "interface", + "name": "bytebeasts::models::leaderboard::Ileaderboard", + "items": [ + { + "type": "function", + "name": "ensure_abi", + "inputs": [ + { + "name": "model", + "type": "bytebeasts::models::leaderboard::Leaderboard" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "bytebeasts::models::leaderboard::leaderboard::Event", + "kind": "enum", + "variants": [] + } +] \ No newline at end of file diff --git a/manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json b/manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json new file mode 100644 index 0000000..6723d2a --- /dev/null +++ b/manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json @@ -0,0 +1,441 @@ +[ + { + "type": "impl", + "name": "DojoModelImpl", + "interface_name": "dojo::model::model::IModel" + }, + { + "type": "struct", + "name": "core::byte_array::ByteArray", + "members": [ + { + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" + } + ] + }, + { + "type": "enum", + "name": "core::option::Option::", + "variants": [ + { + "name": "Some", + "type": "core::integer::u32" + }, + { + "name": "None", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::layout::FieldLayout", + "members": [ + { + "name": "selector", + "type": "core::felt252" + }, + { + "name": "layout", + "type": "dojo::model::layout::Layout" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::model::layout::Layout", + "variants": [ + { + "name": "Fixed", + "type": "core::array::Span::" + }, + { + "name": "Struct", + "type": "core::array::Span::" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + }, + { + "name": "Enum", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::introspect::Member", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "ty", + "type": "dojo::model::introspect::Ty" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::introspect::Struct", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::<(core::felt252, dojo::model::introspect::Ty)>", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::<(core::felt252, dojo::model::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::introspect::Enum", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::<(core::felt252, dojo::model::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::model::introspect::Ty", + "variants": [ + { + "name": "Primitive", + "type": "core::felt252" + }, + { + "name": "Struct", + "type": "dojo::model::introspect::Struct" + }, + { + "name": "Enum", + "type": "dojo::model::introspect::Enum" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + } + ] + }, + { + "type": "interface", + "name": "dojo::model::model::IModel", + "items": [ + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "version", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u8" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "name_hash", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_hash", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "unpacked_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "packed_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "layout", + "inputs": [], + "outputs": [ + { + "type": "dojo::model::layout::Layout" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "schema", + "inputs": [], + "outputs": [ + { + "type": "dojo::model::introspect::Ty" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "leaderboard_entryImpl", + "interface_name": "bytebeasts::models::leaderboard::Ileaderboard_entry" + }, + { + "type": "enum", + "name": "core::bool", + "variants": [ + { + "name": "False", + "type": "()" + }, + { + "name": "True", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "bytebeasts::models::leaderboard::LeaderboardEntry", + "members": [ + { + "name": "player_id", + "type": "core::integer::u32" + }, + { + "name": "player_name", + "type": "core::felt252" + }, + { + "name": "score", + "type": "core::integer::u32" + }, + { + "name": "wins", + "type": "core::integer::u32" + }, + { + "name": "losses", + "type": "core::integer::u32" + }, + { + "name": "highest_score", + "type": "core::integer::u32" + }, + { + "name": "is_active", + "type": "core::bool" + } + ] + }, + { + "type": "interface", + "name": "bytebeasts::models::leaderboard::Ileaderboard_entry", + "items": [ + { + "type": "function", + "name": "ensure_abi", + "inputs": [ + { + "name": "model", + "type": "bytebeasts::models::leaderboard::LeaderboardEntry" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "bytebeasts::models::leaderboard::leaderboard_entry::Event", + "kind": "enum", + "variants": [] + } +] \ No newline at end of file diff --git a/manifests/dev/base/models/bytebeasts-Leaderboard-7e680376.toml b/manifests/dev/base/models/bytebeasts-Leaderboard-7e680376.toml new file mode 100644 index 0000000..b32b775 --- /dev/null +++ b/manifests/dev/base/models/bytebeasts-Leaderboard-7e680376.toml @@ -0,0 +1,31 @@ +kind = "DojoModel" +class_hash = "0x13d964c4091be478133a4aadbb278da14229f77225ce8da5dd246e42abc7ac0" +original_class_hash = "0x13d964c4091be478133a4aadbb278da14229f77225ce8da5dd246e42abc7ac0" +abi = "manifests/dev/base/abis/models/bytebeasts-Leaderboard-7e680376.json" +tag = "bytebeasts-Leaderboard" +manifest_name = "bytebeasts-Leaderboard-7e680376" + +[[members]] +name = "leaderboard_id" +type = "u64" +key = true + +[[members]] +name = "name" +type = "felt252" +key = false + +[[members]] +name = "description" +type = "felt252" +key = false + +[[members]] +name = "entries" +type = "Array" +key = false + +[[members]] +name = "last_updated" +type = "u64" +key = false diff --git a/manifests/dev/base/models/bytebeasts-LeaderboardEntry-7237950c.toml b/manifests/dev/base/models/bytebeasts-LeaderboardEntry-7237950c.toml new file mode 100644 index 0000000..b460d64 --- /dev/null +++ b/manifests/dev/base/models/bytebeasts-LeaderboardEntry-7237950c.toml @@ -0,0 +1,41 @@ +kind = "DojoModel" +class_hash = "0x4f9ca94349981f722e598f396bcca466289aca05dcfb277ef562ee02fee333c" +original_class_hash = "0x4f9ca94349981f722e598f396bcca466289aca05dcfb277ef562ee02fee333c" +abi = "manifests/dev/base/abis/models/bytebeasts-LeaderboardEntry-7237950c.json" +tag = "bytebeasts-LeaderboardEntry" +manifest_name = "bytebeasts-LeaderboardEntry-7237950c" + +[[members]] +name = "player_id" +type = "u32" +key = true + +[[members]] +name = "player_name" +type = "felt252" +key = false + +[[members]] +name = "score" +type = "u32" +key = false + +[[members]] +name = "wins" +type = "u32" +key = false + +[[members]] +name = "losses" +type = "u32" +key = false + +[[members]] +name = "highest_score" +type = "u32" +key = false + +[[members]] +name = "is_active" +type = "bool" +key = false diff --git a/src/models/leaderboard.cairo b/src/models/leaderboard.cairo index 72eccc9..c552664 100644 --- a/src/models/leaderboard.cairo +++ b/src/models/leaderboard.cairo @@ -15,7 +15,6 @@ pub struct LeaderboardEntry { pub player_id: u32, // player ID pub player_name: felt252, // Display name pub score: u32, // Overall score - pub rank: u32, // Rank in the leaderboard pub wins: u32, // Total wins pub losses: u32, // Total losses pub highest_score: u32, // Highest score in a single game @@ -70,8 +69,9 @@ impl LeaderboardImpl of LeaderboardTrait { // PRIVATE METHODS fn pop_front_n(ref self: Leaderboard, mut n: usize) -> Array { - // generic fn pop_front_n of ArrayTraitExt does not work properly, so its my implementation - // only for internal use + // pops n elements from the front of the array, and returns them + // Alexandria implementation of pop_front_n does not return the popped elements + // in the current version of the library let mut res: Array = array![]; while (n != 0) { @@ -98,14 +98,21 @@ impl LeaderboardImpl of LeaderboardTrait { Result::Ok(()) } + // PUBLIC METHODS + fn get_liderboard_length(ref self: Leaderboard) -> u32 { + // returns number of entries in the leaderboard + self.entries.len() + } + fn get_index_by_player_id(ref self: Leaderboard, player_id: u32) -> Result { + // returns index of entry with given player_id. Index stands for rank in the leaderboard + // player with highest score has index 0 let entry = LeaderboardEntry { player_id: player_id, player_name: '', score: 0, - rank: 0, wins: 0, losses: 0, highest_score: 0, @@ -161,7 +168,7 @@ impl LeaderboardImpl of LeaderboardTrait { } fn update_entry(ref self: Leaderboard, entry: LeaderboardEntry) -> Result<(), felt252> { - // updates user entry in leaderboard + // updates user entry in leaderboard, sorts array match self.remove_entry(entry) { Result::Ok(_) => { match self.add_entry(entry) { @@ -172,6 +179,35 @@ impl LeaderboardImpl of LeaderboardTrait { Result::Err(e) => Result::Err(e), } } + + fn get_entries(ref self: Leaderboard) -> Array { + // returns all entries in the leaderboard + self.entries.clone() + } + + fn get_slice(ref self: Leaderboard, start: u32, end: u32) -> Result, felt252> { + // returns entries from start to end (exclusive) + // can be used to get top of the leaderboard + let mut res: Array = array![]; + match self.entries.len() { + 0 => Result::Err('Leaderboard is empty'), + _ => { + if (start >= end) { + return Result::Err('Invalid range'); + } + if (end > self.entries.len()) { + return Result::Err('End index out of bounds'); + } + let mut i = start; + while (i < end) { + res.append(self.entries.at(i).clone()); + i += 1; + }; + Result::Ok(res) + }, + } + } + } #[cfg(test)] @@ -190,7 +226,6 @@ mod tests { player_id: player_id, player_name: name, score: score, - rank: 0, wins: wins, losses: losses, highest_score: highest_score, @@ -217,6 +252,8 @@ mod tests { assert_eq!(leaderboard.entries.len(), 1); assert_eq!(leaderboard.entries.at(0).player_name, @'Alice', "Wrong player name"); + let duplicate_res = leaderboard.add_entry(entry); + assert_eq!(duplicate_res.is_err(), true, "Duplicate entry should return error"); } #[test] @@ -236,6 +273,29 @@ mod tests { assert_eq!(not_added.len(), 2, "Wrong number of not added entries"); assert_eq!(leaderboard.entries.at(0).player_name, @'Bob', "Wrong first player name"); assert_eq!(leaderboard.entries.at(4).player_name, @'Eve', "Wrong last player name"); + + let duplicate_entries = array![entry1, entry2]; + let not_added_duplicates = leaderboard.add_batch(duplicate_entries); + assert_eq!(not_added_duplicates.len(), 2, "Duplicate entries should not be added"); + } + + #[test] + fn test_pop_front_n() { + let mut leaderboard = create_empty_leaderboard(); + let entry1 = create_mock_entry(12, 'Alice', 1100, 10, 5, 100, true); + let entry2 = create_mock_entry(2, 'Bob', 200121, 20, 10, 200, true); + let entry3 = create_mock_entry(34, 'Charlie', 1300, 30, 15, 300, true); + let _ = leaderboard.add_batch(array![entry1, entry2, entry3]); + let popped_entries = leaderboard.pop_front_n(2); + assert_eq!(popped_entries.len(), 2, "Wrong number of popped entries"); + assert_eq!(popped_entries.at(0).player_name, @'Bob', "Wrong first popped player name"); + assert_eq!(popped_entries.at(1).player_name, @'Charlie', "Wrong second popped player name"); + assert_eq!(leaderboard.entries.len(), 1, "Wrong number of remaining entries"); + assert_eq!(leaderboard.entries.at(0).player_name, @'Alice', "Wrong remaining player name"); + + let mut empty_leaderboard = create_empty_leaderboard(); + let popped_entries_empty = empty_leaderboard.pop_front_n(5); + assert_eq!(popped_entries_empty.len(), 0, "Popping from empty leaderboard should return empty array"); } #[test] @@ -253,5 +313,98 @@ mod tests { assert_eq!(res.is_ok(), true); assert_eq!(leaderboard.entries.len(), 4, "Wrong number of entries"); assert_eq!(leaderboard.entries.at(2).player_name, @'Alice', "Wrong player name"); + + let non_existent_entry = create_mock_entry(99, 'NonExistent', 0, 0, 0, 0, false); + let res_non_existent = leaderboard.remove_entry(non_existent_entry); + assert_eq!(res_non_existent.is_err(), true, "Removing non-existent entry should return error"); + } + + #[test] + fn test_get_index_by_player_id() { + let mut leaderboard = create_empty_leaderboard(); + let entry1 = create_mock_entry(12, 'Alice', 1100, 10, 5, 100, true); + let entry2 = create_mock_entry(2, 'Bob', 200121, 20, 10, 200, true); + let entry3 = create_mock_entry(34, 'Charlie', 1300, 30, 15, 300, true); + let entry4 = create_mock_entry(9, 'David', 22400, 40, 20, 400, true); + let entry5 = create_mock_entry(5, 'Eve', 500, 50, 25, 500, true); + + let _ = leaderboard.add_batch(array![entry1, entry2, entry3, entry4, entry5]); + let rank = leaderboard.get_index_by_player_id(34).unwrap(); + assert_eq!(rank, 2, "Wrong rank for Charlie"); + let rank = leaderboard.get_index_by_player_id(2).unwrap(); + assert_eq!(rank, 0, "Wrong rank for Bob"); + let rank = leaderboard.get_index_by_player_id(5).unwrap(); + assert_eq!(rank, 4, "Wrong rank for Eve"); + + let non_existent_rank = leaderboard.get_index_by_player_id(99); + assert_eq!(non_existent_rank.is_err(), true, "Getting index of non-existent player should return error"); + } + + #[test] + fn test_update_entry() { + let mut leaderboard = create_empty_leaderboard(); + let entry1 = create_mock_entry(12, 'Alice', 1100, 10, 5, 100, true); + let entry2 = create_mock_entry(2, 'Bob', 200121, 20, 10, 200, true); + let entry3 = create_mock_entry(34, 'Charlie', 1300, 30, 15, 300, true); + let entry4 = create_mock_entry(9, 'David', 22400, 40, 20, 400, true); + let entry5 = create_mock_entry(5, 'Eve', 500, 50, 25, 500, true); + + let _ = leaderboard.add_batch(array![entry1, entry2, entry3, entry4, entry5]); + let new_score: u32 = 100; + let new_wins: u32 = 31; + let updated_entry = create_mock_entry(34, 'Charlie', new_score, new_wins, 15, 300, true); + let res = leaderboard.update_entry(updated_entry); + let rank = leaderboard.get_index_by_player_id(34).unwrap(); + assert_eq!(res.is_ok(), true); + assert_eq!(rank, 4, "Wrong rank"); + assert_eq!(leaderboard.entries.len(), 5, "Wrong number of entries"); + assert_eq!(leaderboard.entries.at(rank).score, @new_score, "Wrong score"); + assert_eq!(leaderboard.entries.at(rank).wins, @new_wins, "Wrong wins"); + + let non_existent_entry = create_mock_entry(99, 'NonExistent', 0, 0, 0, 0, false); + let res_non_existent = leaderboard.update_entry(non_existent_entry); + assert_eq!(res_non_existent.is_err(), true, "Updating non-existent entry should return error"); + } + + #[test] + fn test_get_entries() { + let mut leaderboard = create_empty_leaderboard(); + let entry1 = create_mock_entry(12, 'Alice', 1100, 10, 5, 100, true); + let entry2 = create_mock_entry(2, 'Bob', 200121, 20, 10, 200, true); + let _ = leaderboard.add_batch(array![entry1, entry2]); + let entries = leaderboard.get_entries(); + assert_eq!(entries.len(), 2, "Wrong number of entries"); + assert_eq!(entries.at(0).player_name, @'Bob', "Wrong first player name"); + assert_eq!(entries.at(1).player_name, @'Alice', "Wrong second player name"); + + let mut empty_leaderboard = create_empty_leaderboard(); + let entries_empty = empty_leaderboard.get_entries(); + assert_eq!(entries_empty.len(), 0, "Empty leaderboard should return empty array"); + } + + #[test] + fn test_get_slice() { + let mut leaderboard = create_empty_leaderboard(); + let entry1 = create_mock_entry(12, 'Alice', 1100, 10, 5, 100, true); + let entry2 = create_mock_entry(2, 'Bob', 200121, 20, 10, 200, true); + let entry3 = create_mock_entry(34, 'Charlie', 1300, 30, 15, 300, true); + let entry4 = create_mock_entry(9, 'David', 22400, 40, 20, 400, true); + let entry5 = create_mock_entry(5, 'Eve', 500, 50, 25, 500, true); + + let _ = leaderboard.add_batch(array![entry1, entry2, entry3, entry4, entry5]); + let slice = leaderboard.get_slice(1, 4).unwrap(); + assert_eq!(slice.len(), 3, "Wrong number of entries in slice"); + assert_eq!(slice.at(0).player_name, @'David', "Wrong first player name in slice"); + assert_eq!(slice.at(2).player_name, @'Alice', "Wrong last player name in slice"); + + let invalid_slice = leaderboard.get_slice(4, 1); + assert_eq!(invalid_slice.is_err(), true, "Invalid slice range should return error"); + + let out_of_bounds_slice = leaderboard.get_slice(1, 10); + assert_eq!(out_of_bounds_slice.is_err(), true, "Out of bounds slice should return error"); + + let mut empty_leaderboard = create_empty_leaderboard(); + let empty_slice = empty_leaderboard.get_slice(0, 1); + assert_eq!(empty_slice.is_err(), true, "Empty leaderboard should return error"); } }