统一发布 bootstrap 在事务内补必填文本校验

在 `get_or_create_game_distribution_game_for_publish_tx` 落库前校验 title/summary/category/orientation 非空,与旧 create_game 同口径,不假设 api-server 是唯一调用方
新增源码结构回归测试
This commit is contained in:
2026-10-08 12:32:13 +08:00
parent 4967377598
commit 122230e257
@@ -3816,16 +3816,23 @@ fn get_or_create_game_distribution_game_for_publish_tx(
}
}
// 事务内最后一道非空防线:api-server 已校验,但这条 get-or-create 是公开 procedure 的落库点,
// 不能假设只有那一个调用方(与旧 `create_game_distribution_game_tx` 同口径)。
let title = required_game_distribution_text(input.title.clone(), "title")?;
let summary = required_game_distribution_text(input.summary.clone(), "summary")?;
let category = required_game_distribution_text(input.category.clone(), "category")?;
let orientation = required_game_distribution_text(input.orientation.clone(), "orientation")?;
let now = Timestamp::from_micros_since_unix_epoch(input.now_micros);
ctx.db
.game_distribution_game()
.insert(GameDistributionGame {
game_id: input.game_id.clone(),
owner_user_id: owner_user_id.to_string(),
title: input.title.clone(),
summary: input.summary.clone(),
title,
summary,
description: input.description.clone().unwrap_or_default(),
category: input.category.clone(),
category,
tags_json: input.tags_json.clone(),
cover_asset_id: None,
author_name: input.author_name.clone(),
@@ -3834,7 +3841,7 @@ fn get_or_create_game_distribution_game_for_publish_tx(
device_support_mobile: input.device_support_mobile,
device_support_touch: input.device_support_touch,
input_modes_json: input.input_modes_json.clone(),
orientation: input.orientation.clone(),
orientation,
publication_revision: 0,
active_version_id: None,
visibility: GAME_DISTRIBUTION_VISIBILITY_UNPUBLISHED.to_string(),
@@ -8477,6 +8484,28 @@ mod tests {
panic!("{marker} 的函数体不闭合");
}
/// get-or-create 的作品行落库点必须自己再校验一次必填文本。
///
/// api-server 是当前唯一调用方,但这是公开 procedure 的落库点;与旧
/// `create_game_distribution_game_tx` 同口径,空标题 / 空分类 / 未知 orientation 必须在事务内
/// 失败关闭,而不是写进脏行。
#[test]
fn bootstrap_publish_game_validates_required_text_before_insert() {
let source = include_str!("game_distribution.rs");
let body = function_body(
source,
"fn get_or_create_game_distribution_game_for_publish_tx(",
);
for field in ["title", "summary", "category", "orientation"] {
assert!(
body.contains(&format!(
"required_game_distribution_text(input.{field}.clone(), \"{field}\")?"
)),
"作品行 bootstrap 必须校验 {field}"
);
}
}
/// 版本表的 `change_summary` 必须留在**表尾**并带默认值。
///
/// schema guard 只允许「末尾追加 + 显式默认」:插在中间会让既有行的列序整体错位(不是迁移能补