oh-my-muse/muse-studio/e2e/market-handoff-precheck.spec.ts
zizi 6dcabf69fb feat(solo): 收口治理入口并裁剪 studio 表面
完成 2.0.0 S4 的外部 AI、admin、account 与 solo SQL 收口,并推进 S5 studio 市场/handoff/旧编辑器入口裁剪。S5 的前端单测、构建与 route-only e2e 已留证;MSW-off 创作主线 e2e 仍因本地后端活体启动口径待修正,已在总账标为未完成验收。
2026-07-08 11:45:26 +08:00

92 lines
3.5 KiB
TypeScript

import { expect, test, type Page } from '@playwright/test';
import { Client } from 'pg';
test.skip(true, '2.0.0 S5 单人版已移除市场、handoff 或多用户前端入口,本 spec 隔离保留待恢复');
/**
* 市场资产「绑定到作品授权预检」e2e(来源侧 handoff 预检,真实后端,MSW off)。
*
* 前置:vite VITE_API_MOCK=false、真实 muse-server 48080、muse_slice_live 有 listed knowledge_base marker 市场资产。
* 目的:资产详情页先获取授权(acquire)→ 对目标作品做 bind-precheck(targetOwner=knowledge/targetAction=bind),
* 读回来源侧授权摘要与就绪态(handoffReady),直打真后端(反假绿)。
* 注:完整交接 token 由知识域消费,本切片只验来源侧预检(Market 不消费 token)。
*/
const TOKEN = 'test1';
const KNOWLEDGE_HANDOFF_ASSET_NAME = '活体市场知识库·handoff e2e';
async function seedToken(page: Page): Promise<void> {
await page.addInitScript((t) => {
window.localStorage.setItem('accessToken', t);
window.localStorage.setItem('tenantId', '1');
}, TOKEN);
}
function createPgClient(): Client {
return new Client({
host: process.env.MUSE_POSTGRES_HOST,
port: Number(process.env.MUSE_POSTGRES_PORT ?? '5433'),
database: process.env.MUSE_POSTGRES_DATABASE ?? 'muse_slice_live',
user: process.env.MUSE_POSTGRES_USERNAME ?? 'root',
password: process.env.MUSE_POSTGRES_PASSWORD,
ssl: false,
connectionTimeoutMillis: 10_000,
});
}
async function readKnowledgeHandoffAssetId(): Promise<number> {
const client = createPgClient();
await client.connect();
try {
const result = await client.query(
`SELECT id
FROM muse_market_asset
WHERE tenant_id=1
AND name=$1
AND asset_type='knowledge_base'
AND listing_status='listed'
AND status='active'
AND deleted=false
ORDER BY id DESC
LIMIT 1`,
[KNOWLEDGE_HANDOFF_ASSET_NAME]
);
expect(result.rowCount, '应存在 knowledge_base handoff e2e 市场资产').toBe(1);
return Number(result.rows[0].id);
} finally {
await client.end();
}
}
test('已授权资产绑定到作品授权预检 → 来源侧就绪(真实后端)', async ({ page, request }) => {
const assetId = await readKnowledgeHandoffAssetId();
await seedToken(page);
// 前提:确保知识库资产已获取授权(幂等 best-effort;bind-precheck 需授权存在,否则返「Market 授权不存在」)。
await request
.post(`http://localhost:48080/app-api/muse/marketplace/assets/${assetId}/purchase`, {
headers: { Authorization: 'Bearer test1', 'tenant-id': '1', 'X-API-Version': '1' },
data: { commandId: `e2e-acq-${Date.now()}` },
})
.catch(() => undefined);
await page.goto(`/market/assets/${assetId}`);
// 输入目标作品 + 授权预检,捕获真实 bind-precheck 响应(打真后端,非 mock)。
await page.getByLabel('目标作品 ID').fill('1');
const [resp] = await Promise.all([
page.waitForResponse(
(r) => new RegExp(`/assets/${assetId}/bind-precheck$`).test(r.url()) && r.request().method() === 'POST',
{ timeout: 15_000 }
),
page.getByRole('button', { name: '授权预检' }).click(),
]);
// Yudao 口径:成功为 HTTP 200 + CommonResult{code:0}。
expect(resp.status()).toBe(200);
const body = await resp.json();
expect(body.code).toBe(0);
expect(body.data?.handoffReady).toBe(true);
// 回显:预检就绪态(可绑定)。
await expect(page.getByText('可绑定(就绪)')).toBeVisible({ timeout: 15_000 });
});