3D 图生输入解析只收敛「引用不可用」状态码

- resolve_source_object_key_with 的 4xx 收敛从 is_client_error() 收窄为 400 / 404,不再把 403 服务身份校验失败与 409 并发冲突也说成「图片必须是已登记资源」
- 与模块注释「只有基础设施失败保留原状态码」的实际口径统一,403 / 409 / 5xx 一律按原状态码上报
- 定向用例改名 image_source_resolution_only_collapses_unavailable_references,覆盖 400 / 404 收敛与 403 / 409 / 500 保留
This commit is contained in:
2026-09-23 14:51:09 +08:00
parent 8a8c00fbb6
commit 1be77d2f99
@@ -118,12 +118,12 @@ where
Model3dGenerationSource::Asset { asset_id } => ("asset", asset_id.as_str()),
};
let resolved = resolve(reference_id.to_string()).await.map_err(|error| {
// 未登记、跨 owner 与已删除在 provider 侧都是同一类“引用不可用”,
// 这里也收敛成同一句 400;只有基础设施故障才继续按原状态码上报。
if error.status_code().is_client_error() {
image_source_unavailable()
} else {
error
// 只有「未登记(404)/ 引用非法(400)」这一类才是“引用不可用”,收敛成同一句 400;
// 403(服务身份校验失败)、409(版本冲突)与 5xx 都是基础设施 / 并发失败,
// 把它们也说成「图片必须是已登记资源」会指错方向,因此继续按原状态码上报。
match error.status_code() {
StatusCode::BAD_REQUEST | StatusCode::NOT_FOUND => image_source_unavailable(),
_ => error,
}
})?;
if resolved.kind != requested_kind {
@@ -251,31 +251,41 @@ mod tests {
);
}
/// 未登记 / 跨 owner / 已删除这类 4xx 收敛成同一句 400;基础设施故障保留原状态码。
/// 未登记(404)与引用非法(400)收敛成同一句 400;403 / 409 与 5xx 一律保留原状态码。
#[tokio::test]
async fn image_source_resolution_collapses_client_errors_but_keeps_infra_errors() {
async fn image_source_resolution_only_collapses_unavailable_references() {
let source = Model3dGenerationSource::Resource {
resource_id: "res-1".to_string(),
};
let collapsed = resolve_source_object_key_with(&source, |_| async {
Err(probe_error(StatusCode::NOT_FOUND))
})
.await
.expect_err("4xx 必须收敛");
assert_eq!(collapsed.status_code(), StatusCode::BAD_REQUEST);
assert_eq!(
reason_of(&collapsed),
Some("model3d-image-source-unavailable")
);
for unavailable in [StatusCode::BAD_REQUEST, StatusCode::NOT_FOUND] {
let collapsed = resolve_source_object_key_with(&source, |_| async move {
Err(probe_error(unavailable))
})
.await
.expect_err("引用不可用必须收敛");
assert_eq!(collapsed.status_code(), StatusCode::BAD_REQUEST);
assert_eq!(
reason_of(&collapsed),
Some("model3d-image-source-unavailable")
);
}
let kept = resolve_source_object_key_with(&source, |_| async {
Err(probe_error(StatusCode::INTERNAL_SERVER_ERROR))
})
.await
.expect_err("基础设施故障必须继续报错");
assert_eq!(kept.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(reason_of(&kept), Some("probe"));
// 403 来自 spacetime runtime 的服务身份校验、409 来自并发写入:都不是用户的引用写错了,
// 收敛成 400 会把基础设施故障说成「图片必须是已登记资源」。
for kept_status in [
StatusCode::FORBIDDEN,
StatusCode::CONFLICT,
StatusCode::INTERNAL_SERVER_ERROR,
] {
let kept = resolve_source_object_key_with(&source, |_| async move {
Err(probe_error(kept_status))
})
.await
.expect_err("基础设施 / 并发故障必须继续报错");
assert_eq!(kept.status_code(), kept_status);
assert_eq!(reason_of(&kept), Some("probe"));
}
}
/// 本模块只做定点解析:一旦退回「列工程 / 列素材库再筛」的老路,