Files
Genarrative/server-rs/crates/module-runtime-story/src/battle_tests.rs

92 lines
2.8 KiB
Rust

use serde_json::json;
use shared_contracts::runtime_story::{
RuntimeStoryActionRequest, RuntimeStoryChoiceAction, RuntimeStoryPatch,
};
use crate::{
battle::resolve_battle_action, build_status_patch, read_bool_field, read_i32_field,
read_optional_string_field,
};
fn build_battle_fixture() -> serde_json::Value {
json!({
"inBattle": true,
"npcInteractionActive": false,
"playerHp": 4,
"playerMaxHp": 40,
"playerMana": 10,
"playerMaxMana": 10,
"playerSkillCooldowns": {},
"runtimeStats": {
"hostileNpcsDefeated": 0,
"itemsUsed": 0,
"questsAccepted": 0,
"scenesTraveled": 0,
"playTimeMs": 0,
"lastPlayTickAt": null
},
"currentNpcBattleMode": "fight",
"currentNpcBattleOutcome": null,
"currentEncounter": {
"kind": "npc",
"id": "npc_bandit_01",
"npcName": "断桥匪首",
"hostile": true,
"hp": 8,
"experienceReward": 24
},
"sceneHostileNpcs": [{
"id": "npc_bandit_01",
"name": "断桥匪首",
"hp": 8,
"maxHp": 80,
"experienceReward": 24
}]
})
}
fn build_request(function_id: &str, option_text: &str) -> RuntimeStoryActionRequest {
RuntimeStoryActionRequest {
session_id: "runtime-main".to_string(),
client_version: Some(0),
action: RuntimeStoryChoiceAction {
action_type: "story_choice".to_string(),
function_id: function_id.to_string(),
target_id: None,
payload: Some(json!({
"optionText": option_text
})),
},
}
}
#[test]
fn battle_resolution_prefers_player_defeat_when_both_sides_fall_in_same_turn() {
let request = build_request("battle_all_in_crush", "全力压制");
let mut game_state = build_battle_fixture();
let resolution = resolve_battle_action(&mut game_state, &request, "battle_all_in_crush")
.expect("battle action should resolve");
assert_eq!(read_i32_field(&game_state, "playerHp"), Some(0));
assert_eq!(
read_optional_string_field(&game_state, "currentNpcBattleOutcome"),
Some("fight_defeat".to_string())
);
assert_eq!(read_bool_field(&game_state, "inBattle"), Some(false));
assert!(resolution.result_text.contains("败北"));
assert!(matches!(
resolution.patches.first(),
Some(RuntimeStoryPatch::BattleResolved { outcome, .. }) if outcome == "defeat"
));
assert_eq!(
resolution.patches.get(1),
Some(&build_status_patch(&game_state))
);
assert_eq!(
resolution.battle.and_then(|battle| battle.outcome),
Some("defeat".to_string())
);
}