oh-my-muse/muse-studio/e2e/market-publish.spec.ts
lili 0b6b1e8887 feat(studio+market): 市场生产侧发布草稿活体纵切 + 修 publish 部分索引 ON CONFLICT 潜伏 bug
studio 生产侧首切片(创作者飞轮起点):新增 /market/publish 发布表单(MarketPublish + useMarketPublish hook),
保存发布草稿经唯一合法入口 POST /marketplace/publish-drafts → my-publish-records 真实回读;
MarketBrowse 加「我要发布资产」入口。market-publish.spec.ts(chromium, MSW off, 真连 48080)正+负绿,
DB 证 muse_market_publish_draft 落库(status=draft);命令幂等键按材料 hash 派生→自包含可重复跑、无需预种。

附带修真实潜伏后端 bug(链路从未活体跑过):publish draft/check/request 的 (tenant_id,command_id)
被 V15 建为部分唯一索引(WHERE command_id IS NOT NULL),而各 mapper insertIgnore 发无谓词
ON CONFLICT (tenant_id,command_id)→PostgreSQL 无法用部分索引作仲裁器→savePublishDraft 500
(BadSqlGrammarException: no unique or exclusion constraint matching the ON CONFLICT specification)。
修=部分→完整唯一索引,对齐 muse_market_command/purchase 既有约定(V22 迁移 + muse_slice_live 已应用,
零 Java 改动/零重启)。遗留:handoff/appeal 等 market 表同类部分索引待对应流程活体时收口。

验证:整套 e2e 10/10 绿、vitest 47/47 无回归、tsc 绿。进度总账 §五C/§四/per-BC 已就地更新。

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

65 lines
3.0 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
});
});