From 2348315dba61ed9cac15eb374d33e887646e6503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Tue, 22 Sep 2026 16:46:19 +0800 Subject: [PATCH] =?UTF-8?q?3D=20=E7=BB=93=E6=9E=9C=E6=89=93=E6=A0=87?= =?UTF-8?q?=E5=A5=91=E7=BA=A6=E6=B5=8B=E8=AF=95=E8=A1=A5=E9=BD=90=20previe?= =?UTF-8?q?w=20=E4=B8=8E=20imageToModel=20=E5=88=86=E6=94=AF=20-=20textToM?= =?UTF-8?q?odel=20=E7=94=A8=E4=BE=8B=E8=A1=A5=20preview=20=E4=BA=A7?= =?UTF-8?q?=E7=89=A9=E7=9A=84=E7=9B=B8=E7=AD=89=E6=96=AD=E8=A8=80=20-=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AF=B9=E7=A7=B0=E7=9A=84=20imageToModel=20?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=EF=BC=8C=E5=AE=88=E4=BD=8F=20tag=20=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E4=B8=8E=E5=88=86=E6=94=AF=E5=AD=97=E6=AE=B5=20-=20?= =?UTF-8?q?=E8=A3=B8=E5=AD=97=E7=AC=A6=E4=B8=B2=20source=20=E7=9A=84?= =?UTF-8?q?=E8=B4=9F=E4=BE=8B=E6=94=B9=E4=B8=BA=E7=82=B9=E5=90=8D=20Model3?= =?UTF-8?q?dGenerationSource=20=E5=B9=B6=E5=8D=95=E7=8B=AC=E5=8F=8D?= =?UTF-8?q?=E5=BA=8F=E5=88=97=E5=8C=96=20source=20=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/model3d_api_request_contract.rs | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/server-rs/crates/shared-contracts/tests/model3d_api_request_contract.rs b/server-rs/crates/shared-contracts/tests/model3d_api_request_contract.rs index 05c4a14ba..50aa7c121 100644 --- a/server-rs/crates/shared-contracts/tests/model3d_api_request_contract.rs +++ b/server-rs/crates/shared-contracts/tests/model3d_api_request_contract.rs @@ -1,7 +1,8 @@ use serde_json::json; use shared_contracts::model3d::Model3dGenerationResult; use shared_contracts::model3d::common::{ - Model3dGeneratedArtifact, Model3dGenerationTarget, Model3dGenerationTargetRef, + Model3dGeneratedArtifact, Model3dGenerationSource, Model3dGenerationTarget, + Model3dGenerationTargetRef, }; use shared_contracts::model3d::image_to_model::Model3dImageToModelRequest; use shared_contracts::model3d::text_to_model::Model3dTextToModelRequest; @@ -56,8 +57,18 @@ fn image_source_rejects_ambiguous_and_raw_input() { "generation": { "model": "v3.1-20260211" }, "target": { "kind": "assetLibrary", "folderId": "folder-1", "label": "木椅" } }); - serde_json::from_value::(raw_input) + let error = serde_json::from_value::(raw_input) .expect_err("裸字符串图片输入应被拒绝"); + // 拒绝必须发生在 source 本身:错误里点名 Model3dGenerationSource,避免将来 + // generation / target 的必填变化把这条负例“顶替”成通过。 + assert!( + error.to_string().contains("Model3dGenerationSource"), + "裸字符串 source 的报错应点名 Model3dGenerationSource,实际为:{error}" + ); + + // 同样把 source 单独反序列化一遍,锁死被拒的是图片输入的形状本身。 + serde_json::from_value::(json!("https://example.com/a.png")) + .expect_err("裸字符串 source 值应被拒绝"); } #[test] @@ -113,4 +124,60 @@ fn generation_result_is_tagged_per_endpoint() { sha256: "0f1e".into(), } ); + assert_eq!( + text.preview, + Model3dGeneratedArtifact { + object_key: "models/res-1.webp".into(), + content_type: "image/webp".into(), + content_length: 20_480, + sha256: "2a3b".into(), + } + ); + + // 对称覆盖 imageToModel 分支:tag 路由与分支字段都要单独守住。 + let image_result: Model3dGenerationResult = serde_json::from_value(json!({ + "kind": "imageToModel", + "target": { "kind": "projectResource", "resourceId": "res-2" }, + "model": { + "objectKey": "models/res-2.glb", + "contentType": "model/gltf-binary", + "contentLength": 41_636_077_u64, + "sha256": "3c4d" + }, + "preview": { + "objectKey": "models/res-2.webp", + "contentType": "image/webp", + "contentLength": 20_481_u64, + "sha256": "4e5f" + } + })) + .expect("按端点的结果应可反序列化"); + + let Model3dGenerationResult::ImageToModel(image) = image_result else { + panic!("kind=imageToModel 不应解成 textToModel 分支"); + }; + assert_eq!( + image.target, + Model3dGenerationTargetRef::ProjectResource { + resource_id: "res-2".into() + } + ); + assert_eq!( + image.model, + Model3dGeneratedArtifact { + object_key: "models/res-2.glb".into(), + content_type: "model/gltf-binary".into(), + content_length: 41_636_077, + sha256: "3c4d".into(), + } + ); + assert_eq!( + image.preview, + Model3dGeneratedArtifact { + object_key: "models/res-2.webp".into(), + content_type: "image/webp".into(), + content_length: 20_481, + sha256: "4e5f".into(), + } + ); }