test(market): U-retrieve 检索打通(D0-fork 第三单元、RetrievalApiImpl 零改动)

D0-fork 第三单元:验证 installed_ref 物化后检索链自然打通、消除 no_dataset。RetrievalApiImpl 生产代码 0 改动(临时-04 判断属实):U-materialize 已在数据层闭合去污染链(binding.kb_id=本地 kbId、ragflow_binding 指公开副本)→第四门 selectActiveDatasetByKbId 返非空→no_dataset 分支不进→进 RAGFlow 检索;授权快照 knowledge/api 全程 String 透传无 parseLong(ADR-020、无 NumberFormatException);document_ids/metadata_filter 保持 null(命中纯公开副本、无需运行时隔离)。

加 2 单测(MuseKnowledgeRetrievalApiImplTest 15/15):no_dataset 消除+命中(对照基线无 dataset binding 时 no_dataset)、字符串授权快照透传。发布者私有不泄露(真 RAGFlow)+真 PG 端到端留 U-verify。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-26 11:33:43 -07:00
parent 30a693e1b7
commit d4309ab0e9

View File

@ -85,6 +85,15 @@ class MuseKnowledgeRetrievalApiImplTest extends BaseMockitoUnitTest {
return d;
}
/**
* installed_ref binding 范式D0-fork / U-materialize 写出形态kb_id 已去污染为本地 installed_ref kbId
* authorizationSnapshotId 承载字符串 envelopeADR-020 V14 VARCHARbindingScope search 检索用途
* 对应 U-materialize binding.setKbId(本地kbId)/binding.setAuthorizationSnapshotId(字符串)
*/
private MuseKnowledgeBindingDO installedRefBinding(Long localKbId, String authSnapshotEnvelope) {
return binding(localKbId, "search,generate", authSnapshotEnvelope);
}
private RuntimeResult retrievalSuccess(String responseJson) {
Map<String, Object> summary = new LinkedHashMap<>();
summary.put("responseBody", JsonUtils.parseTree(responseJson));
@ -286,4 +295,95 @@ class MuseKnowledgeRetrievalApiImplTest extends BaseMockitoUnitTest {
assertNull(sent.ragflowDocumentIds(), "document_ids 为 null → RAGFlow 对该 dataset 整库返回,无文档级隔离");
assertNull(sent.metadataFilter(), "metadata_filter 为 null → 无元数据级 chunk 过滤");
}
// ========================================================================================
// U-retrieve临时-04 §U-retrieve :270-290 / D0-fork 第三单元检索打通
// 验证 RetrievalApiImpl 主路径无需改动 U-materialize binding.kb_id 去污染为本地 installed_ref
// kbId并建好指向公开副本 dataset ragflow_binding 第四门 selectActiveDatasetByKbId(本地kbId) 自然
// 返非空 no_dataset 静默省略消除 RAGFlow 检索公开副本授权快照沿用 ADR-020 VARCHAR 字符串 envelope
// 这是数据形态测试mock U-materialize 的写出形态不改任何检索/绑定主路径代码
// ========================================================================================
/**
* U-retrieve 核心no_dataset 消除 + 命中
* <p>输入U-materialize 物化后的 installed_refbinding.kb_id=本地 kbIdK2=9002已去污染 assetId
* 该本地 kbId active dataset binding 指向公开副本 D_pubwork 绑定含 search 用途 + 授权快照
* <p>预期结果<b></b> {@code empty("no_dataset")}进入 RAGFlow 检索第四门拿到副本 dataset
* {@code RetrievalResult.ok(chunks)}chunk 带齐 §5.3 合同字段
* <p>对照基线{@link #should_returnNoDataset_whenAuthorizedButNoActiveDataset()} 钉死了"无 dataset binding
* 时必返 no_dataset"——本 case 证明 U-materialize 建好指向副本的 dataset binding 后,同一检索链 no_dataset 消失。
*/
@Test
void uRetrieve_should_hitPublicForkDataset_eliminatingNoDataset_whenInstalledRefMaterialized() {
Long localInstalledRefKbId = 9002L; // U-materialize 新建的本地 installed_ref kbIdK2非市场 assetId
String publicForkDatasetId = "ds-public-fork-Dpub"; // U-materialize 指向的公开副本 dataset
// installed_ref bindingkb_id=本地 kbId去污染后不再是 assetId
when(bindingMapper.selectActiveByWorkId(4001L))
.thenReturn(List.of(installedRefBinding(localInstalledRefKbId, "auth-envelope-installed")));
when(projectionMapper.selectActiveByWorkId(4001L))
.thenReturn(List.of(projection(localInstalledRefKbId, "active")));
// 第四门本地 kbId 命中指向公开副本的 active dataset bindingU-materialize ensureForkDatasetBinding 写出
when(ragflowBindingMapper.selectActiveDatasetByKbId(localInstalledRefKbId))
.thenReturn(dataset(publicForkDatasetId));
// 公开副本整库返回的本就只有公开内容发布者私有物理不在副本内无需运行时隔离
String json = "{\"code\":0,\"data\":{\"chunks\":[{"
+ "\"content\":\"公开副本里的写作技巧\",\"document_id\":\"doc-pub-1\",\"id\":\"c-1\","
+ "\"kb_id\":\"" + publicForkDatasetId + "\",\"similarity\":0.9}],\"total\":1}}";
when(ragFlowClient.retrieveChunks(any(RetrieveChunksCommand.class))).thenReturn(retrievalSuccess(json));
RetrievalResult result = retrievalApi.retrieveForWork(req());
// no_dataset 消除命中后 omittedReason null对照基线 should_returnNoDataset omittedReason="no_dataset"
assertNull(result.omittedReason(), "no_dataset 应已消除U-materialize 建好指向副本的 dataset binding第四门命中");
// RAGFlow 检索且命中 ok
assertTrue(result.hasChunks(), "应进入 RAGFlow 检索并命中公开副本");
assertEquals("ok", result.status());
assertEquals(1, result.chunks().size());
// 检索发往 RAGFlow 的确是公开副本 dataset且授权来源归因到本地 installed_ref kbId
ArgumentCaptor<RetrieveChunksCommand> captor = ArgumentCaptor.forClass(RetrieveChunksCommand.class);
verify(ragFlowClient).retrieveChunks(captor.capture());
assertEquals(List.of(publicForkDatasetId), captor.getValue().ragflowDatasetIds(),
"检索命中的是公开副本 dataset纯公开内容无需运行时隔离");
MuseKnowledgeRetrievalApi.RetrievedChunk chunk = result.chunks().get(0);
assertEquals(localInstalledRefKbId, chunk.sourceKbId(), "chunk 归因到本地 installed_ref kbId去污染后");
assertEquals(publicForkDatasetId, chunk.datasetId());
assertEquals("公开副本里的写作技巧", chunk.contentSummary());
}
/**
* U-retrieve字符串授权快照ADR-020 V14 VARCHAR envelope
* <p>输入installed_ref binding authorization_snapshot_id <b>字符串 envelope 形态</b>
* 非纯数字 {@code "snap:installed-ref:v1:abc-123"}
* <p>预期§5.3 授权快照门正常放行hasText 判定 parseLongchunk 原样透传该字符串到
* {@code authorizationSnapshotId} 合同字段全程无 NumberFormatException该字段不落 null
* <p>这钉死 ADR-020 的核心约束检索门读 binding.getAuthorizationSnapshotId() 当字符串用
* 绝不回退 BIGINT/parseLong否则字符串 envelope NumberFormatException 或被丢成 null
*/
@Test
void uRetrieve_should_passThroughStringAuthorizationSnapshot_withoutNumberFormatException() {
Long localInstalledRefKbId = 9003L;
// 字符串 envelope 形态的授权快照绝非可 parseLong 的纯数字
String stringEnvelopeSnapshot = "snap:installed-ref:v1:abc-123";
when(bindingMapper.selectActiveByWorkId(4001L))
.thenReturn(List.of(installedRefBinding(localInstalledRefKbId, stringEnvelopeSnapshot)));
when(projectionMapper.selectActiveByWorkId(4001L))
.thenReturn(List.of(projection(localInstalledRefKbId, "active")));
when(ragflowBindingMapper.selectActiveDatasetByKbId(localInstalledRefKbId))
.thenReturn(dataset("ds-public-fork-Dpub"));
when(ragFlowClient.retrieveChunks(any(RetrieveChunksCommand.class)))
.thenReturn(retrievalSuccess("{\"code\":0,\"data\":{\"chunks\":[{"
+ "\"content\":\"片段\",\"document_id\":\"doc-1\",\"id\":\"c-1\","
+ "\"kb_id\":\"ds-public-fork-Dpub\",\"similarity\":0.7}]}}"));
// 全程不应抛 NumberFormatException若检索门误 parseLong 字符串 envelope 即会抛 catch 降级成 retrieval_error
RetrievalResult result = retrievalApi.retrieveForWork(req());
// 放行且命中未因字符串 envelope 被门拒也未异常降级
assertTrue(result.hasChunks(), "字符串授权快照应放行检索门并命中,不应被当作无授权/异常降级");
assertEquals("ok", result.status());
// chunk 原样透传字符串 envelope不落 null不被 parseLong 截断
MuseKnowledgeRetrievalApi.RetrievedChunk chunk = result.chunks().get(0);
assertEquals(stringEnvelopeSnapshot, chunk.authorizationSnapshotId(),
"授权快照字符串 envelope 原样透传到 chunk 合同字段,未 parseLong、未落 null");
}
}