From ca0e04c9a98d27a16813016b641366ffbfed40c7 Mon Sep 17 00:00:00 2001 From: lili Date: Thu, 25 Jun 2026 14:35:19 -0700 Subject: [PATCH] =?UTF-8?q?test(studio-e2e):=20source-attribution=20?= =?UTF-8?q?=E8=A7=A3=E8=80=A6=E5=88=B0=20pristine=20block3=20=E6=A0=B9?= =?UTF-8?q?=E6=B2=BB=E6=B5=8B=E8=AF=95=E9=97=B4=E6=9D=A5=E6=BA=90=E6=B1=A1?= =?UTF-8?q?=E6=9F=93=20flaky?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit source-attribution 只读 spec 期望 block1 来源=user_original,但全量串行时前序 accept-suggestion(写 ai_suggestion)/block-autosave/block-structure 按字母序先跑且都写 block1,block1 顶层 revision 来源在 user_original/ai_suggestion 漂移→偶发渲染"AI 生成"红;global-setup 每轮仅跑一次、无法撤销跑中途污染。修:spec 改读 pristine work3/block3(grep 确认无人导航 /workspace/3、无人写 block3)+global-setup §3 每轮删净 block3 归因并重建唯一 user_original rev1 行(幂等)。断言未放宽仍真断言 user_original。免疫验证:强灌 block1 顶层 ai_suggestion(旧 spec 必红条件)新 spec 仍绿;独立连跑 source-attribution 3 次全绿。 Co-Authored-By: Claude Opus 4.8 (1M context) --- muse-studio/e2e/global-setup.ts | 18 ++++++++++++++++-- muse-studio/e2e/source-attribution.spec.ts | 14 +++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/muse-studio/e2e/global-setup.ts b/muse-studio/e2e/global-setup.ts index 59f80c7b..1f2be661 100644 --- a/muse-studio/e2e/global-setup.ts +++ b/muse-studio/e2e/global-setup.ts @@ -113,14 +113,28 @@ async function globalSetup(): Promise { await crudEnt('crudshiti-yi'); await crudEnt('crudshiti-bing'); - // 3) accept-suggestion:复位 block3→rev1 + 清来源归因;suggestion3 复 pending/active(spec 硬编码 work3/block3/suggestion3)。 + // 3) accept-suggestion + source-attribution 只读 fixture:复位 block3(work3)→rev1 + 重建来源归因为 user_original。 + // WHY(source-attribution 反污染,根治偶发红):source-attribution.spec 读「某 Block 当前 revision 的来源」并断言 + // 「用户原创」。它原指向 /workspace/1(=block1),但 block1 被 accept-suggestion(写 ai_suggestion 来源)、 + // block-autosave、block-structure 三个 spec 在前序消费(三者按字母序均先于 source-attribution 跑),其当前 revision + // 的 source_type 由「最后一个写 block1 的 spec」决定 → 非确定(实测 block1 顶层 revision 在 user_original/ai_suggestion + // 间漂移,顶层落 ai_suggestion 时 source-attribution 渲染「AI 生成」而非「用户原创」→ 偶发红)。globalSetup 每轮只跑 + // 一次(在所有 spec 之前),无法撤销 spec 跑中途的污染,故根治法是让 source-attribution 改读「无任何 spec 会写」的 + // pristine Block:work3/block3(grep 全 spec 确认无人导航 /workspace/3、无人写 block3;此处每轮把 block3 复位 rev1 + + // 重建唯一一条 user_original 来源,使该读断言恒确定真)。block3 的 user_original 是真实初态(用户原创正文),断言未放宽。 await client.query('DELETE FROM muse_content_block_source_attribution WHERE tenant_id=1 AND block_id=3'); const blk = await client.query( "UPDATE muse_content_block SET revision=1, content_text='活体原始正文待采纳', word_count=9 WHERE tenant_id=1 AND id=3" ); + // 重建 block3 rev1 的 user_original 来源(source-attribution.spec 读此并断言「用户原创」);幂等:上面已先删 block3 全部归因。 + await client.query( + `INSERT INTO muse_content_block_source_attribution + (work_id, block_id, revision, source_type, source_object_id, source_status, tenant_id) + VALUES (3,3,1,'user_original','3','active',1)` + ); await client.query("UPDATE muse_ai_suggestion SET status='pending', source_status='active' WHERE tenant_id=1 AND id=3"); if (blk.rowCount === 0) { - console.warn('[e2e globalSetup] ⚠️ 未找到 block id=3(accept-suggestion 基础种子缺失,需先建基础数据)'); + console.warn('[e2e globalSetup] ⚠️ 未找到 block id=3(source-attribution 只读 fixture 需基础种子,需先建基础数据)'); } // 4) account-security-ack:把"待确认演练"事件复位为未确认 + 清其确认记录(已确认态由 ack 记录派生)。 diff --git a/muse-studio/e2e/source-attribution.spec.ts b/muse-studio/e2e/source-attribution.spec.ts index 8b1848b1..263e3fc9 100644 --- a/muse-studio/e2e/source-attribution.spec.ts +++ b/muse-studio/e2e/source-attribution.spec.ts @@ -3,10 +3,18 @@ import { expect, test, type Page } from '@playwright/test'; /** * Block 来源归因查看端到端(真实后端,MSW off)。 * - * 覆盖 E 链路:进 work1 workspace → 右侧「来源」Tab → 真实 GET source-attribution → - * 展示来源(work1 block 真后端含 user_original → 渲染「用户原创」)。纯读,无副作用,幂等。 + * 覆盖 E 链路:进 work3 workspace → 右侧「来源」Tab → 真实 GET source-attribution → + * 展示来源(work3/block3 真后端含 user_original → 渲染「用户原创」)。纯读,无副作用,幂等。 + * + * WHY 用 work3 而非 work1(反污染、根治偶发红):本读断言「block 当前 revision 的来源=用户原创」。若读 work1/block1, + * 该 block 被 accept-suggestion(写 ai_suggestion 来源)、block-autosave、block-structure 三个 spec 在前序消费(字母序均先 + * 于本 spec),其顶层 revision 的 source_type 由「最后写 block1 的 spec」决定 → 非确定,顶层落 ai_suggestion 时本断言渲染 + * 「AI 生成」偶发红。改读 work3/block3:无任何 spec 导航 /workspace/3 或写 block3(grep 全 spec 确认),globalSetup §3 每轮 + * 把 block3 复位 rev1 + 重建唯一 user_original 来源,使本读恒确定真(断言未放宽,仍真断言「用户原创」)。 */ const TOKEN = 'test1'; +/** 读取来源归因的目标作品:work3/block3 是无任何 spec 写入的 pristine Block(见上文 WHY),globalSetup §3 每轮复位其来源。 */ +const WORK_ID = 3; async function seedToken(page: Page): Promise { await page.addInitScript((t) => { @@ -18,7 +26,7 @@ async function seedToken(page: Page): Promise { test('用户查看章节正文来源归因(真实后端)', async ({ page }) => { await seedToken(page); - await page.goto('/workspace/1'); + await page.goto(`/workspace/${WORK_ID}`); await expect(page.getByRole('heading', { name: /正在创作/ })).toBeVisible({ timeout: 15_000 }); // 等章节结构条出现(activeBlock 已加载,来源 Tab 才有目标 Block)。 await expect(page.getByText(/章节结构/)).toBeVisible({ timeout: 15_000 });