oh-my-muse/muse-studio/e2e/market-publish.spec.ts
lili 288fce955f feat(studio+market): 市场发布提交审核全链(草稿→检查→提交)活体绿 + V22 补 review_event 索引
承上一提交(草稿切片),完成创作者发布全链到「提交审核」:
- MarketPublish 加 licenseType / 权利声明字段 + 「提交审核」按钮(save→运行检查→检查通过则提交申请);
  useMarketPublish 加 useRunPublishCheck / useSubmitPublishRequest。
- market-publish.spec.ts 3/3 绿(草稿正 / 缺标题负 / 提交全链),DB 证 muse_market_publish_request=submitted
  + muse_market_review_event=submitted 落库;全链测用唯一名→可重复跑。
  整套 e2e 11/11、vitest 47/47 无回归、tsc 绿。

修复:submitPublishRequest 的 writeSubmittedReviewEvent 命中 muse_market_review_event 同款部分索引
ON CONFLICT 不匹配 bug(submit 500);V22 迁移 + muse_slice_live 增补 review_event 索引(部分→完整,共 4 表)。
遗留:handoff/appeal/authorization_summary 等 market 表同类部分索引待对应流程活体时收口。

进度总账 §五C 已就地更新(市场生产侧:发布草稿 + 提交审核全链 )。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 02:10:12 -07:00

93 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { expect, test, type Page } from '@playwright/test';
/**
* 市场生产侧发布草稿 e2e真实后端MSW off
*
* 前置vite 以 VITE_API_MOCK=false 运行(关 MSW、真实 muse-server 在 48080。
* 目的rendered studio 生产侧「发布到市场」表单 → 经唯一合法入口 POST /marketplace/publish-drafts
* 把资产写为 Shadow 发布草稿,并经「我的发布记录」(GET /marketplace/my-publish-records) 真实回读渲染(反假绿)。
* 自包含savePublishDraft 按材料 hash 派生幂等 commandId故同输入可重复跑命中回放、不重复建无需预置 fixture。
*/
const TOKEN = 'test1';
// 稳定标题 → 后端材料 hash 幂等键稳定 → 重复跑命中命令回放、不产生重复草稿。
const DRAFT_NAME = '活体发布草稿·e2e';
async function seedToken(page: Page): Promise<void> {
await page.addInitScript((t) => {
window.localStorage.setItem('accessToken', t);
window.localStorage.setItem('tenantId', '1');
}, TOKEN);
}
test.describe('市场生产侧发布草稿(真实后端, MSW off)', () => {
test('保存发布草稿→真打 publish-drafts→「我的发布记录」出现该资产', async ({ page }) => {
await seedToken(page);
await page.goto('/market/publish');
await page.getByPlaceholder(/在此处输入市场展示标题/).fill(DRAFT_NAME);
const [resp] = await Promise.all([
page.waitForResponse(
(r) => /\/marketplace\/publish-drafts$/.test(r.url()) && r.request().method() === 'POST',
{ timeout: 15_000 }
),
page.getByRole('button', { name: /保存发布草稿/ }).click(),
]);
// Yudao 口径:成功为 HTTP 200 + CommonResult{code:0};草稿落库返回 draftId + status=draft。
expect(resp.status()).toBe(200);
const body = await resp.json();
expect(body.code).toBe(0);
expect(typeof body.data?.draftId).toBe('number');
expect(body.data?.status).toBe('draft');
// 「我的发布记录」刷新后出现该草稿(证后端 my-publish-records 把草稿作为记录真实回读渲染)。
await expect(page.getByText(DRAFT_NAME).first()).toBeVisible({ timeout: 15_000 });
});
test('缺市场标题被后端拒绝(@NotBlank, 非成功业务码)', async ({ page }) => {
await seedToken(page);
// 用 page.request 直接打接口绕 UI缺 name → 后端 @NotBlank 校验失败。
// WHYpage.request 绕开 client.ts需手动带 X-API-Version / Authorization / tenant-id。
const resp = await page.request.post('/app-api/muse/marketplace/publish-drafts', {
headers: {
'X-API-Version': '1',
Authorization: `Bearer ${TOKEN}`,
'tenant-id': '1',
'Content-Type': 'application/json',
},
data: { assetType: 'agent', sourceId: 1 }, // 故意缺 name
});
const body = await resp.json();
expect(body.code).not.toBe(0); // @NotBlank → 业务错误码Yudao 包成 200+code≠0 或 400
});
test('提交审核全链(保存草稿→运行检查→提交申请)→申请 status=submitted', async ({ page }) => {
await seedToken(page);
await page.goto('/market/publish');
// 唯一标题:全链会 consumePassedCheck消费检查用唯一名每次产新 draft/check/request → 可重复跑(不被回放卡死)。
const uniqueName = `活体上架·e2e-${Date.now()}`;
await page.getByPlaceholder(/在此处输入市场展示标题/).fill(uniqueName);
// licenseType + 权利声明 已有默认值(发布检查硬门槛),无需额外填写即可通过检查。
// 点「提交审核」触发 save→check→submit 三段链;等待最后一段 publish-requests 的真实写入响应。
const [submitResp] = await Promise.all([
page.waitForResponse(
(r) => /\/marketplace\/publish-requests$/.test(r.url()) && r.request().method() === 'POST',
{ timeout: 20_000 }
),
page.getByRole('button', { name: /提交审核/ }).click(),
]);
expect(submitResp.status()).toBe(200);
const submitBody = await submitResp.json();
expect(submitBody.code).toBe(0);
expect(submitBody.data?.status).toBe('submitted');
expect(String(submitBody.data?.requestId ?? '')).not.toBe('');
// 提交后该资产以 submitted 态进入「我的发布记录」(证 my-publish-records 真实回读已提交申请)。
await expect(page.getByText(uniqueName).first()).toBeVisible({ timeout: 15_000 });
});
});