Files
Genarrative/server-rs/crates/shared-contracts/tests/model3d_multiview_request_contract.rs
T
k88936 c48f0627cf 修复 ts-rs 对 enum 级 deny_unknown_fields 的解析告警并保留契约严格性
- 把 server-rs 与 apps/ai-game-creator-shell/src-tauri 两个 workspace 的 ts-rs 从 registry 固定到上游修复 commit fd8679ef20f866f747c046b30bd29c15e3d7a003(PR #491:已发布版 12.0.1 的 EnumAttr 不识别 deny_unknown_fields,只有 StructAttr 会静默忽略它),两个 Cargo.lock 同步改成 git 源
- 说明固定原因与退出条件:crates.io 最新仍是 12.0.1、GitHub 最新 tag 仍是 v12.0.0,等发布 > 12.0.1 后改回 registry 版本并删除 commit 固定
- 明确不使用 ts-rs 的 no-serde-warnings feature 消警:它会连同 deserialize_with 这类真实未支持的 serde 属性一起静默,掩盖 Rust 与 TS 绑定的漂移
- 保留 shared-contracts 的 Model3dMultiviewInputs enum 级 deny_unknown_fields(该属性一直由 serde 生效,告警只来自 ts-rs 的属性解析),注释改为写明不要为消警删属性、也不要改用 no-serde-warnings
- 新增契约测试 tests/model3d_multiview_request_contract.rs:钉住 views/taskId 变体内部与请求体顶层拒绝未知字段,并覆盖 kind 与 camelCase 字段名解析
- 记录排障经验与决策:pitfalls.md 说明无 span 的告警不代表属性失效,decision-log.md 记录固定 commit、影响面与回退条件
- 验证:cargo check -p shared-contracts 与该 crate 的 cargo test 不再出现 deny_unknown_fields 告警且全绿;cargo test --locked -p shared-contracts model3d 加 prettier 后 packages/shared 的 model3d 绑定零 diff;AGC 的 cargo check --locked --bin genarrative-ai-game-creator-shell 不再出现该告警(AGC 侧仍保留 1 条既有的 deserialize_with 告警,与本改动无关);npm run check:encoding、npm run check:doc-index、git diff --check、cargo fmt --all --check 通过
2026-09-19 16:56:24 +08:00

79 lines
2.6 KiB
Rust

use serde_json::json;
use shared_contracts::model3d::common::Model3dModelVersion;
use shared_contracts::model3d::multiview_to_model::{
Model3dMultiviewInputs, Model3dMultiviewToModelRequest,
};
#[test]
fn multiview_inputs_accepts_declared_fields_and_camel_case_tag() {
let inputs: Model3dMultiviewInputs = serde_json::from_value(json!({
"kind": "views",
"front": "front.png",
"left": "left.png"
}))
.expect("已声明字段应可反序列化");
match inputs {
Model3dMultiviewInputs::Views { front, left, .. } => {
assert_eq!(front, "front.png");
assert_eq!(left.as_deref(), Some("left.png"));
}
Model3dMultiviewInputs::TaskId { .. } => panic!("kind=views 不应解成 taskId 变体"),
}
let task: Model3dMultiviewInputs = serde_json::from_value(json!({
"kind": "taskId",
"taskId": "tripo-task-1"
}))
.expect("taskId 变体使用 camelCase 字段名");
assert!(matches!(task, Model3dMultiviewInputs::TaskId { .. }));
}
#[test]
fn multiview_inputs_reject_unknown_fields_inside_variants() {
// enum 级 `deny_unknown_fields` 由 serde 生效,ts-rs 12 无法在 TS 绑定里表达;
// 这条用例钉住该严格性,防止为消除 ts-rs 告警而把它改成非严格模式。
let error = serde_json::from_value::<Model3dMultiviewInputs>(json!({
"kind": "views",
"front": "front.png",
"left": "left.png",
"back": "back.png",
"right": "right.png",
"unexpected": true
}))
.expect_err("变体里出现未知字段应被拒绝");
assert!(
error.to_string().contains("unknown field"),
"未知字段错误应指出 unknown field,实际为:{error}"
);
let error = serde_json::from_value::<Model3dMultiviewInputs>(json!({
"kind": "taskId",
"taskId": "tripo-task-1",
"unexpected": true
}))
.expect_err("taskId 变体里出现未知字段应被拒绝");
assert!(
error.to_string().contains("unknown field"),
"未知字段错误应指出 unknown field,实际为:{error}"
);
}
#[test]
fn multiview_request_rejects_unknown_top_level_fields() {
let error = serde_json::from_value::<Model3dMultiviewToModelRequest>(json!({
"inputs": { "kind": "taskId", "taskId": "tripo-task-1" },
"model": Model3dModelVersion::H31.as_str(),
"unexpected": true
}))
.expect_err("请求体顶层出现未知字段应被拒绝");
assert!(
error.to_string().contains("unknown field"),
"未知字段错误应指出 unknown field,实际为:{error}"
);
}