diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b12bcc..f6c8591 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,57 +2,53 @@ name: Cairo-Dojo CI on: push: - branches: [ main, master ] + branches: [main] pull_request: - branches: [ main, master ] + branches: [main] + +env: + DOJO_VERSION: 1.6.1 + SCARB_VERSION: 2.10.1 + WORKING_DIRECTORY: ./backend/dojo_examples/combat_game jobs: build-and-test: name: Build and Test runs-on: ubuntu-latest - + steps: - name: Checkout code - uses: actions/checkout@v3 - - - name: Install asdf - uses: asdf-vm/actions/setup@v2 - - - name: Verify asdf installation - run: asdf --version + uses: actions/checkout@v4 - - name: Install Dojo plugin + - name: Download Dojo release artifact run: | - asdf plugin add dojo https://github.com/dojoengine/asdf-dojo - asdf install dojo 1.2.1 - asdf global dojo 1.2.1 - asdf list dojo - which sozo || echo "sozo not found" - ls -la /home/runner/.config/.dojo/bin/ || echo "Directory not found" + curl -L -o dojo-linux-x86_64.tar.gz https://github.com/dojoengine/dojo/releases/download/v${{ env.DOJO_VERSION }}/dojo_v${{ env.DOJO_VERSION }}_linux_amd64.tar.gz + tar -xzf dojo-linux-x86_64.tar.gz + sudo mv sozo /usr/local/bin/ - - name: Update PATH - run: echo "$HOME/.asdf/shims" >> $GITHUB_PATH - - - name: Install Scarb plugin - run: | - asdf plugin add scarb - asdf install scarb 2.9.2 - asdf global scarb 2.9.2 + - name: Setup Scarb + uses: software-mansion/setup-scarb@v1 + with: + scarb-version: ${{ env.SCARB_VERSION }} - name: Build contracts - working-directory: backend/dojo_examples/combat_game + working-directory: ${{ env.WORKING_DIRECTORY }} run: | sozo build - if [[ `git status --porcelain` ]]; then - echo "The git repo is dirty" - echo "Make sure to run 'sozo build' after changing Scarb.toml" - exit 1 - fi - name: Run tests - working-directory: backend/dojo_examples/combat_game + working-directory: ${{ env.WORKING_DIRECTORY }} run: sozo test - - name: Check formatting - working-directory: backend/dojo_examples/combat_game - run: scarb fmt + check: + runs-on: ubuntu-latest + name: Check format + steps: + - uses: actions/checkout@v4 + - uses: software-mansion/setup-scarb@v1 + with: + scarb-version: ${{ env.SCARB_VERSION }} + - name: Format + working-directory: ${{ env.WORKING_DIRECTORY }} + run: scarb fmt --check + shell: bash diff --git a/backend/dojo_examples/combat_game/.tool-versions b/backend/dojo_examples/combat_game/.tool-versions index faadd70..c65b0fa 100644 --- a/backend/dojo_examples/combat_game/.tool-versions +++ b/backend/dojo_examples/combat_game/.tool-versions @@ -1,2 +1,2 @@ -dojo v1.6.0-alpha.1 +dojo 1.6.1 scarb 2.10.1 diff --git a/backend/dojo_examples/combat_game/Scarb.lock b/backend/dojo_examples/combat_game/Scarb.lock index ddb276c..4d0982c 100644 --- a/backend/dojo_examples/combat_game/Scarb.lock +++ b/backend/dojo_examples/combat_game/Scarb.lock @@ -4,7 +4,7 @@ version = 1 [[package]] name = "achievement" version = "0.0.0" -source = "git+https://github.com/cartridge-gg/arcade?tag=v1.6.0-alpha.1#0378ed6d9d8188fa6532e66c8db8729a27eb8a5f" +source = "git+https://github.com/cartridge-gg/arcade?tag=v1.6.1#6438a74b589c3cac19b9782709e3845215ec67d6" dependencies = [ "dojo", ] @@ -20,16 +20,16 @@ dependencies = [ [[package]] name = "dojo" -version = "1.6.0-alpha.0" -source = "git+https://github.com/dojoengine/dojo?tag=v1.6.0-alpha.1#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +version = "1.6.1" +source = "git+https://github.com/dojoengine/dojo?tag=v1.6.1#a4f9445cf925e999d97038f596806ec307981a9b" dependencies = [ "dojo_plugin", ] [[package]] name = "dojo_cairo_test" -version = "1.6.0-alpha.0" -source = "git+https://github.com/dojoengine/dojo?tag=v1.6.0-alpha.1#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +version = "1.6.1" +source = "git+https://github.com/dojoengine/dojo?tag=v1.6.1#a4f9445cf925e999d97038f596806ec307981a9b" dependencies = [ "dojo", ] @@ -37,4 +37,4 @@ dependencies = [ [[package]] name = "dojo_plugin" version = "2.10.1" -source = "git+https://github.com/dojoengine/dojo?tag=v1.6.0-alpha.1#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +source = "git+https://github.com/dojoengine/dojo?tag=v1.6.1#a4f9445cf925e999d97038f596806ec307981a9b" diff --git a/backend/dojo_examples/combat_game/Scarb.toml b/backend/dojo_examples/combat_game/Scarb.toml index df2095d..0998f11 100644 --- a/backend/dojo_examples/combat_game/Scarb.toml +++ b/backend/dojo_examples/combat_game/Scarb.toml @@ -2,7 +2,7 @@ cairo-version = "2.10.1" name = "combat_game" version = "0.1.0" -edition = "2023_11" +edition = "2024_07" [cairo] sierra-replace-ids = true @@ -11,18 +11,18 @@ sierra-replace-ids = true sepolia = "sozo --profile sepolia clean && sozo --profile sepolia build && sozo --profile sepolia migrate --account-address $DEPLOYER_ACCOUNT_ADDRESS --private-key $DEPLOYER_PRIVATE_KEY --fee strk" [dependencies] -dojo = { git = "https://github.com/dojoengine/dojo", tag = "v1.6.0-alpha.1" } -achievement = { git = "https://github.com/cartridge-gg/arcade", tag = "v1.6.0-alpha.1" } +dojo = { git = "https://github.com/dojoengine/dojo", tag = "v1.6.1" } +achievement = { git = "https://github.com/cartridge-gg/arcade", tag = "v1.6.1" } [[target.starknet-contract]] build-external-contracts = [ "dojo::world::world_contract::world", - "achievement::events::index::e_TrophyCreation", - "achievement::events::index::e_TrophyProgression", + "achievement::events::index::e_TrophyCreation", + "achievement::events::index::e_TrophyProgression", ] [dev-dependencies] cairo_test = "2.10.1" -dojo_cairo_test = { git = "https://github.com/dojoengine/dojo", tag = "v1.6.0-alpha.1" } +dojo_cairo_test = { git = "https://github.com/dojoengine/dojo", tag = "v1.6.1" } -[profile.sepolia] \ No newline at end of file +[profile.sepolia] diff --git a/backend/dojo_examples/combat_game/src/systems/battle.cairo b/backend/dojo_examples/combat_game/src/systems/battle.cairo index 2c1178e..8a3ce2a 100644 --- a/backend/dojo_examples/combat_game/src/systems/battle.cairo +++ b/backend/dojo_examples/combat_game/src/systems/battle.cairo @@ -15,6 +15,7 @@ pub mod battle_system { use combat_game::achievements::achievement::{Achievement, AchievementTrait}; use starknet::{get_caller_address, get_block_timestamp, ContractAddress}; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use core::num::traits::zero::Zero; use achievement::components::achievable::AchievableComponent; @@ -316,4 +317,3 @@ pub mod battle_system { } } } - diff --git a/backend/dojo_examples/combat_game/src/systems/beast.cairo b/backend/dojo_examples/combat_game/src/systems/beast.cairo index cff9cf7..dffc9d0 100644 --- a/backend/dojo_examples/combat_game/src/systems/beast.cairo +++ b/backend/dojo_examples/combat_game/src/systems/beast.cairo @@ -12,6 +12,7 @@ pub trait IBeast { pub mod beast_system { use super::{IBeast, BeastType, BeastStats}; use starknet::get_block_timestamp; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; use combat_game::store::{StoreTrait}; use combat_game::models::beast_stats::BeastStatsActionTrait; diff --git a/backend/dojo_examples/combat_game/src/systems/player.cairo b/backend/dojo_examples/combat_game/src/systems/player.cairo index f8b9010..fdb8dcb 100644 --- a/backend/dojo_examples/combat_game/src/systems/player.cairo +++ b/backend/dojo_examples/combat_game/src/systems/player.cairo @@ -9,6 +9,7 @@ pub mod player_system { use super::IPlayer; use combat_game::models::player::{Player, PlayerAssert}; + use starknet::storage::{StoragePointerWriteAccess}; use combat_game::store::{StoreTrait}; use starknet::{get_caller_address, get_block_timestamp}; diff --git a/backend/dojo_examples/combat_game/src/tests/test_beast.cairo b/backend/dojo_examples/combat_game/src/tests/test_beast.cairo index 62a8a6c..8105074 100644 --- a/backend/dojo_examples/combat_game/src/tests/test_beast.cairo +++ b/backend/dojo_examples/combat_game/src/tests/test_beast.cairo @@ -1,15 +1,8 @@ #[cfg(test)] mod beast_integration_tests { use combat_game::{ - models::{ - beast::{BeastTrait}, - beast_stats::{BeastStatsActionTrait}, - }, - types::{ - beast_type::BeastType, - skill::SkillType, - status_condition::StatusCondition, - }, + models::{beast::{BeastTrait}, beast_stats::{BeastStatsActionTrait}}, + types::{beast_type::BeastType, skill::SkillType, status_condition::StatusCondition}, }; use starknet::{contract_address_const, ContractAddress}; @@ -46,15 +39,23 @@ mod beast_integration_tests { #[available_gas(250000)] fn test_beast_type_effectiveness_integration() { // Test type effectiveness calculations (pure calculations) - let light_vs_shadow = BeastTrait::calculate_effectiveness(BeastType::Light, BeastType::Shadow); - let light_vs_magic = BeastTrait::calculate_effectiveness(BeastType::Light, BeastType::Magic); - let light_vs_light = BeastTrait::calculate_effectiveness(BeastType::Light, BeastType::Light); + let light_vs_shadow = BeastTrait::calculate_effectiveness( + BeastType::Light, BeastType::Shadow, + ); + let light_vs_magic = BeastTrait::calculate_effectiveness( + BeastType::Light, BeastType::Magic, + ); + let light_vs_light = BeastTrait::calculate_effectiveness( + BeastType::Light, BeastType::Light, + ); assert_eq!(light_vs_shadow, 150, "Light should be super effective against Shadow"); assert_eq!(light_vs_magic, 50, "Light should be not very effective against Magic"); assert_eq!(light_vs_light, 100, "Light should be normally effective against Light"); - let magic_vs_light = BeastTrait::calculate_effectiveness(BeastType::Magic, BeastType::Light); + let magic_vs_light = BeastTrait::calculate_effectiveness( + BeastType::Magic, BeastType::Light, + ); assert_eq!(magic_vs_light, 150, "Magic should be super effective against Light"); } @@ -65,12 +66,26 @@ mod beast_integration_tests { let magic_beast = BeastTrait::new(PLAYER1(), 2, BeastType::Magic); // Test skill type favoring - assert!(light_beast.is_favored_attack(SkillType::Beam), "Light beast should favor Beam attacks"); - assert!(light_beast.is_favored_attack(SkillType::Slash), "Light beast should favor Slash attacks"); - assert!(!light_beast.is_favored_attack(SkillType::Blast), "Light beast should NOT favor Blast attacks"); + assert!( + light_beast.is_favored_attack(SkillType::Beam), "Light beast should favor Beam attacks", + ); + assert!( + light_beast.is_favored_attack(SkillType::Slash), + "Light beast should favor Slash attacks", + ); + assert!( + !light_beast.is_favored_attack(SkillType::Blast), + "Light beast should NOT favor Blast attacks", + ); - assert!(magic_beast.is_favored_attack(SkillType::Blast), "Magic beast should favor Blast attacks"); - assert!(!magic_beast.is_favored_attack(SkillType::Beam), "Magic beast should NOT favor Beam attacks"); + assert!( + magic_beast.is_favored_attack(SkillType::Blast), + "Magic beast should favor Blast attacks", + ); + assert!( + !magic_beast.is_favored_attack(SkillType::Beam), + "Magic beast should NOT favor Beam attacks", + ); } #[test] @@ -79,22 +94,16 @@ mod beast_integration_tests { let light_beast = BeastTrait::new(PLAYER1(), 1, BeastType::Light); // Test attack calculation with type effectiveness - let (damage, is_favored, is_effective) = light_beast.attack( - BeastType::Shadow, - SkillType::Beam, - 1 - ); + let (damage, is_favored, is_effective) = light_beast + .attack(BeastType::Shadow, SkillType::Beam, 1); assert!(damage > 0, "Attack should deal damage"); assert!(is_favored, "Beam should be favored by Light beast"); assert!(is_effective, "Light should be effective against Shadow"); // Test non-effective attack - let (damage2, is_favored2, is_effective2) = light_beast.attack( - BeastType::Magic, - SkillType::Beam, - 1 - ); + let (damage2, is_favored2, is_effective2) = light_beast + .attack(BeastType::Magic, SkillType::Beam, 1); assert!(damage2 > 0, "Attack should still deal damage"); assert!(is_favored2, "Beam should still be favored by Light beast"); @@ -115,7 +124,9 @@ mod beast_integration_tests { // Validate initial state assert_eq!(beast_stats.current_hp, beast_stats.max_hp, "Current HP should equal max HP"); - assert_eq!(beast_stats.status_condition, StatusCondition::None, "Should start with no status"); + assert_eq!( + beast_stats.status_condition, StatusCondition::None, "Should start with no status", + ); } #[test] @@ -141,13 +152,20 @@ mod beast_integration_tests { let mut beast_stats = BeastStatsActionTrait::new_beast_stats(1, BeastType::Light, 1, 1000); // Test initial state - assert_eq!(beast_stats.status_condition, StatusCondition::None, "Should start with no status"); + assert_eq!( + beast_stats.status_condition, StatusCondition::None, "Should start with no status", + ); assert!(beast_stats.can_attack(), "Should be able to attack initially"); // Test applying status condition beast_stats.apply_status(StatusCondition::Poisoned); - assert_eq!(beast_stats.status_condition, StatusCondition::Poisoned, "Should have poisoned status"); - assert!(beast_stats.can_attack(), "Should still be able to attack when poisoned (only Stunned prevents attacking)"); + assert_eq!( + beast_stats.status_condition, StatusCondition::Poisoned, "Should have poisoned status", + ); + assert!( + beast_stats.can_attack(), + "Should still be able to attack when poisoned (only Stunned prevents attacking)", + ); // Test clearing status condition beast_stats.clear_status(); @@ -175,7 +193,7 @@ mod beast_integration_tests { // Each type should have different total stats due to modifiers let light_total = light_stats.max_hp + light_stats.attack; let magic_total = magic_stats.max_hp + magic_stats.attack; - + // At least one comparison should be different due to type modifiers assert!(light_total != magic_total, "Beast types should have different stat totals"); } diff --git a/backend/dojo_examples/combat_game/src/tests/test_player.cairo b/backend/dojo_examples/combat_game/src/tests/test_player.cairo index 18028d8..ff501a6 100644 --- a/backend/dojo_examples/combat_game/src/tests/test_player.cairo +++ b/backend/dojo_examples/combat_game/src/tests/test_player.cairo @@ -12,17 +12,13 @@ mod player_integration_tests { battle::{battle_system, IBattleDispatcher, IBattleDispatcherTrait}, }; use combat_game::models::{ - player::{Player, m_Player, PlayerAssert}, - beast::{Beast, m_Beast}, - beast_stats::{BeastStats, m_BeastStats, BeastStatsActionTrait}, - beast_skill::{BeastSkill, m_BeastSkill}, - skill::{Skill, m_Skill}, + player::{Player, m_Player, PlayerAssert}, beast::{Beast, m_Beast}, + beast_stats::{BeastStats, m_BeastStats, BeastStatsActionTrait}, + beast_skill::{BeastSkill, m_BeastSkill}, skill::{Skill, m_Skill}, battle::{Battle, m_Battle, BattleTrait}, }; use combat_game::types::{ - beast_type::BeastType, - battle_status::BattleStatus, - status_condition::StatusCondition, + beast_type::BeastType, battle_status::BattleStatus, status_condition::StatusCondition, }; use combat_game::store::{StoreTrait}; use combat_game::constants::{SECONDS_PER_DAY}; @@ -38,11 +34,11 @@ mod player_integration_tests { fn PLAYER1() -> ContractAddress { contract_address_const::<0x1234>() } - + fn PLAYER2() -> ContractAddress { contract_address_const::<0x5678>() } - + fn PLAYER3() -> ContractAddress { contract_address_const::<0x9abc>() } @@ -64,11 +60,14 @@ mod player_integration_tests { TestResource::Event(battle_system::e_AttackExecuted::TEST_CLASS_HASH), TestResource::Event(battle_system::e_BattleEnded::TEST_CLASS_HASH), TestResource::Event(achievement::events::index::e_TrophyCreation::TEST_CLASS_HASH), - TestResource::Event(achievement::events::index::e_TrophyProgression::TEST_CLASS_HASH), + TestResource::Event( + achievement::events::index::e_TrophyProgression::TEST_CLASS_HASH, + ), TestResource::Contract(player_system::TEST_CLASS_HASH), TestResource::Contract(beast_system::TEST_CLASS_HASH), TestResource::Contract(battle_system::TEST_CLASS_HASH), - ].span(), + ] + .span(), }; ndef } @@ -81,7 +80,8 @@ mod player_integration_tests { .with_writer_of([dojo::utils::bytearray_hash(@"combat_game")].span()), ContractDefTrait::new(@"combat_game", @"battle_system") .with_writer_of([dojo::utils::bytearray_hash(@"combat_game")].span()), - ].span() + ] + .span() } pub fn setup() -> (WorldStorage, IPlayerDispatcher, IBeastDispatcher, IBattleDispatcher) { @@ -106,10 +106,10 @@ mod player_integration_tests { } fn setup_beast_for_player( - mut world: WorldStorage, - beast_system: IBeastDispatcher, - player_address: ContractAddress, - beast_type: BeastType + mut world: WorldStorage, + beast_system: IBeastDispatcher, + player_address: ContractAddress, + beast_type: BeastType, ) -> u16 { testing::set_caller_address(player_address); beast_system.spawn_beast(beast_type) @@ -123,17 +123,17 @@ mod player_integration_tests { #[available_gas(3000000)] fn test_player_creation_and_initialization() { let (mut world, player_system, beast_system, _) = setup(); - + // Set caller to player 1 testing::set_caller_address(PLAYER1()); - + // Spawn player with initial beast player_system.spawn_player(INITIAL_BEAST_ID); - + // Verify player was created with correct values let store = StoreTrait::new(world); let player = store.read_player_from_address(PLAYER1()); - + player.assert_exists(); assert_eq!(player.address, PLAYER1(), "Player address should match"); assert_eq!(player.current_beast_id, INITIAL_BEAST_ID, "Current beast ID should be set"); @@ -141,7 +141,11 @@ mod player_integration_tests { assert_eq!(player.battles_lost, 0, "Initial battles lost should be 0"); assert!(player.creation_day > 0, "Creation day should be set"); assert!(player.last_active_day > 0, "Last active day should be set"); - assert_eq!(player.creation_day, player.last_active_day, "Creation and last active should be same initially"); + assert_eq!( + player.creation_day, + player.last_active_day, + "Creation and last active should be same initially", + ); } #[test] @@ -149,12 +153,12 @@ mod player_integration_tests { #[should_panic(expected: ('Player: Already exist',))] fn test_player_creation_duplicate_should_fail() { let (mut world, player_system, _, _) = setup(); - + testing::set_caller_address(PLAYER1()); - + // Create player first time player_system.spawn_player(INITIAL_BEAST_ID); - + // Try to create same player again - should panic player_system.spawn_player(INITIAL_BEAST_ID); } @@ -163,23 +167,26 @@ mod player_integration_tests { #[available_gas(3000000)] fn test_player_unique_addresses() { let (mut world, player_system, _, _) = setup(); - + // Create player 1 testing::set_caller_address(PLAYER1()); player_system.spawn_player(1); - + // Create player 2 testing::set_caller_address(PLAYER2()); player_system.spawn_player(2); - + let store = StoreTrait::new(world); let player1 = store.read_player_from_address(PLAYER1()); let player2 = store.read_player_from_address(PLAYER2()); - + player1.assert_exists(); player2.assert_exists(); assert!(player1.address != player2.address, "Players should have unique addresses"); - assert!(player1.current_beast_id != player2.current_beast_id, "Players should have different beast IDs"); + assert!( + player1.current_beast_id != player2.current_beast_id, + "Players should have different beast IDs", + ); } // ================================ @@ -190,19 +197,19 @@ mod player_integration_tests { #[available_gas(5000000)] fn test_player_beast_assignment_integration() { let (mut world, player_system, beast_system, _) = setup(); - + testing::set_caller_address(PLAYER1()); - + // Create beast first let beast_id = beast_system.spawn_beast(BeastType::Light); - + // Create player with this beast player_system.spawn_player(beast_id); - + let store = StoreTrait::new(world); let player = store.read_player_from_address(PLAYER1()); let beast = store.read_beast(beast_id); - + // Verify integration assert_eq!(player.current_beast_id, beast_id, "Player should have correct beast ID"); assert_eq!(beast.player, PLAYER1(), "Beast should belong to player"); @@ -213,22 +220,22 @@ mod player_integration_tests { #[available_gas(5000000)] fn test_player_beast_update_integration() { let (mut world, player_system, beast_system, _) = setup(); - + testing::set_caller_address(PLAYER1()); - + // Create initial setup let initial_beast_id = beast_system.spawn_beast(BeastType::Light); player_system.spawn_player(initial_beast_id); - + // Create a new beast let new_beast_id = beast_system.spawn_beast(BeastType::Shadow); - + // Update player profile with new beast player_system.update_profile(true, new_beast_id); - + let store = StoreTrait::new(world); let player = store.read_player_from_address(PLAYER1()); - + assert_eq!(player.current_beast_id, new_beast_id, "Player should have updated beast ID"); assert_eq!(player.battles_won, 1, "Player should have 1 battle won from update"); } @@ -241,37 +248,41 @@ mod player_integration_tests { #[available_gas(8000000)] fn test_battle_statistics_tracking_integration() { let (mut world, player_system, beast_system, battle_system) = setup(); - + // Setup two players with beasts testing::set_caller_address(PLAYER1()); let beast1_id = beast_system.spawn_beast(BeastType::Light); player_system.spawn_player(beast1_id); - + testing::set_caller_address(PLAYER2()); let beast2_id = beast_system.spawn_beast(BeastType::Shadow); player_system.spawn_player(beast2_id); - + // Setup beast stats for both let mut store = StoreTrait::new(world); - let beast1_stats = BeastStatsActionTrait::new_beast_stats(beast1_id, BeastType::Light, 1, get_block_timestamp()); - let beast2_stats = BeastStatsActionTrait::new_beast_stats(beast2_id, BeastType::Shadow, 1, get_block_timestamp()); + let beast1_stats = BeastStatsActionTrait::new_beast_stats( + beast1_id, BeastType::Light, 1, get_block_timestamp(), + ); + let beast2_stats = BeastStatsActionTrait::new_beast_stats( + beast2_id, BeastType::Shadow, 1, get_block_timestamp(), + ); store.write_beast_stats(beast1_stats); store.write_beast_stats(beast2_stats); - + // Simulate battle wins and losses testing::set_caller_address(PLAYER1()); - player_system.update_profile(true, beast1_id); // Win + player_system.update_profile(true, beast1_id); // Win player_system.update_profile(false, beast1_id); // Loss - player_system.update_profile(true, beast1_id); // Win - + player_system.update_profile(true, beast1_id); // Win + testing::set_caller_address(PLAYER2()); player_system.update_profile(false, beast2_id); // Loss player_system.update_profile(false, beast2_id); // Loss - + // Verify statistics let player1 = store.read_player_from_address(PLAYER1()); let player2 = store.read_player_from_address(PLAYER2()); - + assert_eq!(player1.battles_won, 2, "Player1 should have 2 wins"); assert_eq!(player1.battles_lost, 1, "Player1 should have 1 loss"); assert_eq!(player2.battles_won, 0, "Player2 should have 0 wins"); @@ -282,23 +293,23 @@ mod player_integration_tests { #[available_gas(8000000)] fn test_battle_statistics_persistence() { let (mut world, player_system, beast_system, _) = setup(); - + testing::set_caller_address(PLAYER1()); let beast_id = beast_system.spawn_beast(BeastType::Magic); player_system.spawn_player(beast_id); - + // Initial state verification let mut store = StoreTrait::new(world); let initial_player = store.read_player_from_address(PLAYER1()); assert_eq!(initial_player.battles_won, 0, "Initial wins should be 0"); assert_eq!(initial_player.battles_lost, 0, "Initial losses should be 0"); - + // Simulate multiple battle outcomes - player_system.update_profile(true, beast_id); // Win 1 - player_system.update_profile(true, beast_id); // Win 2 - player_system.update_profile(false, beast_id); // Loss 1 - player_system.update_profile(true, beast_id); // Win 3 - + player_system.update_profile(true, beast_id); // Win 1 + player_system.update_profile(true, beast_id); // Win 2 + player_system.update_profile(false, beast_id); // Loss 1 + player_system.update_profile(true, beast_id); // Win 3 + // Verify persistence across multiple transactions let final_player = store.read_player_from_address(PLAYER1()); assert_eq!(final_player.battles_won, 3, "Should have 3 total wins"); @@ -313,59 +324,85 @@ mod player_integration_tests { #[available_gas(5000000)] fn test_player_activity_tracking() { let (mut world, player_system, beast_system, _) = setup(); - + testing::set_caller_address(PLAYER1()); let beast_id = beast_system.spawn_beast(BeastType::Light); - + // Record creation time let creation_timestamp = get_block_timestamp(); player_system.spawn_player(beast_id); - + let store = StoreTrait::new(world); let player = store.read_player_from_address(PLAYER1()); - + let expected_day = creation_timestamp / SECONDS_PER_DAY; - assert_eq!(player.creation_day, expected_day.try_into().unwrap(), "Creation day should match"); - assert_eq!(player.last_active_day, expected_day.try_into().unwrap(), "Last active should match creation initially"); - + assert_eq!( + player.creation_day, expected_day.try_into().unwrap(), "Creation day should match", + ); + assert_eq!( + player.last_active_day, + expected_day.try_into().unwrap(), + "Last active should match creation initially", + ); + // Simulate time passage and activity - testing::set_block_timestamp(creation_timestamp + SECONDS_PER_DAY + 3600); // Next day + 1 hour + testing::set_block_timestamp( + creation_timestamp + SECONDS_PER_DAY + 3600, + ); // Next day + 1 hour player_system.update_profile(true, beast_id); - + let updated_player = store.read_player_from_address(PLAYER1()); let new_expected_day = (creation_timestamp + SECONDS_PER_DAY + 3600) / SECONDS_PER_DAY; - - assert_eq!(updated_player.creation_day, expected_day.try_into().unwrap(), "Creation day should not change"); - assert_eq!(updated_player.last_active_day, new_expected_day.try_into().unwrap(), "Last active should update"); - assert!(updated_player.last_active_day > updated_player.creation_day, "Last active should be after creation"); + + assert_eq!( + updated_player.creation_day, + expected_day.try_into().unwrap(), + "Creation day should not change", + ); + assert_eq!( + updated_player.last_active_day, + new_expected_day.try_into().unwrap(), + "Last active should update", + ); + assert!( + updated_player.last_active_day > updated_player.creation_day, + "Last active should be after creation", + ); } #[test] #[available_gas(5000000)] fn test_multiple_activity_updates() { let (mut world, player_system, beast_system, _) = setup(); - + testing::set_caller_address(PLAYER1()); let beast_id = beast_system.spawn_beast(BeastType::Shadow); player_system.spawn_player(beast_id); - + let store = StoreTrait::new(world); let initial_player = store.read_player_from_address(PLAYER1()); let initial_last_active = initial_player.last_active_day; - + // Multiple activities on same day should update last_active to same day player_system.update_profile(true, beast_id); player_system.update_profile(false, beast_id); - + let same_day_player = store.read_player_from_address(PLAYER1()); - assert_eq!(same_day_player.last_active_day, initial_last_active, "Same day activities should not change day"); - + assert_eq!( + same_day_player.last_active_day, + initial_last_active, + "Same day activities should not change day", + ); + // Activity on different day should update testing::set_block_timestamp(get_block_timestamp() + SECONDS_PER_DAY * 2); player_system.update_profile(true, beast_id); - + let different_day_player = store.read_player_from_address(PLAYER1()); - assert!(different_day_player.last_active_day > initial_last_active, "Different day activity should update last_active_day"); + assert!( + different_day_player.last_active_day > initial_last_active, + "Different day activity should update last_active_day", + ); } // ================================ @@ -376,42 +413,46 @@ mod player_integration_tests { #[available_gas(10000000)] fn test_player_beast_battle_full_integration() { let (mut world, player_system, beast_system, battle_system) = setup(); - + // Setup Player 1 testing::set_caller_address(PLAYER1()); let beast1_id = beast_system.spawn_beast(BeastType::Light); player_system.spawn_player(beast1_id); - + // Setup Player 2 testing::set_caller_address(PLAYER2()); let beast2_id = beast_system.spawn_beast(BeastType::Shadow); player_system.spawn_player(beast2_id); - + // Setup beast stats let mut store = StoreTrait::new(world); - let beast1_stats = BeastStatsActionTrait::new_beast_stats(beast1_id, BeastType::Light, 1, get_block_timestamp()); - let beast2_stats = BeastStatsActionTrait::new_beast_stats(beast2_id, BeastType::Shadow, 1, get_block_timestamp()); + let beast1_stats = BeastStatsActionTrait::new_beast_stats( + beast1_id, BeastType::Light, 1, get_block_timestamp(), + ); + let beast2_stats = BeastStatsActionTrait::new_beast_stats( + beast2_id, BeastType::Shadow, 1, get_block_timestamp(), + ); store.write_beast_stats(beast1_stats); store.write_beast_stats(beast2_stats); - + // Initialize beast skills store.init_beast_skills(beast1_id); store.init_beast_skills(beast2_id); - + // Create and join battle testing::set_caller_address(PLAYER1()); let battle_id = battle_system.create_battle(PLAYER2(), BATTLE_TYPE); - + testing::set_caller_address(PLAYER2()); battle_system.join_battle(battle_id); - + // Verify cross-system state let battle = store.read_battle(battle_id); let player1 = store.read_player_from_address(PLAYER1()); let player2 = store.read_player_from_address(PLAYER2()); let beast1 = store.read_beast(beast1_id); let beast2 = store.read_beast(beast2_id); - + assert_eq!(battle.player1, PLAYER1(), "Battle player1 should match"); assert_eq!(battle.player2, PLAYER2(), "Battle player2 should match"); assert_eq!(battle.status, BattleStatus::Active, "Battle should be active"); @@ -425,27 +466,27 @@ mod player_integration_tests { #[available_gas(8000000)] fn test_player_beast_switching_integration() { let (mut world, player_system, beast_system, _) = setup(); - + testing::set_caller_address(PLAYER1()); - + // Create multiple beasts let beast1_id = beast_system.spawn_beast(BeastType::Light); let beast2_id = beast_system.spawn_beast(BeastType::Magic); let beast3_id = beast_system.spawn_beast(BeastType::Shadow); - + // Start with beast1 player_system.spawn_player(beast1_id); - + let store = StoreTrait::new(world); let initial_player = store.read_player_from_address(PLAYER1()); assert_eq!(initial_player.current_beast_id, beast1_id, "Should start with beast1"); - + // Switch to beast2 player_system.update_profile(true, beast2_id); let updated_player = store.read_player_from_address(PLAYER1()); assert_eq!(updated_player.current_beast_id, beast2_id, "Should switch to beast2"); assert_eq!(updated_player.battles_won, 1, "Should increment wins"); - + // Switch to beast3 player_system.update_profile(false, beast3_id); let final_player = store.read_player_from_address(PLAYER1()); @@ -462,34 +503,34 @@ mod player_integration_tests { #[available_gas(8000000)] fn test_multiple_players_simultaneous_creation() { let (mut world, player_system, beast_system, _) = setup(); - + // Create multiple players simultaneously testing::set_caller_address(PLAYER1()); let beast1_id = beast_system.spawn_beast(BeastType::Light); player_system.spawn_player(beast1_id); - + testing::set_caller_address(PLAYER2()); let beast2_id = beast_system.spawn_beast(BeastType::Magic); player_system.spawn_player(beast2_id); - + testing::set_caller_address(PLAYER3()); let beast3_id = beast_system.spawn_beast(BeastType::Shadow); player_system.spawn_player(beast3_id); - + // Verify all players exist independently let store = StoreTrait::new(world); let player1 = store.read_player_from_address(PLAYER1()); let player2 = store.read_player_from_address(PLAYER2()); let player3 = store.read_player_from_address(PLAYER3()); - + player1.assert_exists(); player2.assert_exists(); player3.assert_exists(); - + assert_eq!(player1.current_beast_id, beast1_id, "Player1 should have correct beast"); assert_eq!(player2.current_beast_id, beast2_id, "Player2 should have correct beast"); assert_eq!(player3.current_beast_id, beast3_id, "Player3 should have correct beast"); - + // Verify they're all unique assert!(player1.address != player2.address, "Player1 and Player2 should be different"); assert!(player1.address != player3.address, "Player1 and Player3 should be different"); @@ -500,39 +541,39 @@ mod player_integration_tests { #[available_gas(10000000)] fn test_multiple_players_battle_interactions() { let (mut world, player_system, beast_system, _) = setup(); - + // Setup three players testing::set_caller_address(PLAYER1()); let beast1_id = beast_system.spawn_beast(BeastType::Light); player_system.spawn_player(beast1_id); - + testing::set_caller_address(PLAYER2()); let beast2_id = beast_system.spawn_beast(BeastType::Magic); player_system.spawn_player(beast2_id); - + testing::set_caller_address(PLAYER3()); let beast3_id = beast_system.spawn_beast(BeastType::Shadow); player_system.spawn_player(beast3_id); - + // Simulate battle outcomes between different players testing::set_caller_address(PLAYER1()); - player_system.update_profile(true, beast1_id); // Player1 wins vs someone - + player_system.update_profile(true, beast1_id); // Player1 wins vs someone + testing::set_caller_address(PLAYER2()); - player_system.update_profile(false, beast2_id); // Player2 loses vs Player1 - + player_system.update_profile(false, beast2_id); // Player2 loses vs Player1 + testing::set_caller_address(PLAYER1()); - player_system.update_profile(false, beast1_id); // Player1 loses vs Player3 - + player_system.update_profile(false, beast1_id); // Player1 loses vs Player3 + testing::set_caller_address(PLAYER3()); - player_system.update_profile(true, beast3_id); // Player3 wins vs Player1 - + player_system.update_profile(true, beast3_id); // Player3 wins vs Player1 + // Verify statistics let store = StoreTrait::new(world); let player1 = store.read_player_from_address(PLAYER1()); let player2 = store.read_player_from_address(PLAYER2()); let player3 = store.read_player_from_address(PLAYER3()); - + assert_eq!(player1.battles_won, 1, "Player1 should have 1 win"); assert_eq!(player1.battles_lost, 1, "Player1 should have 1 loss"); assert_eq!(player2.battles_won, 0, "Player2 should have 0 wins"); @@ -550,9 +591,9 @@ mod player_integration_tests { #[should_panic(expected: ('Player: Does not exist',))] fn test_update_nonexistent_player() { let (mut world, player_system, _, _) = setup(); - + testing::set_caller_address(PLAYER1()); - + // Try to update profile without creating player first player_system.update_profile(true, 1); } @@ -561,15 +602,15 @@ mod player_integration_tests { #[available_gas(3000000)] fn test_player_with_zero_beast_id() { let (mut world, player_system, _, _) = setup(); - + testing::set_caller_address(PLAYER1()); - + // Create player with zero beast ID (should be allowed) player_system.spawn_player(0); - + let store = StoreTrait::new(world); let player = store.read_player_from_address(PLAYER1()); - + player.assert_exists(); assert_eq!(player.current_beast_id, 0, "Player should have zero beast ID"); } @@ -578,21 +619,21 @@ mod player_integration_tests { #[available_gas(5000000)] fn test_player_battle_statistics_overflow_prevention() { let (mut world, player_system, beast_system, _) = setup(); - + testing::set_caller_address(PLAYER1()); let beast_id = beast_system.spawn_beast(BeastType::Light); player_system.spawn_player(beast_id); - + // Simulate many wins (test large numbers) let mut i: u16 = 0; while i < 100 { player_system.update_profile(true, beast_id); i += 1; }; - + let store = StoreTrait::new(world); let player = store.read_player_from_address(PLAYER1()); - + assert_eq!(player.battles_won, 100, "Should handle 100 wins correctly"); assert_eq!(player.battles_lost, 0, "Should still have 0 losses"); } @@ -601,34 +642,42 @@ mod player_integration_tests { #[available_gas(5000000)] fn test_player_state_consistency_across_operations() { let (mut world, player_system, beast_system, _) = setup(); - + testing::set_caller_address(PLAYER1()); let beast_id = beast_system.spawn_beast(BeastType::Magic); player_system.spawn_player(beast_id); - + let store = StoreTrait::new(world); - + // Perform multiple operations and verify consistency let initial_player = store.read_player_from_address(PLAYER1()); let initial_creation_day = initial_player.creation_day; - + player_system.update_profile(true, beast_id); let after_win = store.read_player_from_address(PLAYER1()); - assert_eq!(after_win.creation_day, initial_creation_day, "Creation day should never change"); + assert_eq!( + after_win.creation_day, initial_creation_day, "Creation day should never change", + ); assert_eq!(after_win.battles_won, 1, "Wins should increment"); - + player_system.update_profile(false, beast_id); let after_loss = store.read_player_from_address(PLAYER1()); - assert_eq!(after_loss.creation_day, initial_creation_day, "Creation day should never change"); + assert_eq!( + after_loss.creation_day, initial_creation_day, "Creation day should never change", + ); assert_eq!(after_loss.battles_won, 1, "Wins should stay same"); assert_eq!(after_loss.battles_lost, 1, "Losses should increment"); - + // Change beast and verify consistency let new_beast_id = beast_system.spawn_beast(BeastType::Shadow); player_system.update_profile(true, new_beast_id); let after_beast_change = store.read_player_from_address(PLAYER1()); - - assert_eq!(after_beast_change.creation_day, initial_creation_day, "Creation day should never change"); + + assert_eq!( + after_beast_change.creation_day, + initial_creation_day, + "Creation day should never change", + ); assert_eq!(after_beast_change.current_beast_id, new_beast_id, "Beast ID should update"); assert_eq!(after_beast_change.battles_won, 2, "Wins should increment"); assert_eq!(after_beast_change.battles_lost, 1, "Losses should stay same"); @@ -638,20 +687,26 @@ mod player_integration_tests { #[available_gas(3000000)] fn test_player_zero_state_validation() { let (mut world, _, _, _) = setup(); - + // Test Zero player state let store = StoreTrait::new(world); let nonexistent_player = store.read_player_from_address(PLAYER1()); - + assert!(nonexistent_player.is_zero(), "Non-existent player should be zero"); - + // Verify zero player structure use combat_game::constants; - assert_eq!(nonexistent_player.address, constants::ZERO_ADDRESS(), "Zero player should have zero address"); + assert_eq!( + nonexistent_player.address, + constants::ZERO_ADDRESS(), + "Zero player should have zero address", + ); assert_eq!(nonexistent_player.current_beast_id, 0, "Zero player should have zero beast ID"); assert_eq!(nonexistent_player.battles_won, 0, "Zero player should have zero wins"); assert_eq!(nonexistent_player.battles_lost, 0, "Zero player should have zero losses"); - assert_eq!(nonexistent_player.last_active_day, 0, "Zero player should have zero last active"); + assert_eq!( + nonexistent_player.last_active_day, 0, "Zero player should have zero last active", + ); assert_eq!(nonexistent_player.creation_day, 1, "Zero player should have creation day 1"); } }