a5e5ba4426
Deploy / Build Production Runtime Images (push) Has been skipped
Deploy / Deploy Production (push) Has been skipped
Deploy / Production Browser E2E (push) Has been skipped
Deploy / Deploy Staging (push) Failing after 142h46m12s
CI/CD Pipeline / Frontend Lint (push) Failing after 142h46m42s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 142h46m50s
- 认证流程(注册/登录/登出/获取用户信息,正向+反向共 10 个用例) - 工作空间流程(创建/列出/详情/成员,共 6 个用例) - 项目流程(创建/列出/详情/未授权,共 6 个用例) - 素材库流程(创建库/列出库/创建素材/列出素材,共 8 个用例) 共 30 个回归测试用例,覆盖视频生成 SaaS 核心业务路径。
242 lines
8.1 KiB
TypeScript
Executable File
242 lines
8.1 KiB
TypeScript
Executable File
/**
|
||
* 素材库流程 E2E 测试
|
||
*
|
||
* 覆盖:创建素材库、列出素材库、创建素材记录
|
||
* 每个测试独立,先注册登录获取 auth token。
|
||
*/
|
||
import { expect, test } from '@playwright/test';
|
||
|
||
const PASSWORD = 'Test123456!';
|
||
const apiBase = process.env.E2E_API_BASE || '/api/v1';
|
||
|
||
function uniqueEmail(prefix: string): string {
|
||
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}@example.com`;
|
||
}
|
||
|
||
function uniqueUsername(prefix: string): string {
|
||
return `${prefix}_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
|
||
}
|
||
|
||
/** 注册并登录,返回 { headers, email, username, userId } */
|
||
async function createAuthedUser(request: any, label: string) {
|
||
const email = uniqueEmail(label);
|
||
const username = uniqueUsername(label);
|
||
|
||
const reg = await request.post(`${apiBase}/auth/register`, {
|
||
data: { email, password: PASSWORD, username, display_name: `E2E ${label}` },
|
||
});
|
||
expect(reg.ok(), `注册应成功: ${await reg.text()}`).toBeTruthy();
|
||
const regData = await reg.json();
|
||
|
||
const login = await request.post(`${apiBase}/auth/login`, {
|
||
data: { email, password: PASSWORD },
|
||
});
|
||
expect(login.ok(), `登录应成功: ${await login.text()}`).toBeTruthy();
|
||
const loginData = await login.json();
|
||
|
||
return {
|
||
headers: { Authorization: `Bearer ${loginData.access_token}` },
|
||
email,
|
||
username,
|
||
userId: regData.user_id,
|
||
};
|
||
}
|
||
|
||
/** 创建一个项目并返回 project id */
|
||
async function createProject(request: any, headers: Record<string, string>, suffix: string): Promise<string> {
|
||
const resp = await request.post(`${apiBase}/projects`, {
|
||
headers,
|
||
data: { name: `Asset Test Proj ${suffix}`, description: 'E2E asset test' },
|
||
});
|
||
expect(resp.ok(), `创建项目应成功: ${await resp.text()}`).toBeTruthy();
|
||
const data = await resp.json();
|
||
return data.id;
|
||
}
|
||
|
||
test.describe('素材库流程', () => {
|
||
test('创建素材库', async ({ request }) => {
|
||
const { headers } = await createAuthedUser(request, 'lib-create');
|
||
const projectId = await createProject(request, headers, Date.now().toString());
|
||
|
||
const response = await request.post(`${apiBase}/asset-libraries`, {
|
||
headers,
|
||
data: {
|
||
project_id: projectId,
|
||
name: `视频素材库 ${Date.now()}`,
|
||
kind: 'video',
|
||
},
|
||
});
|
||
|
||
expect(response.ok(), `创建素材库应返回 2xx,实际: ${response.status()} ${await response.text()}`).toBeTruthy();
|
||
|
||
const data = await response.json();
|
||
expect(data.id, '应返回素材库 ID').toBeTruthy();
|
||
expect(data.name).toContain('视频素材库');
|
||
expect(data.kind).toBe('video');
|
||
expect(data.project_id).toBe(projectId);
|
||
});
|
||
|
||
test('创建素材库 - 无效 kind 反向', async ({ request }) => {
|
||
const { headers } = await createAuthedUser(request, 'lib-badkind');
|
||
const projectId = await createProject(request, headers, Date.now().toString());
|
||
|
||
const response = await request.post(`${apiBase}/asset-libraries`, {
|
||
headers,
|
||
data: {
|
||
project_id: projectId,
|
||
name: 'Bad Kind Library',
|
||
kind: 'invalid_kind',
|
||
},
|
||
});
|
||
|
||
// kind 有 pattern 校验 ^(video|voice|image)$,应返回 422
|
||
expect([400, 422]).toContain(response.status());
|
||
});
|
||
|
||
test('创建素材库 - 不存在的项目反向', async ({ request }) => {
|
||
const { headers } = await createAuthedUser(request, 'lib-nopj');
|
||
|
||
const response = await request.post(`${apiBase}/asset-libraries`, {
|
||
headers,
|
||
data: {
|
||
project_id: 'nonexistent-project-999',
|
||
name: 'Orphan Library',
|
||
kind: 'video',
|
||
},
|
||
});
|
||
|
||
expect(response.status(), '不存在的项目应返回 404').toBe(404);
|
||
});
|
||
|
||
test('列出素材库', async ({ request }) => {
|
||
const { headers } = await createAuthedUser(request, 'lib-list');
|
||
const projectId = await createProject(request, headers, Date.now().toString());
|
||
|
||
// 创建 2 个不同类型的素材库
|
||
await request.post(`${apiBase}/asset-libraries`, {
|
||
headers,
|
||
data: { project_id: projectId, name: `Video Lib ${Date.now()}`, kind: 'video' },
|
||
});
|
||
await request.post(`${apiBase}/asset-libraries`, {
|
||
headers,
|
||
data: { project_id: projectId, name: `Image Lib ${Date.now()}`, kind: 'image' },
|
||
});
|
||
|
||
// 列出(按 project_id 过滤)
|
||
const response = await request.get(`${apiBase}/asset-libraries`, {
|
||
headers,
|
||
params: { project_id: projectId },
|
||
});
|
||
|
||
expect(response.ok(), `列出素材库应返回 2xx,实际: ${response.status()} ${await response.text()}`).toBeTruthy();
|
||
|
||
const data = await response.json();
|
||
const items = data.items || [];
|
||
expect(items.length, '应至少有 2 个素材库').toBeGreaterThanOrEqual(2);
|
||
|
||
const kinds = items.map((i: any) => i.kind);
|
||
expect(kinds).toContain('video');
|
||
expect(kinds).toContain('image');
|
||
});
|
||
|
||
test('创建素材记录', async ({ request }) => {
|
||
const { headers, userId } = await createAuthedUser(request, 'asset-create');
|
||
const projectId = await createProject(request, headers, Date.now().toString());
|
||
|
||
// 创建素材库
|
||
const lib = await request.post(`${apiBase}/asset-libraries`, {
|
||
headers,
|
||
data: { project_id: projectId, name: `Asset Lib ${Date.now()}`, kind: 'video' },
|
||
});
|
||
expect(lib.ok()).toBeTruthy();
|
||
const libData = await lib.json();
|
||
|
||
// 创建素材记录
|
||
const response = await request.post(`${apiBase}/assets`, {
|
||
headers,
|
||
data: {
|
||
project_id: projectId,
|
||
library_id: libData.id,
|
||
name: `test_video_${Date.now()}.mp4`,
|
||
storage_key: `uploads/e2e/test_${Date.now()}.mp4`,
|
||
mime_type: 'video/mp4',
|
||
metadata: { duration: 15.5, resolution: '1080p' },
|
||
file_size: 1024000,
|
||
status: 'ready',
|
||
uploaded_by_user_id: userId,
|
||
},
|
||
});
|
||
|
||
expect(response.ok(), `创建素材应返回 2xx,实际: ${response.status()} ${await response.text()}`).toBeTruthy();
|
||
|
||
const data = await response.json();
|
||
expect(data.id, '应返回素材 ID').toBeTruthy();
|
||
expect(data.name).toContain('test_video');
|
||
expect(data.mime_type).toBe('video/mp4');
|
||
expect(data.library_id).toBe(libData.id);
|
||
});
|
||
|
||
test('列出素材', async ({ request }) => {
|
||
const { headers, userId } = await createAuthedUser(request, 'asset-list');
|
||
const projectId = await createProject(request, headers, Date.now().toString());
|
||
|
||
// 创建素材库
|
||
const lib = await request.post(`${apiBase}/asset-libraries`, {
|
||
headers,
|
||
data: { project_id: projectId, name: `List Lib ${Date.now()}`, kind: 'video' },
|
||
});
|
||
expect(lib.ok(), `创建素材库应成功: ${await lib.text()}`).toBeTruthy();
|
||
const libData = await lib.json();
|
||
|
||
// 创建 2 个素材
|
||
await request.post(`${apiBase}/assets`, {
|
||
headers,
|
||
data: {
|
||
project_id: projectId,
|
||
library_id: libData.id,
|
||
name: `clip_a_${Date.now()}.mp4`,
|
||
storage_key: `uploads/e2e/clip_a.mp4`,
|
||
mime_type: 'video/mp4',
|
||
status: 'ready',
|
||
uploaded_by_user_id: userId,
|
||
},
|
||
});
|
||
await request.post(`${apiBase}/assets`, {
|
||
headers,
|
||
data: {
|
||
project_id: projectId,
|
||
library_id: libData.id,
|
||
name: `clip_b_${Date.now()}.mp4`,
|
||
storage_key: `uploads/e2e/clip_b.mp4`,
|
||
mime_type: 'video/mp4',
|
||
status: 'ready',
|
||
uploaded_by_user_id: userId,
|
||
},
|
||
});
|
||
|
||
// 列出素材
|
||
const response = await request.get(`${apiBase}/assets`, {
|
||
headers,
|
||
params: { library_id: libData.id },
|
||
});
|
||
|
||
expect(response.ok(), `列出素材应返回 2xx,实际: ${response.status()} ${await response.text()}`).toBeTruthy();
|
||
|
||
const data = await response.json();
|
||
const items = data.items || [];
|
||
expect(items.length, '应至少有 2 个素材').toBeGreaterThanOrEqual(2);
|
||
});
|
||
|
||
test('未登录创建素材库 - 反向', async ({ request }) => {
|
||
const response = await request.post(`${apiBase}/asset-libraries`, {
|
||
data: {
|
||
project_id: 'some-project',
|
||
name: 'Unauthorized Library',
|
||
kind: 'video',
|
||
},
|
||
});
|
||
|
||
expect([401, 403]).toContain(response.status());
|
||
});
|
||
});
|