From 194c3c82899a2e31d317e7536a0af3bec15bc75e Mon Sep 17 00:00:00 2001 From: saimeunt Date: Tue, 3 Dec 2024 10:27:41 +0100 Subject: [PATCH] Add tests for tournament_system (#101) * Add tests for tournament_system * Quickfixes * sozo build --- .tool-versions | 2 + ...bytebeasts-tournament_system-1f2bbf20.json | 365 ++++++++++++++ .../bytebeasts-Tournament-12bdecb1.json | 445 ++++++++++++++++++ ...bytebeasts-tournament_system-1f2bbf20.toml | 10 + .../bytebeasts-Tournament-12bdecb1.toml | 41 ++ src/lib.cairo | 1 + src/models/tournament.cairo | 21 +- src/systems/tournament.cairo | 38 +- src/tests/test_tournament.cairo | 313 ++++++++++++ 9 files changed, 1210 insertions(+), 26 deletions(-) create mode 100644 .tool-versions create mode 100644 manifests/dev/base/abis/contracts/bytebeasts-tournament_system-1f2bbf20.json create mode 100644 manifests/dev/base/abis/models/bytebeasts-Tournament-12bdecb1.json create mode 100644 manifests/dev/base/contracts/bytebeasts-tournament_system-1f2bbf20.toml create mode 100644 manifests/dev/base/models/bytebeasts-Tournament-12bdecb1.toml create mode 100644 src/tests/test_tournament.cairo diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..366991b --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +scarb 2.7.0 +dojo 1.0.0-alpha.5 diff --git a/manifests/dev/base/abis/contracts/bytebeasts-tournament_system-1f2bbf20.json b/manifests/dev/base/abis/contracts/bytebeasts-tournament_system-1f2bbf20.json new file mode 100644 index 0000000..682a26f --- /dev/null +++ b/manifests/dev/base/abis/contracts/bytebeasts-tournament_system-1f2bbf20.json @@ -0,0 +1,365 @@ +[ + { + "type": "impl", + "name": "ContractImpl", + "interface_name": "dojo::contract::contract::IContract" + }, + { + "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": "interface", + "name": "dojo::contract::contract::IContract", + "items": [ + { + "type": "function", + "name": "contract_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": "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": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "WorldProviderImpl", + "interface_name": "dojo::world::world_contract::IWorldProvider" + }, + { + "type": "struct", + "name": "dojo::world::world_contract::IWorldDispatcher", + "members": [ + { + "name": "contract_address", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "type": "interface", + "name": "dojo::world::world_contract::IWorldProvider", + "items": [ + { + "type": "function", + "name": "world", + "inputs": [], + "outputs": [ + { + "type": "dojo::world::world_contract::IWorldDispatcher" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "TournamentActionImpl", + "interface_name": "bytebeasts::systems::tournament::ITournamentAction" + }, + { + "type": "enum", + "name": "bytebeasts::models::tournament::TournamentStatus", + "variants": [ + { + "name": "Pending", + "type": "()" + }, + { + "name": "Ongoing", + "type": "()" + }, + { + "name": "Completed", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "bytebeasts::models::tournament::Tournament", + "members": [ + { + "name": "tournament_id", + "type": "core::integer::u32" + }, + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "status", + "type": "bytebeasts::models::tournament::TournamentStatus" + }, + { + "name": "entry_fee", + "type": "core::integer::u32" + }, + { + "name": "max_participants", + "type": "core::integer::u32" + }, + { + "name": "current_participants", + "type": "core::array::Array::" + }, + { + "name": "prize_pool", + "type": "core::integer::u32" + } + ] + }, + { + "type": "interface", + "name": "bytebeasts::systems::tournament::ITournamentAction", + "items": [ + { + "type": "function", + "name": "create_tournament", + "inputs": [ + { + "name": "tournament_id", + "type": "core::integer::u32" + }, + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "status", + "type": "bytebeasts::models::tournament::TournamentStatus" + }, + { + "name": "entry_fee", + "type": "core::integer::u32" + }, + { + "name": "max_participants", + "type": "core::integer::u32" + }, + { + "name": "current_participants", + "type": "core::array::Array::" + }, + { + "name": "prize_pool", + "type": "core::integer::u32" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "register_player", + "inputs": [ + { + "name": "tournament_id", + "type": "core::integer::u32" + }, + { + "name": "new_player_id", + "type": "core::integer::u32" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "start_tournament", + "inputs": [ + { + "name": "tournament_id", + "type": "core::integer::u32" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "complete_tournament", + "inputs": [ + { + "name": "tournament_id", + "type": "core::integer::u32" + }, + { + "name": "player_id", + "type": "core::integer::u32" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "get_tournament", + "inputs": [ + { + "name": "tournament_id", + "type": "core::integer::u32" + } + ], + "outputs": [ + { + "type": "bytebeasts::models::tournament::Tournament" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "IDojoInitImpl", + "interface_name": "bytebeasts::systems::tournament::tournament_system::IDojoInit" + }, + { + "type": "interface", + "name": "bytebeasts::systems::tournament::tournament_system::IDojoInit", + "items": [ + { + "type": "function", + "name": "dojo_init", + "inputs": [], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "UpgradableImpl", + "interface_name": "dojo::contract::upgradeable::IUpgradeable" + }, + { + "type": "interface", + "name": "dojo::contract::upgradeable::IUpgradeable", + "items": [ + { + "type": "function", + "name": "upgrade", + "inputs": [ + { + "name": "new_class_hash", + "type": "core::starknet::class_hash::ClassHash" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "type": "event", + "name": "dojo::contract::upgradeable::upgradeable::Upgraded", + "kind": "struct", + "members": [ + { + "name": "class_hash", + "type": "core::starknet::class_hash::ClassHash", + "kind": "data" + } + ] + }, + { + "type": "event", + "name": "dojo::contract::upgradeable::upgradeable::Event", + "kind": "enum", + "variants": [ + { + "name": "Upgraded", + "type": "dojo::contract::upgradeable::upgradeable::Upgraded", + "kind": "nested" + } + ] + }, + { + "type": "event", + "name": "bytebeasts::systems::tournament::tournament_system::Event", + "kind": "enum", + "variants": [ + { + "name": "UpgradeableEvent", + "type": "dojo::contract::upgradeable::upgradeable::Event", + "kind": "nested" + } + ] + } +] \ No newline at end of file diff --git a/manifests/dev/base/abis/models/bytebeasts-Tournament-12bdecb1.json b/manifests/dev/base/abis/models/bytebeasts-Tournament-12bdecb1.json new file mode 100644 index 0000000..9f93476 --- /dev/null +++ b/manifests/dev/base/abis/models/bytebeasts-Tournament-12bdecb1.json @@ -0,0 +1,445 @@ +[ + { + "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": "tournamentImpl", + "interface_name": "bytebeasts::models::tournament::Itournament" + }, + { + "type": "enum", + "name": "bytebeasts::models::tournament::TournamentStatus", + "variants": [ + { + "name": "Pending", + "type": "()" + }, + { + "name": "Ongoing", + "type": "()" + }, + { + "name": "Completed", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "bytebeasts::models::tournament::Tournament", + "members": [ + { + "name": "tournament_id", + "type": "core::integer::u32" + }, + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "status", + "type": "bytebeasts::models::tournament::TournamentStatus" + }, + { + "name": "entry_fee", + "type": "core::integer::u32" + }, + { + "name": "max_participants", + "type": "core::integer::u32" + }, + { + "name": "current_participants", + "type": "core::array::Array::" + }, + { + "name": "prize_pool", + "type": "core::integer::u32" + } + ] + }, + { + "type": "interface", + "name": "bytebeasts::models::tournament::Itournament", + "items": [ + { + "type": "function", + "name": "ensure_abi", + "inputs": [ + { + "name": "model", + "type": "bytebeasts::models::tournament::Tournament" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "bytebeasts::models::tournament::tournament::Event", + "kind": "enum", + "variants": [] + } +] \ No newline at end of file diff --git a/manifests/dev/base/contracts/bytebeasts-tournament_system-1f2bbf20.toml b/manifests/dev/base/contracts/bytebeasts-tournament_system-1f2bbf20.toml new file mode 100644 index 0000000..97f792e --- /dev/null +++ b/manifests/dev/base/contracts/bytebeasts-tournament_system-1f2bbf20.toml @@ -0,0 +1,10 @@ +kind = "DojoContract" +class_hash = "0x20aead2ca71d2f0bfcbfa28a41b4b7687aa1af2579394c256ca0c4dc73bd2e8" +original_class_hash = "0x20aead2ca71d2f0bfcbfa28a41b4b7687aa1af2579394c256ca0c4dc73bd2e8" +base_class_hash = "0x0" +abi = "manifests/dev/base/abis/contracts/bytebeasts-tournament_system-1f2bbf20.json" +reads = [] +writes = [] +init_calldata = [] +tag = "bytebeasts-tournament_system" +manifest_name = "bytebeasts-tournament_system-1f2bbf20" diff --git a/manifests/dev/base/models/bytebeasts-Tournament-12bdecb1.toml b/manifests/dev/base/models/bytebeasts-Tournament-12bdecb1.toml new file mode 100644 index 0000000..a4d790e --- /dev/null +++ b/manifests/dev/base/models/bytebeasts-Tournament-12bdecb1.toml @@ -0,0 +1,41 @@ +kind = "DojoModel" +class_hash = "0x6d38c864554f8be34be3ab85e56ce73c86ad695b61b8ccec9553cf208631d0b" +original_class_hash = "0x6d38c864554f8be34be3ab85e56ce73c86ad695b61b8ccec9553cf208631d0b" +abi = "manifests/dev/base/abis/models/bytebeasts-Tournament-12bdecb1.json" +tag = "bytebeasts-Tournament" +manifest_name = "bytebeasts-Tournament-12bdecb1" + +[[members]] +name = "tournament_id" +type = "u32" +key = true + +[[members]] +name = "name" +type = "felt252" +key = false + +[[members]] +name = "status" +type = "TournamentStatus" +key = false + +[[members]] +name = "entry_fee" +type = "u32" +key = false + +[[members]] +name = "max_participants" +type = "u32" +key = false + +[[members]] +name = "current_participants" +type = "Array" +key = false + +[[members]] +name = "prize_pool" +type = "u32" +key = false diff --git a/src/lib.cairo b/src/lib.cairo index c574db2..44d6b37 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -33,4 +33,5 @@ mod models { mod tests { mod test_battle; mod test_bag; + mod test_tournament; } diff --git a/src/models/tournament.cairo b/src/models/tournament.cairo index c31bfd9..47b6118 100644 --- a/src/models/tournament.cairo +++ b/src/models/tournament.cairo @@ -1,5 +1,3 @@ -use super::player::Player; - #[derive(Serde, Copy, Drop, Introspect, PartialEq, Debug)] pub enum TournamentStatus { Pending, @@ -17,7 +15,7 @@ pub struct Tournament { pub status: TournamentStatus, pub entry_fee: u32, pub max_participants: u32, - pub current_participants: Array, + pub current_participants: Array, pub prize_pool: u32, } @@ -25,32 +23,19 @@ pub struct Tournament { #[cfg(test)] mod tests { use bytebeasts::{ - models::{tournament::Tournament, tournament::TournamentStatus, player::Player} + models::{tournament::Tournament, tournament::TournamentStatus} }; #[test] fn test_tournament_initialization() { - let mut players = ArrayTrait::new(); - - let player_ash = Player { - player_id: 1, - player_name: 'Ash', - beast_1: 1, // Beast 1 assigned - beast_2: 0, // No beast assigned - beast_3: 0, // No beast assigned - beast_4: 0, // No beast assigned - potions: 1 - }; - players.append(player_ash); - let tournament = Tournament { tournament_id: 1, name: 'gersonwashere', status: TournamentStatus::Pending, entry_fee: 1, max_participants: 2, - current_participants: players, + current_participants: array![1], prize_pool: 1, }; diff --git a/src/systems/tournament.cairo b/src/systems/tournament.cairo index 6c94b8a..33411d6 100644 --- a/src/systems/tournament.cairo +++ b/src/systems/tournament.cairo @@ -1,5 +1,5 @@ use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; -use bytebeasts::{models::{player::Player, tournament::Tournament, tournament::TournamentStatus},}; +use bytebeasts::{models::{tournament::Tournament, tournament::TournamentStatus},}; #[dojo::interface] trait ITournamentAction { @@ -10,12 +10,12 @@ trait ITournamentAction { status: TournamentStatus, entry_fee: u32, max_participants: u32, - current_participants: Array, + current_participants: Array, prize_pool: u32 ); - fn register_player(ref world: IWorldDispatcher, tournament_id: u32, new_player: Player); + fn register_player(ref world: IWorldDispatcher, tournament_id: u32, new_player_id: u32); fn start_tournament(ref world: IWorldDispatcher, tournament_id: u32); - // fn complete_tournament(ref world: IWorldDispatcher, tournament_id: u32, player_id: u32); + fn complete_tournament(ref world: IWorldDispatcher, tournament_id: u32, player_id: u32); fn get_tournament(world: @IWorldDispatcher, tournament_id: u32) -> Tournament; } @@ -24,7 +24,7 @@ trait ITournamentAction { mod tournament_system { use super::ITournamentAction; use bytebeasts::{ - models::{player::Player, tournament::Tournament, tournament::TournamentStatus}, + models::{tournament::Tournament, tournament::TournamentStatus}, }; #[abi(embed_v0)] @@ -36,7 +36,7 @@ mod tournament_system { status: TournamentStatus, entry_fee: u32, max_participants: u32, - current_participants: Array, + current_participants: Array, prize_pool: u32 ) { let tournament = Tournament { @@ -51,7 +51,7 @@ mod tournament_system { set!(world, (tournament)) } - fn register_player(ref world: IWorldDispatcher, tournament_id: u32, new_player: Player) { + fn register_player(ref world: IWorldDispatcher, tournament_id: u32, new_player_id: u32) { let mut tournament = get!(world, tournament_id, (Tournament)); assert!(tournament.status == TournamentStatus::Pending, "Tournament not open for registration"); @@ -61,7 +61,7 @@ mod tournament_system { "Tournament is full" ); - tournament.current_participants.append(new_player); + tournament.current_participants.append(new_player_id); set!(world, (tournament)); } @@ -81,6 +81,28 @@ mod tournament_system { set!(world, (tournament)); } + fn complete_tournament(ref world: IWorldDispatcher, tournament_id: u32, player_id: u32) { + let tournament = get!(world, tournament_id, (Tournament)); + + assert!(tournament.status == TournamentStatus::Ongoing, "Tournament not ongoing"); + + // Validate winner is a participant + let mut is_participant = false; + for participant in tournament.current_participants { + if participant == player_id { + is_participant = true; + break; + } + }; + assert!(is_participant, "Winner not participant"); + + // TODO distribute prize pool to winner + let mut tournament = get!(world, tournament_id, (Tournament)); + tournament.status = TournamentStatus::Completed; + + set!(world, (tournament)); + } + fn get_tournament(world: @IWorldDispatcher, tournament_id: u32) -> Tournament { let tournament_from_world = get!(world, tournament_id, (Tournament)); tournament_from_world diff --git a/src/tests/test_tournament.cairo b/src/tests/test_tournament.cairo new file mode 100644 index 0000000..7e67f7a --- /dev/null +++ b/src/tests/test_tournament.cairo @@ -0,0 +1,313 @@ +#[cfg(test)] +mod tests { + use starknet::ContractAddress; + + use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; + use dojo::utils::test::{spawn_test_world, deploy_contract}; + + use bytebeasts::{ + systems::{tournament::{tournament_system, ITournamentActionDispatcher, ITournamentActionDispatcherTrait}} + }; + + use bytebeasts::{ + models::player::{Player, player}, + models::tournament::{Tournament, tournament, TournamentStatus}, + }; + + const TOURNAMENT_ID: u32 = 1; + const TOURNAMENT_NAME: felt252 = 'TOURNAMENT_NAME'; + const TOURNAMENT_ENTRY_FEE: u32 = 0; + const TOURNAMENT_MAX_PARTICIPANTS: u32 = 2; + const TOURNAMENT_PRIZE_POOL: u32 = 0; + + const PLAYER1_ID: u32 = 1; + const PLAYER1_NAME: felt252 = 'PLAYER1'; + const PLAYER1_BEAST1: u32 = 0; + const PLAYER1_BEAST2: u32 = 0; + const PLAYER1_BEAST3: u32 = 0; + const PLAYER1_BEAST4: u32 = 0; + const PLAYER1_POTIONS: u32 = 0; + + // Helper function to create the first player + fn get_player1() -> Player { + Player { + player_id: PLAYER1_ID, + player_name: PLAYER1_NAME, + beast_1: PLAYER1_BEAST1, + beast_2: PLAYER1_BEAST2, + beast_3: PLAYER1_BEAST3, + beast_4: PLAYER1_BEAST4, + potions: PLAYER1_POTIONS + } + } + + const PLAYER2_ID: u32 = 2; + const PLAYER2_NAME: felt252 = 'PLAYER2'; + const PLAYER2_BEAST1: u32 = 0; + const PLAYER2_BEAST2: u32 = 0; + const PLAYER2_BEAST3: u32 = 0; + const PLAYER2_BEAST4: u32 = 0; + const PLAYER2_POTIONS: u32 = 0; + + // Helper function to create the second player + fn get_player2() -> Player { + Player { + player_id: PLAYER2_ID, + player_name: PLAYER2_NAME, + beast_1: PLAYER2_BEAST1, + beast_2: PLAYER2_BEAST2, + beast_3: PLAYER2_BEAST3, + beast_4: PLAYER2_BEAST4, + potions: PLAYER2_POTIONS + } + } + + const PLAYER3_ID: u32 = 3; + const PLAYER3_NAME: felt252 = 'PLAYER3'; + const PLAYER3_BEAST1: u32 = 0; + const PLAYER3_BEAST2: u32 = 0; + const PLAYER3_BEAST3: u32 = 0; + const PLAYER3_BEAST4: u32 = 0; + const PLAYER3_POTIONS: u32 = 0; + + // Helper function to create the third player + fn get_player3() -> Player { + Player { + player_id: PLAYER3_ID, + player_name: PLAYER3_NAME, + beast_1: PLAYER3_BEAST1, + beast_2: PLAYER3_BEAST2, + beast_3: PLAYER3_BEAST3, + beast_4: PLAYER3_BEAST4, + potions: PLAYER3_POTIONS + } + } + + // Initializes the testing environment by creating the world with the required models and deploying the tournament contract + fn setup_world() -> (IWorldDispatcher, ITournamentActionDispatcher) { + let mut models = array![ + player::TEST_CLASS_HASH, + tournament::TEST_CLASS_HASH + ]; + + // Spawns a test world with the specified name and models + let world = spawn_test_world("bytebeasts", models); + + // Deploys the tournament system contract and retrieves its address + let contract_address = world.deploy_contract('salt', tournament_system::TEST_CLASS_HASH.try_into().unwrap()); + + // Initializes the tournament system dispatcher with the deployed contract address + let tournament_system = ITournamentActionDispatcher { contract_address }; + + // Grants write permissions for the tournament system contract + world.grant_writer(dojo::utils::bytearray_hash(@"bytebeasts"), contract_address); + + // Returns the world instance and tournament system dispatcher + (world, tournament_system) + } + + // Initializes an empty tournament + fn setup_tournament() -> (IWorldDispatcher, ITournamentActionDispatcher) { + let (world, tournament_system) = setup_world(); + + // Creates an empty pending tournament with no participants + tournament_system.create_tournament( + TOURNAMENT_ID, + TOURNAMENT_NAME, + TournamentStatus::Pending, + TOURNAMENT_ENTRY_FEE, + TOURNAMENT_MAX_PARTICIPANTS, + array![], + TOURNAMENT_PRIZE_POOL + ); + + (world, tournament_system) + } + + // Initializes a tournament with 2 players + fn setup_tournament_with_players() -> (IWorldDispatcher, ITournamentActionDispatcher) { + let (world, tournament_system) = setup_world(); + + // Creates the 2 players + let player1 = get_player1(); + set!(world, (player1)); + let player2 = get_player2(); + set!(world, (player2)); + + // Creates a tournament with 2 participants + tournament_system.create_tournament( + TOURNAMENT_ID, + TOURNAMENT_NAME, + TournamentStatus::Pending, + TOURNAMENT_ENTRY_FEE, + TOURNAMENT_MAX_PARTICIPANTS, + array![player1.player_id, player2.player_id], + TOURNAMENT_PRIZE_POOL + ); + + (world, tournament_system) + } + + // This test verifies the creation of an empty tournament + #[test] + fn test_create_tournament() { + let (world, _) = setup_tournament(); + + let tournament = get!(world, TOURNAMENT_ID, (Tournament)); + + assert_eq!(tournament.tournament_id, TOURNAMENT_ID); + assert_eq!(tournament.name, TOURNAMENT_NAME); + assert_eq!(tournament.status, TournamentStatus::Pending); + assert_eq!(tournament.entry_fee, TOURNAMENT_ENTRY_FEE); + assert_eq!(tournament.max_participants, TOURNAMENT_MAX_PARTICIPANTS); + assert_eq!(tournament.current_participants, array![]); + assert_eq!(tournament.prize_pool, TOURNAMENT_PRIZE_POOL); + } + + // This test verifies the creation of a tournament with 2 players + #[test] + fn test_create_tournament_with_players() { + let (world, _) = setup_tournament_with_players(); + + let tournament = get!(world, TOURNAMENT_ID, (Tournament)); + + assert_eq!(tournament.tournament_id, TOURNAMENT_ID); + assert_eq!(tournament.name, TOURNAMENT_NAME); + assert_eq!(tournament.status, TournamentStatus::Pending); + assert_eq!(tournament.entry_fee, TOURNAMENT_ENTRY_FEE); + assert_eq!(tournament.max_participants, TOURNAMENT_MAX_PARTICIPANTS); + assert_eq!(tournament.current_participants, array![PLAYER1_ID, PLAYER2_ID]); + assert_eq!(tournament.prize_pool, TOURNAMENT_PRIZE_POOL); + } + + // This test verifies a player can't be registered if the tournament status is not pending + #[test] + #[should_panic(expected: ("Tournament not open for registration", 'ENTRYPOINT_FAILED'))] + fn test_register_player_not_pending() { + let (world, tournament_system) = setup_tournament(); + + // Mutates the status to ongoing to make player registration fail + let mut tournament = get!(world, TOURNAMENT_ID, (Tournament)); + tournament.status = TournamentStatus::Ongoing; + set!(world, (tournament)); + + let player1 = get_player1(); + set!(world, (player1)); + + tournament_system.register_player(TOURNAMENT_ID, player1.player_id); + } + + // This test verifies it's impossible for a player to join an already full tournament + #[test] + #[should_panic(expected: ("Tournament is full", 'ENTRYPOINT_FAILED'))] + fn test_register_player_tournament_full() { + let (world, tournament_system) = setup_tournament_with_players(); + + let player3 = get_player3(); + set!(world, (player3)); + + tournament_system.register_player(TOURNAMENT_ID, player3.player_id); + } + + // This test verifies the tournament can register players + #[test] + fn test_register_players() { + let (world, tournament_system) = setup_tournament(); + + let player1 = get_player1(); + set!(world, (player1)); + tournament_system.register_player(TOURNAMENT_ID, player1.player_id); + let tournament = get!(world, TOURNAMENT_ID, (Tournament)); + assert_eq!(tournament.current_participants, array![player1.player_id]); + + let player2 = get_player2(); + set!(world, (player2)); + tournament_system.register_player(TOURNAMENT_ID, player2.player_id); + let tournament = get!(world, TOURNAMENT_ID, (Tournament)); + assert_eq!(tournament.current_participants, array![player1.player_id, player2.player_id]); + } + + // This test verifies the tournament cannot be started if not in the pending status + #[test] + #[should_panic(expected: ("Tournament not pending", 'ENTRYPOINT_FAILED'))] + fn test_start_tournament_not_pending() { + let (world, tournament_system) = setup_tournament(); + + // Mutates the status to ongoing so that the tournament will fail to start + let mut tournament = get!(world, TOURNAMENT_ID, (Tournament)); + tournament.status = TournamentStatus::Ongoing; + set!(world, (tournament)); + + tournament_system.start_tournament(TOURNAMENT_ID); + } + + // This test verifies the tournament cannot be started until we reach at least 2 participants + #[test] + #[should_panic(expected: ("Not enough participants to start", 'ENTRYPOINT_FAILED'))] + fn test_start_tournament_not_enough_participants() { + let (_, tournament_system) = setup_tournament(); + + tournament_system.start_tournament(TOURNAMENT_ID); + } + + // This test verifies the tournament can be started correctly + #[test] + fn test_start_tournament() { + let (world, tournament_system) = setup_tournament_with_players(); + + tournament_system.start_tournament(TOURNAMENT_ID); + + let tournament = get!(world, TOURNAMENT_ID, (Tournament)); + assert_eq!(tournament.status, TournamentStatus::Ongoing); + } + + // This test verifies the tournament can't be completed if not ongoing + #[test] + #[should_panic(expected: ("Tournament not ongoing", 'ENTRYPOINT_FAILED'))] + fn test_complete_tournament_not_ongoing() { + let (_, tournament_system) = setup_tournament(); + + tournament_system.complete_tournament(TOURNAMENT_ID, PLAYER1_ID); + } + + // This test verifies that the declared winner of a tournament must be a participant + #[test] + #[should_panic(expected: ("Winner not participant", 'ENTRYPOINT_FAILED'))] + fn test_complete_tournament_not_participant() { + let (_, tournament_system) = setup_tournament_with_players(); + + tournament_system.start_tournament(TOURNAMENT_ID); + + tournament_system.complete_tournament(TOURNAMENT_ID, PLAYER3_ID); + } + + // This test verifies the tournament can be completed correctly + #[test] + fn test_complete_tournament() { + let (world, tournament_system) = setup_tournament_with_players(); + + tournament_system.start_tournament(TOURNAMENT_ID); + + tournament_system.complete_tournament(TOURNAMENT_ID, PLAYER1_ID); + + let tournament = get!(world, TOURNAMENT_ID, (Tournament)); + assert_eq!(tournament.status, TournamentStatus::Completed); + } + + // This test verifies the custom getter returns the same data stored in the world + #[test] + fn test_get_tournament() { + let (world, tournament_system) = setup_tournament(); + + let tournament_from_system = tournament_system.get_tournament(TOURNAMENT_ID); + let tournament_from_world = get!(world, TOURNAMENT_ID, (Tournament)); + + assert_eq!(tournament_from_system.tournament_id, tournament_from_world.tournament_id); + assert_eq!(tournament_from_system.name, tournament_from_world.name); + assert_eq!(tournament_from_system.status, tournament_from_world.status); + assert_eq!(tournament_from_system.entry_fee, tournament_from_world.entry_fee); + assert_eq!(tournament_from_system.max_participants, tournament_from_world.max_participants); + assert_eq!(tournament_from_system.current_participants, tournament_from_world.current_participants); + assert_eq!(tournament_from_system.prize_pool, tournament_from_world.prize_pool); + } +}