Spaces:
Sleeping
Sleeping
| //! Tests for core rule enforcement (Referee Logic). | |
| //! These tests verify that the engine correctly prevents actions when costs or conditions are not met. | |
| use crate::core::logic::card_db::LOGIC_ID_MASK; | |
| use crate::core::logic::*; | |
| use crate::test_helpers::{create_test_db, create_test_state}; | |
| fn test_enforce_cost_failure() { | |
| let mut db = create_test_db(); | |
| let mut state = create_test_state(); | |
| // Create member with cost 2 energy | |
| let cid = 3991; | |
| let mut m = MemberCard::default(); | |
| m.card_id = cid; | |
| m.abilities.push(Ability { | |
| trigger: TriggerType::Activated, | |
| costs: vec![Cost { | |
| cost_type: AbilityCostType::Energy, | |
| value: 2, | |
| ..Default::default() | |
| }], | |
| bytecode: vec![O_DRAW, 1, 0, 0, O_RETURN, 0, 0, 0], | |
| ..Default::default() | |
| }); | |
| db.members.insert(cid, m.clone()); | |
| db.members_vec[(cid as usize) & LOGIC_ID_MASK as usize] = Some(m); | |
| state.players[0].stage[0] = cid; | |
| state.players[0].energy_zone.clear(); // 0 Energy | |
| state.debug.debug_ignore_conditions = false; // ENSURE ENFORCEMENT IS ON | |
| // Attempt to activate (0, 0) | |
| let res = state.activate_ability(&db, 0, 0); | |
| assert!(res.is_err(), "Should fail due to insufficient energy"); | |
| assert!(res.unwrap_err().contains("Cannot afford cost")); | |
| } | |
| fn test_enforce_condition_failure() { | |
| let mut db = create_test_db(); | |
| let mut state = create_test_state(); | |
| // Create member with condition Stage >= 3 | |
| let cid = 3992; | |
| let mut m = MemberCard::default(); | |
| m.card_id = cid; | |
| m.abilities.push(Ability { | |
| trigger: TriggerType::Activated, | |
| conditions: vec![Condition { | |
| condition_type: ConditionType::CountStage, | |
| value: 3, | |
| ..Default::default() | |
| }], | |
| bytecode: vec![O_DRAW, 1, 0, 0, O_RETURN, 0, 0, 0], | |
| ..Default::default() | |
| }); | |
| db.members.insert(cid, m.clone()); | |
| db.members_vec[(cid as usize) & LOGIC_ID_MASK as usize] = Some(m); | |
| state.players[0].stage = [cid, -1, -1]; // Only 1 member | |
| state.debug.debug_ignore_conditions = false; | |
| // Attempt to activate (0, 0) | |
| let res = state.activate_ability(&db, 0, 0); | |
| assert!( | |
| res.is_err(), | |
| "Should fail due to conditions not met (Stage count < 3)" | |
| ); | |
| assert!(res.unwrap_err().contains("Conditions not met")); | |
| } | |
| fn test_enforce_once_per_turn_failure() { | |
| let mut db = create_test_db(); | |
| let mut state = create_test_state(); | |
| // Create member with Once Per Turn | |
| let cid = 3993; | |
| let mut m = MemberCard::default(); | |
| m.card_id = cid; | |
| m.abilities.push(Ability { | |
| trigger: TriggerType::Activated, | |
| is_once_per_turn: true, | |
| bytecode: vec![O_DRAW, 1, 0, 0, O_RETURN, 0, 0, 0], | |
| ..Default::default() | |
| }); | |
| db.members.insert(cid, m.clone()); | |
| db.members_vec[(cid as usize) & LOGIC_ID_MASK as usize] = Some(m); | |
| state.players[0].stage[0] = cid; | |
| state.players[0].deck = vec![1, 2, 3].into(); | |
| state.debug.debug_ignore_conditions = false; | |
| // First activation - Success | |
| let res1 = state.activate_ability(&db, 0, 0); | |
| assert!( | |
| res1.is_ok(), | |
| "First activation should succeed. Error: {:?}", | |
| res1.err() | |
| ); | |
| // Second activation - Failure | |
| let res2 = state.activate_ability(&db, 0, 0); | |
| assert!( | |
| res2.is_err(), | |
| "Second activation should fail due to Once-Per-Turn" | |
| ); | |
| assert!(res2.unwrap_err().contains("Ability already used this turn")); | |
| } | |
| fn test_enforce_play_member_cost_failure() { | |
| let mut db = create_test_db(); | |
| let mut state = create_test_state(); | |
| // Member with cost 2 | |
| let cid = 3994; | |
| let mut m = MemberCard::default(); | |
| m.card_id = cid; | |
| m.cost = 2; | |
| db.members.insert(cid, m.clone()); | |
| db.members_vec[(cid as usize) & LOGIC_ID_MASK as usize] = Some(m); | |
| state.players[0].hand = vec![cid].into(); | |
| state.players[0].energy_zone.clear(); // 0 Energy | |
| state.debug.debug_ignore_conditions = false; | |
| // Attempt to play to slot 0 | |
| let res = state.play_member(&db, 0, 0); | |
| assert!( | |
| res.is_err(), | |
| "Should fail to play member due to insufficient energy" | |
| ); | |
| assert!(res.unwrap_err().contains("Not enough energy")); | |
| } | |