3D 图生输入解析的测试改为断言可观察行为
- 删掉 include_str! 嵌源码、按 token 断言「没出现列工程 / 列素材库」的守卫:它测的是实现文本,改名、换格式、调顺序都会误伤 - 定点查询用例改成覆盖 resource / asset 两个分支,断言每个分支只发起一次按 ID 的查询、且对象键原样来自记录 - 模块文档写明定点查询由注入签名收口(一个 ID → 一条记录),失败语义与测试口径一并写实
This commit is contained in:
@@ -2,12 +2,17 @@
|
||||
//!
|
||||
//! 请求只给站内的画布资源 ID 或账号素材 ID,分两步处理:
|
||||
//! 1. 提交时只做元数据预检:按 ID 定点确认归属与记录类型,不读字节、不调用 provider,
|
||||
//! 因此跨 owner、未登记与类型不符都在扣费与入队之前返回 400;
|
||||
//! 因此跨 owner、未登记、已删除与类型不符都在扣费与入队之前失败;
|
||||
//! 2. worker 执行时重新确认同一事实,再从私有 OSS 读出字节,最后上传 provider 换
|
||||
//! `file_token`。**不把带签名的临时地址交给第三方**,也不接受 URL / data URL。
|
||||
//!
|
||||
//! 预检只按主键定点查引用,不拉取当前用户的工程列表或素材库;跨 owner、已删除与不存在
|
||||
//! 收敛成同一句 400,避免把别的账号是否存在该 ID 变成可探测信息。
|
||||
//! 收敛成同一句 400,避免把别的账号是否存在该 ID 变成可探测信息。只有这类「引用不可用」
|
||||
//! 才是 400:403(服务身份校验失败)、409(版本冲突)与 5xx 都是基础设施 / 并发问题,
|
||||
//! 按原状态码上报;「记录已解析但缺对象键」是行本身不完整,按 502 上报。
|
||||
//!
|
||||
//! 定点查询是注入到 `resolve_source_object_key_with` 的参数,解析每分支只发一次;测试按可观察
|
||||
//! 行为断言(查询次数、查询 ID、带出的对象键、各分支状态码),不依赖源码文本。
|
||||
//!
|
||||
//! TODO(oss-direct-read): 「让 Tripo 侧直接读站内对象」这条替代路径(交签名 URL,或对端直读
|
||||
//! OSS)能省掉一次字节中转与一份上行带宽,但等于把私有对象和带时效的读取凭据交给第三方,
|
||||
@@ -189,23 +194,40 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析只发起一次按 ID 的定点查询:查询到的 ID 必须与请求里的引用 ID 一致。
|
||||
/// 两个 source 分支都只发起一次按 ID 的定点查询,并把查到的那条记录的对象键原样带出:
|
||||
/// 解析不需要「列工程 / 列素材库再筛」这类宽查询,注入的查询签名本身就是「一个 ID → 一条记录」。
|
||||
#[tokio::test]
|
||||
async fn image_source_resolution_queries_only_the_requested_reference_id() {
|
||||
let source = Model3dGenerationSource::Resource {
|
||||
resource_id: "res-1".to_string(),
|
||||
};
|
||||
let mut queried: Vec<String> = Vec::new();
|
||||
for (source, requested_id, kind, object_key) in [
|
||||
(
|
||||
Model3dGenerationSource::Resource {
|
||||
resource_id: "res-1".to_string(),
|
||||
},
|
||||
"res-1",
|
||||
"resource",
|
||||
"objects/res-1.png",
|
||||
),
|
||||
(
|
||||
Model3dGenerationSource::Asset {
|
||||
asset_id: "asset-9".to_string(),
|
||||
},
|
||||
"asset-9",
|
||||
"asset",
|
||||
"objects/asset-9.png",
|
||||
),
|
||||
] {
|
||||
let mut queried: Vec<String> = Vec::new();
|
||||
|
||||
let object_key = resolve_source_object_key_with(&source, |reference_id| {
|
||||
queried.push(reference_id);
|
||||
async { Ok(resolved("resource", Some("objects/res-1.png"))) }
|
||||
})
|
||||
.await
|
||||
.expect("同类型的已登记引用应解析出对象键");
|
||||
let resolved_key = resolve_source_object_key_with(&source, |reference_id| {
|
||||
queried.push(reference_id);
|
||||
async move { Ok(resolved(kind, Some(object_key))) }
|
||||
})
|
||||
.await
|
||||
.expect("同类型的已登记引用应解析出对象键");
|
||||
|
||||
assert_eq!(object_key, "objects/res-1.png");
|
||||
assert_eq!(queried, vec!["res-1".to_string()]);
|
||||
assert_eq!(resolved_key, object_key);
|
||||
assert_eq!(queried, vec![requested_id.to_string()]);
|
||||
}
|
||||
}
|
||||
|
||||
/// 记录类型与 `source` 分支不一致时报类型不符,而不是把对象键照用。
|
||||
@@ -287,26 +309,4 @@ mod tests {
|
||||
assert_eq!(reason_of(&kept), Some("probe"));
|
||||
}
|
||||
}
|
||||
|
||||
/// 本模块只做定点解析:一旦退回「列工程 / 列素材库再筛」的老路,
|
||||
/// 这两个 token 会出现在源码里,断言立刻失败。
|
||||
///
|
||||
/// 预检不读字节、不调用 provider 由类型保证(`preflight_image_source` 不接
|
||||
/// provider client,也不碰 OSS),解析规则本身由上面的注入式用例覆盖。
|
||||
#[test]
|
||||
fn image_source_module_never_lists_projects_or_asset_libraries() {
|
||||
let source = include_str!("image_source.rs");
|
||||
|
||||
assert!(
|
||||
source.contains("resolve_editor_reference_record_by_id_for_owner("),
|
||||
"定点解析必须复用既有的按 ID 窄查询"
|
||||
);
|
||||
// 用 concat! 拼出待检查的字面量,否则断言里的字面量会被 include_str! 自己命中。
|
||||
for forbidden in [
|
||||
concat!("list_editor_", "projects"),
|
||||
concat!("get_editor_", "asset_library"),
|
||||
] {
|
||||
assert!(!source.contains(forbidden), "本模块不得包含 {forbidden}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user