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 + ); +}