test(studio): bindings 读回 live e2e spec(ready-but-unrun,镜像 graph spec)

muse-studio/e2e/knowledge-bindings.spec.ts:nav /knowledge/1→捕获真实 GET /works/1/knowledge-bindings→断 200/code:0/active 绑定 + 面板渲染(作品已绑定的知识来源/市场知识库/生效中)。playwright --list 确认编译有效可发现(1 test)。

尝试活体运行受阻(诚实,非假绿):远端 PG 宿主 100.64.0.8:5433 离线(不在 tailnet 节点列表)、本机无 PG 兜底,全栈 muse-server 起不来。PG 宿主恢复后按 .agents/knowledge §六 起栈(redis 本机可启、jar 需重建含 GET 端点、muse_slice_live seed work1 绑定)即可跑。后端 218 + studio 50/tsc/build 已绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-19 22:49:46 -07:00
parent 643826a3ea
commit 5a137f1861
2 changed files with 53 additions and 1 deletions

View File

@ -5,5 +5,5 @@
- **目标(owner 职责)**:Local KB / User KB / 全局知识处理 / Knowledge Draft / Knowledge Source Binding / 投影与索引状态。
- **边界(不可违反)**:**进 Local KB 唯一入口=用户显式确认草稿或手动修正**;全书解析章节确认只产/推 Draft,不写正式知识;来源优先级 Local KB > 用户绑定 > 市场/全局;依赖 RAGFlow GraphRAG(不可用时 Unavailable 优雅降级);守 BC 边界不碰他域 `.dal`([bc-boundaries](../../.agents/rules/bc-boundaries.md))。
- **out-of-scope**:正文写入(归 content)。
- **现状**:只读评估 72%;后端最完整模块之一(59 端点对齐契约),RAGFlow 真 HTTP + SSRF 防护;installed-KB 删除记录回显 bug 已修(2026-06-19:`MuseKnowledgeBindingMapperTest` + `MuseInstalledKnowledgeBaseServiceTest` 10/10,删除时写 `deleted=true` 且列表排除 `binding_status=deleted`);impact preview 已按 `muse_knowledge_source_binding_projection.owner_user_id` 输出 installed/source-binding owner 审计计数(2026-06-19:`MuseKnowledgeBaseServiceTest` + `MuseKnowledgeSourceBindingProjectionMapperTest` 30/30);source binding projection 回填闭环已修(2026-06-19:绑定确认为权威回填点写读模型行、unbind 按 bindingId 作用域撤销不误伤同源跨作品投影、唯一键补 work_id 修跨作品复用误判冲突〔V24〕;`MuseKnowledgeBindingServiceTest` + `MuseKnowledgeSourceBindingProjectionMapperTest` 11/11、整套件 214/214 绿);**读回端到端坐实 + unbind 真软删修复(2026-06-19)**:嵌入式 DB(H2)往返证 bind 写投影→`selectActiveByWorkId` 读回该来源、unbind 后读回消失、同源他作品投影存活;并修 unbind 软删真 bug——`deleted` 是 `@TableLogic`,实体 `setDeleted(true)` 被 MP 普通 update 剥离成空操作,改 `setSql("deleted = true")` 才真软删(`MuseKnowledgeSourceBindingProjectionRoundTripTest` 2/2、整套件 216/216 绿;新增 knowledge 模块首套嵌入式 DB 测试基建)。**GET bindings 读端点已补(2026-06-19)**:`AppMuseKnowledgeBindingController` 加 `GET /muse/works/{workId}/knowledge-bindings`→`listKnowledgeBindings`(requireWorkOwner 防 IDOR + selectActiveByWorkId 读回投影,id→String;happy-path + 越权 fail-closed 单测,整套件 218/218 绿);**bindings 读回后端腿(写→读→端点)已闭环,仅余 FE hook**。**FE 读回面板已加(2026-06-19)**:studio `useKnowledgeBindings` hook + `KnowledgeBindingsPanel`(镜像 DraftPanel)接入 `KnowledgePage`,契约+组件测试经 vitest 验证(全 studio 50/50、tsc 干净);live playwright e2e 需全栈 app(env 受限)未跑。bindings 写→读→端点→FE 读回展示链(除 live e2e)全通。
- **现状**:只读评估 72%;后端最完整模块之一(59 端点对齐契约),RAGFlow 真 HTTP + SSRF 防护;installed-KB 删除记录回显 bug 已修(2026-06-19:`MuseKnowledgeBindingMapperTest` + `MuseInstalledKnowledgeBaseServiceTest` 10/10,删除时写 `deleted=true` 且列表排除 `binding_status=deleted`);impact preview 已按 `muse_knowledge_source_binding_projection.owner_user_id` 输出 installed/source-binding owner 审计计数(2026-06-19:`MuseKnowledgeBaseServiceTest` + `MuseKnowledgeSourceBindingProjectionMapperTest` 30/30);source binding projection 回填闭环已修(2026-06-19:绑定确认为权威回填点写读模型行、unbind 按 bindingId 作用域撤销不误伤同源跨作品投影、唯一键补 work_id 修跨作品复用误判冲突〔V24〕;`MuseKnowledgeBindingServiceTest` + `MuseKnowledgeSourceBindingProjectionMapperTest` 11/11、整套件 214/214 绿);**读回端到端坐实 + unbind 真软删修复(2026-06-19)**:嵌入式 DB(H2)往返证 bind 写投影→`selectActiveByWorkId` 读回该来源、unbind 后读回消失、同源他作品投影存活;并修 unbind 软删真 bug——`deleted` 是 `@TableLogic`,实体 `setDeleted(true)` 被 MP 普通 update 剥离成空操作,改 `setSql("deleted = true")` 才真软删(`MuseKnowledgeSourceBindingProjectionRoundTripTest` 2/2、整套件 216/216 绿;新增 knowledge 模块首套嵌入式 DB 测试基建)。**GET bindings 读端点已补(2026-06-19)**:`AppMuseKnowledgeBindingController` 加 `GET /muse/works/{workId}/knowledge-bindings`→`listKnowledgeBindings`(requireWorkOwner 防 IDOR + selectActiveByWorkId 读回投影,id→String;happy-path + 越权 fail-closed 单测,整套件 218/218 绿);**bindings 读回后端腿(写→读→端点)已闭环,仅余 FE hook**。**FE 读回面板已加(2026-06-19)**:studio `useKnowledgeBindings` hook + `KnowledgeBindingsPanel`(镜像 DraftPanel)接入 `KnowledgePage`,契约+组件测试经 vitest 验证(全 studio 50/50、tsc 干净);live playwright e2e 需全栈 app(env 受限)未跑。bindings 写→读→端点→FE 读回展示链(除 live e2e)全通。**live e2e 已写 ready 未跑(2026-06-19)**:`muse-studio/e2e/knowledge-bindings.spec.ts`(镜像 graph spec,`playwright --list` 编译有效);活体受阻=远端 PG 宿主 100.64.0.8 离线(不在 tailnet)、无本地 PG 兜底,PG 宿主恢复后按 [.agents/knowledge §六] 起栈可跑。
- **关键风险 / TODO**:前端仅约 1/3 面(草稿确认/绑定/发布/图谱缺 hook);Task4 剩余 owner 缺口已收窄为 **document owner count / export_task_owner** 仍不能伪造,需后续 owner 事实或外部服务接入;recheck/worker 默认未生效。

View File

@ -0,0 +1,52 @@
import { expect, test, type Page } from '@playwright/test';
/**
* 作品已绑定知识来源读回视图 e2e(来源绑定读模型,真实后端,MSW off)。
*
* 前置(同 knowledge-graph.spec.ts 的活体全栈口径):
* - vite 以 VITE_API_MOCK=false 运行、真实 muse-server 在 48080;
* - muse_slice_live 作品 1 下已种子一条<生效>的来源绑定读模型投影
* (muse_knowledge_source_binding_projection:work_id=1、deleted=false、status=active、
* source_type=market_kb、kb_id=4001;可由真实 bind(precheck→bind)产生,或等价 seed)。
* 目的:rendered studio 的「作品已绑定的知识来源」面板经 GET /works/1/knowledge-bindings
* 读真实投影,把当前生效的来源绑定在 UI 渲染——证"绑定确认 → 读模型回填 → 读回展示"链路活体可见(打真后端,非 mock)。
*
* ⚠️ 状态(2026-06-19):本 spec 已编写但<尚未>活体运行——远端 PG 宿主(100.64.0.8:5433)当时离线、
* 全栈 app 无法启动(见 docs/mvp/进度总账.md bindings 条目"诚实边界")。PG 宿主恢复后按上述前置起栈即可运行。
* 后端读端点 + FE hook/面板已在 vitest/单测/tsc/build 层验证(knowledge-server 218、studio 50 + build 绿)。
*/
const TOKEN = 'test1';
async function seedToken(page: Page): Promise<void> {
await page.addInitScript((t) => {
window.localStorage.setItem('accessToken', t);
window.localStorage.setItem('tenantId', '1');
}, TOKEN);
}
test('已绑定知识来源面板渲染真实投影读回(真实后端)', async ({ page }) => {
await seedToken(page);
// 捕获真实来源绑定读回响应(打真后端,非 mock)。
const [resp] = await Promise.all([
page.waitForResponse(
(r) => /\/works\/1\/knowledge-bindings(\?|$)/.test(r.url()) && r.request().method() === 'GET',
{ timeout: 15_000 }
),
page.goto('/knowledge/1'),
]);
// Yudao 口径:成功为 HTTP 200 + CommonResult{code:0}。
expect(resp.status()).toBe(200);
const body = await resp.json();
expect(body.code).toBe(0);
// 真实后端返回该作品当前生效(未删)的来源绑定。
const bindings = body.data?.bindings ?? [];
expect(bindings.length).toBeGreaterThan(0);
expect(bindings.some((b: { status: string }) => b.status === 'active')).toBe(true);
// UI 渲染:面板标题 + 来源类型徽标(market_kb→市场知识库)+ 生效状态徽标。
await expect(page.getByText('作品已绑定的知识来源').first()).toBeVisible({ timeout: 15_000 });
await expect(page.getByText('市场知识库').first()).toBeVisible({ timeout: 15_000 });
await expect(page.getByText('生效中').first()).toBeVisible({ timeout: 15_000 });
});