fix: polish bark battle creation flow
This commit is contained in:
@@ -30,6 +30,14 @@ pub enum BarkBattleFinishStatus {
|
||||
Rejected,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum BarkBattleAssetSlot {
|
||||
PlayerCharacter,
|
||||
OpponentCharacter,
|
||||
UiBackground,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarkBattleReplacementConfig {
|
||||
@@ -39,8 +47,6 @@ pub struct BarkBattleReplacementConfig {
|
||||
pub opponent_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ui_background_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bark_sound_src: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
@@ -49,20 +55,19 @@ pub struct BarkBattleConfigEditorPayload {
|
||||
pub title: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
pub theme_preset: String,
|
||||
pub player_dog_skin_preset: String,
|
||||
pub opponent_dog_skin_preset: String,
|
||||
pub theme_description: String,
|
||||
pub player_image_description: String,
|
||||
pub opponent_image_description: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub onomatopoeia: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub player_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub opponent_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ui_background_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bark_sound_src: Option<String>,
|
||||
#[serde(default)]
|
||||
pub difficulty_preset: BarkBattleDifficultyPreset,
|
||||
pub leaderboard_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
@@ -71,20 +76,19 @@ pub struct BarkBattleDraftCreateRequest {
|
||||
pub title: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
pub theme_preset: String,
|
||||
pub player_dog_skin_preset: String,
|
||||
pub opponent_dog_skin_preset: String,
|
||||
pub theme_description: String,
|
||||
pub player_image_description: String,
|
||||
pub opponent_image_description: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub onomatopoeia: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub player_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub opponent_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ui_background_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bark_sound_src: Option<String>,
|
||||
#[serde(default)]
|
||||
pub difficulty_preset: BarkBattleDifficultyPreset,
|
||||
pub leaderboard_enabled: bool,
|
||||
}
|
||||
|
||||
impl From<BarkBattleDraftCreateRequest> for BarkBattleConfigEditorPayload {
|
||||
@@ -92,15 +96,59 @@ impl From<BarkBattleDraftCreateRequest> for BarkBattleConfigEditorPayload {
|
||||
Self {
|
||||
title: value.title,
|
||||
description: value.description,
|
||||
theme_preset: value.theme_preset,
|
||||
player_dog_skin_preset: value.player_dog_skin_preset,
|
||||
opponent_dog_skin_preset: value.opponent_dog_skin_preset,
|
||||
theme_description: value.theme_description,
|
||||
player_image_description: value.player_image_description,
|
||||
opponent_image_description: value.opponent_image_description,
|
||||
onomatopoeia: value.onomatopoeia,
|
||||
player_character_image_src: value.player_character_image_src,
|
||||
opponent_character_image_src: value.opponent_character_image_src,
|
||||
ui_background_image_src: value.ui_background_image_src,
|
||||
difficulty_preset: value.difficulty_preset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarkBattleDraftConfigUpdateRequest {
|
||||
pub draft_id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub work_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub config_version: Option<u32>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ruleset_version: Option<String>,
|
||||
pub title: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
pub theme_description: String,
|
||||
pub player_image_description: String,
|
||||
pub opponent_image_description: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub onomatopoeia: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub player_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub opponent_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ui_background_image_src: Option<String>,
|
||||
#[serde(default)]
|
||||
pub difficulty_preset: BarkBattleDifficultyPreset,
|
||||
}
|
||||
|
||||
impl From<BarkBattleDraftConfigUpdateRequest> for BarkBattleConfigEditorPayload {
|
||||
fn from(value: BarkBattleDraftConfigUpdateRequest) -> Self {
|
||||
Self {
|
||||
title: value.title,
|
||||
description: value.description,
|
||||
theme_description: value.theme_description,
|
||||
player_image_description: value.player_image_description,
|
||||
opponent_image_description: value.opponent_image_description,
|
||||
onomatopoeia: value.onomatopoeia,
|
||||
player_character_image_src: value.player_character_image_src,
|
||||
opponent_character_image_src: value.opponent_character_image_src,
|
||||
ui_background_image_src: value.ui_background_image_src,
|
||||
bark_sound_src: value.bark_sound_src,
|
||||
difficulty_preset: value.difficulty_preset,
|
||||
leaderboard_enabled: value.leaderboard_enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,6 +163,30 @@ pub struct BarkBattleWorkPublishRequest {
|
||||
pub published_snapshot: Option<BarkBattleConfigEditorPayload>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarkBattleImageAssetGenerateRequest {
|
||||
pub slot: BarkBattleAssetSlot,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub draft_id: Option<String>,
|
||||
pub config: BarkBattleConfigEditorPayload,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarkBattleGeneratedImageAsset {
|
||||
pub image_src: String,
|
||||
pub asset_id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub source_type: Option<String>,
|
||||
pub model: String,
|
||||
pub size: String,
|
||||
pub task_id: String,
|
||||
pub prompt: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub actual_prompt: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarkBattleDraftConfig {
|
||||
@@ -128,20 +200,19 @@ pub struct BarkBattleDraftConfig {
|
||||
pub title: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
pub theme_preset: String,
|
||||
pub player_dog_skin_preset: String,
|
||||
pub opponent_dog_skin_preset: String,
|
||||
pub theme_description: String,
|
||||
pub player_image_description: String,
|
||||
pub opponent_image_description: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub onomatopoeia: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub player_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub opponent_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ui_background_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bark_sound_src: Option<String>,
|
||||
#[serde(default)]
|
||||
pub difficulty_preset: BarkBattleDifficultyPreset,
|
||||
pub leaderboard_enabled: bool,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
@@ -154,15 +225,14 @@ impl Default for BarkBattleDraftConfig {
|
||||
ruleset_version: None,
|
||||
title: String::new(),
|
||||
description: None,
|
||||
theme_preset: String::new(),
|
||||
player_dog_skin_preset: String::new(),
|
||||
opponent_dog_skin_preset: String::new(),
|
||||
theme_description: String::new(),
|
||||
player_image_description: String::new(),
|
||||
opponent_image_description: String::new(),
|
||||
onomatopoeia: None,
|
||||
player_character_image_src: None,
|
||||
opponent_character_image_src: None,
|
||||
ui_background_image_src: None,
|
||||
bark_sound_src: None,
|
||||
difficulty_preset: BarkBattleDifficultyPreset::Normal,
|
||||
leaderboard_enabled: true,
|
||||
updated_at: String::new(),
|
||||
}
|
||||
}
|
||||
@@ -180,19 +250,18 @@ pub struct BarkBattlePublishedConfig {
|
||||
pub title: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
pub theme_preset: String,
|
||||
pub player_dog_skin_preset: String,
|
||||
pub opponent_dog_skin_preset: String,
|
||||
pub theme_description: String,
|
||||
pub player_image_description: String,
|
||||
pub opponent_image_description: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub onomatopoeia: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub player_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub opponent_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ui_background_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bark_sound_src: Option<String>,
|
||||
pub difficulty_preset: BarkBattleDifficultyPreset,
|
||||
pub leaderboard_enabled: bool,
|
||||
pub updated_at: String,
|
||||
pub published_at: String,
|
||||
}
|
||||
@@ -210,21 +279,75 @@ pub struct BarkBattleRuntimeConfig {
|
||||
pub draw_threshold: f32,
|
||||
pub min_bark_gap_ms: u64,
|
||||
pub difficulty_preset: BarkBattleDifficultyPreset,
|
||||
pub theme_preset: String,
|
||||
pub player_dog_skin_preset: String,
|
||||
pub opponent_dog_skin_preset: String,
|
||||
pub theme_description: String,
|
||||
pub player_image_description: String,
|
||||
pub opponent_image_description: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub onomatopoeia: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub player_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub opponent_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ui_background_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bark_sound_src: Option<String>,
|
||||
pub leaderboard_enabled: bool,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarkBattleWorkSummary {
|
||||
pub work_id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub draft_id: Option<String>,
|
||||
pub owner_user_id: String,
|
||||
pub author_display_name: String,
|
||||
pub title: String,
|
||||
pub summary: String,
|
||||
pub theme_description: String,
|
||||
pub player_image_description: String,
|
||||
pub opponent_image_description: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub onomatopoeia: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub player_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub opponent_character_image_src: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ui_background_image_src: Option<String>,
|
||||
pub difficulty_preset: BarkBattleDifficultyPreset,
|
||||
pub status: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub generation_status: Option<String>,
|
||||
pub publish_ready: bool,
|
||||
pub play_count: u64,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub finish_count: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub win_count: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub draw_count: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub loss_count: Option<u64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub recent_play_count_7d: Option<u64>,
|
||||
pub updated_at: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub published_at: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarkBattleWorksResponse {
|
||||
#[serde(default)]
|
||||
pub items: Vec<BarkBattleWorkSummary>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarkBattleWorkDetailResponse {
|
||||
pub item: BarkBattleWorkSummary,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarkBattleRunStartRequest {
|
||||
@@ -425,6 +548,115 @@ mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn editor_and_runtime_contract_use_description_fields_only() {
|
||||
let editor = BarkBattleConfigEditorPayload {
|
||||
title: "周末狗狗杯".to_string(),
|
||||
description: Some("轻配置草稿".to_string()),
|
||||
theme_description: "霓虹公园里的欢乐擂台".to_string(),
|
||||
player_image_description: "戴红围巾的柴犬主角".to_string(),
|
||||
opponent_image_description: "蓝色护目镜哈士奇对手".to_string(),
|
||||
onomatopoeia: Some(vec!["轰汪!".to_string(), "炸场!".to_string()]),
|
||||
player_character_image_src: Some("/generated-bark-battle/player.png".to_string()),
|
||||
opponent_character_image_src: Some("https://example.test/opponent.png".to_string()),
|
||||
ui_background_image_src: Some("/generated-bark-battle/ui.png".to_string()),
|
||||
difficulty_preset: BarkBattleDifficultyPreset::Hard,
|
||||
};
|
||||
let payload = serde_json::to_value(editor).expect("config should serialize");
|
||||
|
||||
assert_eq!(payload["themeDescription"], json!("霓虹公园里的欢乐擂台"));
|
||||
assert_eq!(
|
||||
payload["playerImageDescription"],
|
||||
json!("戴红围巾的柴犬主角")
|
||||
);
|
||||
assert_eq!(
|
||||
payload["opponentImageDescription"],
|
||||
json!("蓝色护目镜哈士奇对手")
|
||||
);
|
||||
assert_eq!(payload["onomatopoeia"], json!(["轰汪!", "炸场!"]));
|
||||
for removed in [
|
||||
"themePreset",
|
||||
"playerDogSkinPreset",
|
||||
"opponentDogSkinPreset",
|
||||
"barkSoundSrc",
|
||||
"leaderboardEnabled",
|
||||
] {
|
||||
assert!(
|
||||
!payload.as_object().unwrap().contains_key(removed),
|
||||
"{removed} must not remain in v1 public config payload"
|
||||
);
|
||||
}
|
||||
|
||||
let runtime = BarkBattleRuntimeConfig {
|
||||
work_id: "bark-battle-work-1".to_string(),
|
||||
config_version: 1,
|
||||
ruleset_version: "bark-battle-ruleset-v1".to_string(),
|
||||
play_type_id: "bark-battle".to_string(),
|
||||
duration_ms: 30_000,
|
||||
energy_min: 0.0,
|
||||
energy_max: 100.0,
|
||||
draw_threshold: 5.0,
|
||||
min_bark_gap_ms: 220,
|
||||
difficulty_preset: BarkBattleDifficultyPreset::Normal,
|
||||
theme_description: "阳光草坪".to_string(),
|
||||
player_image_description: "小柴犬".to_string(),
|
||||
opponent_image_description: "大金毛".to_string(),
|
||||
onomatopoeia: Some(vec!["轰汪!".to_string(), "燃起来!".to_string()]),
|
||||
player_character_image_src: None,
|
||||
opponent_character_image_src: None,
|
||||
ui_background_image_src: None,
|
||||
updated_at: "2026-05-20T00:00:00Z".to_string(),
|
||||
};
|
||||
let payload = serde_json::to_value(runtime).expect("runtime should serialize");
|
||||
assert_eq!(payload["themeDescription"], json!("阳光草坪"));
|
||||
assert!(
|
||||
!payload
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.contains_key("leaderboardEnabled")
|
||||
);
|
||||
assert!(!payload.as_object().unwrap().contains_key("barkSoundSrc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn work_summary_responses_use_public_gallery_contract() {
|
||||
let response = BarkBattleWorksResponse {
|
||||
items: vec![BarkBattleWorkSummary {
|
||||
work_id: "bark-battle-work-1".to_string(),
|
||||
draft_id: Some("bark-battle-draft-1".to_string()),
|
||||
owner_user_id: "user-1".to_string(),
|
||||
author_display_name: "玩家".to_string(),
|
||||
title: "汪汪测试杯".to_string(),
|
||||
summary: "轻量公开卡片".to_string(),
|
||||
theme_description: "阳光草坪".to_string(),
|
||||
player_image_description: "小柴犬".to_string(),
|
||||
opponent_image_description: "大金毛".to_string(),
|
||||
onomatopoeia: Some(vec!["轰汪!".to_string(), "燃起来!".to_string()]),
|
||||
player_character_image_src: None,
|
||||
opponent_character_image_src: None,
|
||||
ui_background_image_src: None,
|
||||
difficulty_preset: BarkBattleDifficultyPreset::Normal,
|
||||
status: "published".to_string(),
|
||||
generation_status: Some("ready".to_string()),
|
||||
publish_ready: true,
|
||||
play_count: 3,
|
||||
finish_count: Some(2),
|
||||
win_count: Some(1),
|
||||
draw_count: Some(1),
|
||||
loss_count: Some(0),
|
||||
recent_play_count_7d: Some(2),
|
||||
updated_at: "2026-05-20T00:00:00Z".to_string(),
|
||||
published_at: Some("2026-05-20T00:00:00Z".to_string()),
|
||||
}],
|
||||
};
|
||||
|
||||
let payload = serde_json::to_value(response).expect("works response should serialize");
|
||||
|
||||
assert_eq!(payload["items"][0]["themeDescription"], json!("阳光草坪"));
|
||||
assert_eq!(payload["items"][0]["recentPlayCount7d"], json!(2));
|
||||
assert_eq!(payload["items"][0]["status"], json!("published"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draft_config_defaults_to_normal_difficulty() {
|
||||
let config = BarkBattleDraftConfig::default();
|
||||
@@ -523,15 +755,14 @@ mod tests {
|
||||
ruleset_version: Some("bark-battle-ruleset-v1".to_string()),
|
||||
title: "汪汪测试杯".to_string(),
|
||||
description: None,
|
||||
theme_preset: "sunny-yard".to_string(),
|
||||
player_dog_skin_preset: "主角".to_string(),
|
||||
opponent_dog_skin_preset: "对手".to_string(),
|
||||
theme_description: "阳光草坪".to_string(),
|
||||
player_image_description: "主角".to_string(),
|
||||
opponent_image_description: "对手".to_string(),
|
||||
onomatopoeia: None,
|
||||
player_character_image_src: None,
|
||||
opponent_character_image_src: None,
|
||||
ui_background_image_src: None,
|
||||
bark_sound_src: None,
|
||||
difficulty_preset: BarkBattleDifficultyPreset::Normal,
|
||||
leaderboard_enabled: true,
|
||||
updated_at: "2026-05-14T10:00:00.000Z".to_string(),
|
||||
};
|
||||
|
||||
@@ -540,10 +771,96 @@ mod tests {
|
||||
assert_eq!(payload["draftId"], json!("bark-battle-draft-1"));
|
||||
assert_eq!(payload["workId"], json!("bark-battle-work-1"));
|
||||
assert_eq!(payload["configVersion"], json!(2));
|
||||
assert_eq!(payload["rulesetVersion"], json!("bark-battle-ruleset-v1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draft_config_update_request_serializes_generated_assets() {
|
||||
let update = BarkBattleDraftConfigUpdateRequest {
|
||||
draft_id: "bark-battle-draft-1".to_string(),
|
||||
work_id: Some("BB-12345678".to_string()),
|
||||
config_version: Some(2),
|
||||
ruleset_version: Some("bark-battle-ruleset-v1".to_string()),
|
||||
title: "汪汪测试杯".to_string(),
|
||||
description: None,
|
||||
theme_description: "阳光草坪".to_string(),
|
||||
player_image_description: "主角".to_string(),
|
||||
opponent_image_description: "对手".to_string(),
|
||||
onomatopoeia: Some(vec!["炸场!".to_string(), "破阵!".to_string()]),
|
||||
player_character_image_src: Some("/generated-bark-battle/player.png".to_string()),
|
||||
opponent_character_image_src: Some("/generated-bark-battle/opponent.png".to_string()),
|
||||
ui_background_image_src: Some("/generated-bark-battle/background.png".to_string()),
|
||||
difficulty_preset: BarkBattleDifficultyPreset::Normal,
|
||||
};
|
||||
|
||||
let payload = serde_json::to_value(update).expect("draft update should serialize");
|
||||
|
||||
assert_eq!(payload["draftId"], json!("bark-battle-draft-1"));
|
||||
assert_eq!(payload["workId"], json!("BB-12345678"));
|
||||
assert_eq!(
|
||||
payload["rulesetVersion"],
|
||||
json!("bark-battle-ruleset-v1")
|
||||
payload["playerCharacterImageSrc"],
|
||||
json!("/generated-bark-battle/player.png")
|
||||
);
|
||||
assert_eq!(
|
||||
payload["opponentCharacterImageSrc"],
|
||||
json!("/generated-bark-battle/opponent.png")
|
||||
);
|
||||
assert_eq!(
|
||||
payload["uiBackgroundImageSrc"],
|
||||
json!("/generated-bark-battle/background.png")
|
||||
);
|
||||
assert!(!payload.as_object().unwrap().contains_key("barkSoundSrc"));
|
||||
assert!(
|
||||
!payload
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.contains_key("leaderboardEnabled")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_generation_request_uses_dedicated_asset_slot_and_result_prompt() {
|
||||
let request = BarkBattleImageAssetGenerateRequest {
|
||||
slot: BarkBattleAssetSlot::OpponentCharacter,
|
||||
draft_id: Some("bark-battle-draft-1".to_string()),
|
||||
config: BarkBattleConfigEditorPayload {
|
||||
title: "汪汪冠军杯".to_string(),
|
||||
description: Some(String::new()),
|
||||
theme_description: "霓虹公园擂台".to_string(),
|
||||
player_image_description: "红围巾柴犬".to_string(),
|
||||
opponent_image_description: "蓝头带哈士奇".to_string(),
|
||||
onomatopoeia: Some(vec!["轰汪!".to_string(), "炸场!".to_string()]),
|
||||
player_character_image_src: None,
|
||||
opponent_character_image_src: None,
|
||||
ui_background_image_src: None,
|
||||
difficulty_preset: BarkBattleDifficultyPreset::Normal,
|
||||
},
|
||||
};
|
||||
let payload = serde_json::to_value(request).expect("request should serialize");
|
||||
|
||||
assert_eq!(payload["slot"], json!("opponent-character"));
|
||||
assert_eq!(
|
||||
payload["config"]["opponentImageDescription"],
|
||||
json!("蓝头带哈士奇")
|
||||
);
|
||||
|
||||
let response = BarkBattleGeneratedImageAsset {
|
||||
image_src: "/generated-bark-battle-assets/draft/opponent/image.webp".to_string(),
|
||||
asset_id: "asset-1".to_string(),
|
||||
source_type: Some("generated".to_string()),
|
||||
model: "gpt-image-2".to_string(),
|
||||
size: "1024*1024".to_string(),
|
||||
task_id: "task-1".to_string(),
|
||||
prompt: "后端拼装后的对手形象 prompt".to_string(),
|
||||
actual_prompt: None,
|
||||
};
|
||||
let payload = serde_json::to_value(response).expect("response should serialize");
|
||||
|
||||
assert_eq!(
|
||||
payload["imageSrc"],
|
||||
json!("/generated-bark-battle-assets/draft/opponent/image.webp")
|
||||
);
|
||||
assert_eq!(payload["prompt"], json!("后端拼装后的对手形象 prompt"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -551,15 +868,14 @@ mod tests {
|
||||
let config = BarkBattleConfigEditorPayload {
|
||||
title: "周末狗狗杯".to_string(),
|
||||
description: Some("轻配置草稿".to_string()),
|
||||
theme_preset: "neon-park".to_string(),
|
||||
player_dog_skin_preset: "shiba".to_string(),
|
||||
opponent_dog_skin_preset: "husky".to_string(),
|
||||
theme_description: "霓虹公园".to_string(),
|
||||
player_image_description: "柴犬主角".to_string(),
|
||||
opponent_image_description: "哈士奇对手".to_string(),
|
||||
onomatopoeia: Some(vec!["轰汪!".to_string(), "冲啊!".to_string()]),
|
||||
player_character_image_src: Some("/generated-bark-battle/player.png".to_string()),
|
||||
opponent_character_image_src: Some("https://example.test/opponent.png".to_string()),
|
||||
ui_background_image_src: Some("/generated-bark-battle/ui.png".to_string()),
|
||||
bark_sound_src: Some("/generated-bark-battle/bark.mp3".to_string()),
|
||||
difficulty_preset: BarkBattleDifficultyPreset::Hard,
|
||||
leaderboard_enabled: true,
|
||||
};
|
||||
|
||||
let payload = serde_json::to_value(config).expect("config should serialize");
|
||||
@@ -576,10 +892,7 @@ mod tests {
|
||||
payload["uiBackgroundImageSrc"],
|
||||
json!("/generated-bark-battle/ui.png")
|
||||
);
|
||||
assert_eq!(
|
||||
payload["barkSoundSrc"],
|
||||
json!("/generated-bark-battle/bark.mp3")
|
||||
);
|
||||
assert!(!payload.as_object().unwrap().contains_key("barkSoundSrc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user