From d4af6802fbfc28400ad91278727c0306e3b5f470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Mon, 21 Sep 2026 15:16:06 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=A4=9A=E8=A7=86=E5=9B=BE=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=A5=91=E7=BA=A6=E7=9A=84=E5=85=A8=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=BE=80=E8=BF=94=E7=94=A8=E4=BE=8B=20-=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=85=A8=E5=AD=97=E6=AE=B5=E5=BE=80=E8=BF=94=EF=BC=9A=E9=92=89?= =?UTF-8?q?=E4=BD=8F=20camelCase=20=E5=AD=97=E6=AE=B5=E5=90=8D=E3=80=81?= =?UTF-8?q?=E5=90=84=E6=9E=9A=E4=B8=BE=E7=BA=BF=E4=B8=8A=E5=8F=96=E5=80=BC?= =?UTF-8?q?=E4=B8=8E=20Option=20=E7=9A=84=E5=8F=AF=E7=9C=81=E7=95=A5?= =?UTF-8?q?=E5=86=99=E6=B3=95=20-=20=E6=96=B0=E5=A2=9E=E6=98=BE=E5=BC=8F?= =?UTF-8?q?=20null=20=E5=BE=80=E8=BF=94=EF=BC=9A=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E5=8F=AF=E9=80=89=E5=AD=97=E6=AE=B5=E5=9B=9E=E5=86=99=E6=97=B6?= =?UTF-8?q?=E4=BB=8D=E6=98=AF=20null=20=E8=80=8C=E4=B8=8D=E6=98=AF?= =?UTF-8?q?=E6=95=B4=E5=AD=97=E6=AE=B5=E6=B6=88=E5=A4=B1=20-=20=E8=A1=A5?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E5=90=8E=E5=AD=97=E6=AE=B5=E6=94=B9=E5=90=8D?= =?UTF-8?q?=E6=88=96=E5=8F=AF=E7=A9=BA=E6=80=A7=E6=BC=82=E7=A7=BB=E4=BC=9A?= =?UTF-8?q?=E8=A2=AB=E6=B5=8B=E8=AF=95=E6=8B=A6=E4=BD=8F=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E6=98=AF=E5=8F=AA=E5=9C=A8=20TS=20=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E9=87=8C=E5=8F=AF=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model3d_multiview_request_contract.rs | 139 +++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/server-rs/crates/shared-contracts/tests/model3d_multiview_request_contract.rs b/server-rs/crates/shared-contracts/tests/model3d_multiview_request_contract.rs index b3802bcac..c67bf35cc 100644 --- a/server-rs/crates/shared-contracts/tests/model3d_multiview_request_contract.rs +++ b/server-rs/crates/shared-contracts/tests/model3d_multiview_request_contract.rs @@ -1,5 +1,8 @@ use serde_json::json; -use shared_contracts::model3d::common::Model3dModelVersion; +use shared_contracts::model3d::common::{ + Model3dCompression, Model3dExportOrientation, Model3dGeometryQuality, Model3dInputOrientation, + Model3dModelVersion, Model3dTextureAlignment, Model3dTextureQuality, Model3dTextureVersion, +}; use shared_contracts::model3d::multiview_to_model::{ Model3dMultiviewInputs, Model3dMultiviewToModelRequest, }; @@ -76,3 +79,137 @@ fn multiview_request_rejects_unknown_top_level_fields() { "未知字段错误应指出 unknown field,实际为:{error}" ); } + +#[test] +fn multiview_request_round_trips_every_optional_field() { + // 全字段往返:钉住 camelCase 字段名、枚举线上取值与 Option 的可省略写法, + // 与生成的 TypeScript 绑定保持一致。 + let payload = json!({ + "inputs": { + "kind": "views", + "front": "front.png", + "left": "left.png", + "back": "back.png", + "right": "right.png" + }, + "model": "v3.1-20260211", + "modelSeed": 7, + "textureSeed": 8, + "texture": true, + "pbr": true, + "textureQuality": "standard", + "textureVersion": "v3.0-20250812", + "delight": false, + "geometryQuality": "detailed", + "textureAlignment": "original_image", + "faceLimit": 20_000, + "autoSize": false, + "orientation": "align_image", + "quad": false, + "smartLowPoly": true, + "generateParts": false, + "compress": "geometry", + "exportUv": true, + "exportOrientation": "-y" + }); + + let request: Model3dMultiviewToModelRequest = + serde_json::from_value(payload.clone()).expect("全字段请求应可反序列化"); + + match &request.inputs { + Model3dMultiviewInputs::Views { + front, + left, + back, + right, + } => { + assert_eq!(front, "front.png"); + assert_eq!(left.as_deref(), Some("left.png")); + assert_eq!(back.as_deref(), Some("back.png")); + assert_eq!(right.as_deref(), Some("right.png")); + } + Model3dMultiviewInputs::TaskId { .. } => panic!("kind=views 不应解成 taskId 变体"), + } + assert_eq!(request.model, Model3dModelVersion::H31); + assert_eq!(request.model_seed, Some(7)); + assert_eq!(request.texture_seed, Some(8)); + assert_eq!(request.texture, Some(true)); + assert_eq!(request.pbr, Some(true)); + assert_eq!( + request.texture_quality, + Some(Model3dTextureQuality::Standard) + ); + assert_eq!(request.texture_version, Some(Model3dTextureVersion::V30)); + assert_eq!(request.delight, Some(false)); + assert_eq!( + request.geometry_quality, + Some(Model3dGeometryQuality::Detailed) + ); + assert_eq!( + request.texture_alignment, + Some(Model3dTextureAlignment::OriginalImage) + ); + assert_eq!(request.face_limit, Some(20_000)); + assert_eq!(request.auto_size, Some(false)); + assert_eq!( + request.orientation, + Some(Model3dInputOrientation::AlignImage) + ); + assert_eq!(request.quad, Some(false)); + assert_eq!(request.smart_low_poly, Some(true)); + assert_eq!(request.generate_parts, Some(false)); + assert_eq!(request.compress, Some(Model3dCompression::Geometry)); + assert_eq!(request.export_uv, Some(true)); + assert_eq!( + request.export_orientation, + Some(Model3dExportOrientation::MinusY) + ); + + assert_eq!( + serde_json::to_value(&request).expect("请求应可回写为 JSON"), + payload + ); +} + +#[test] +fn multiview_request_round_trips_explicit_nulls() { + // 显式 null 是可省略写法之外的另一种合法形态,回写时必须仍是 null 而不是缺字段。 + let payload = json!({ + "inputs": { "kind": "taskId", "taskId": "tripo-task-1" }, + "model": "P2-20260801", + "modelSeed": null, + "textureSeed": null, + "texture": null, + "pbr": null, + "textureQuality": null, + "textureVersion": null, + "delight": null, + "geometryQuality": null, + "textureAlignment": null, + "faceLimit": null, + "autoSize": null, + "orientation": null, + "quad": null, + "smartLowPoly": null, + "generateParts": null, + "compress": null, + "exportUv": null, + "exportOrientation": null + }); + + let request: Model3dMultiviewToModelRequest = + serde_json::from_value(payload.clone()).expect("显式 null 的请求应可反序列化"); + + assert!(matches!( + request.inputs, + Model3dMultiviewInputs::TaskId { .. } + )); + assert_eq!(request.model, Model3dModelVersion::P2); + assert_eq!(request.model_seed, None); + assert_eq!(request.export_orientation, None); + + assert_eq!( + serde_json::to_value(&request).expect("请求应可回写为 JSON"), + payload + ); +}