Files
cyh-tk-backend/test/data-media-flow.e2e-spec.ts
zhxiao1124 4eff4d877e refactor: 完成项目整体目录重构与路径别名配置
1.  重构项目文件结构,统一文件存放位置与命名规则
2.  配置tsconfig路径别名,替换原有相对路径导入
3.  迁移drama-status枚举到dramas.types统一管理
4.  新增全局配置文件与工具类,补充完整注释与文档
5.  修复所有导入路径与依赖引用问题
2026-07-02 19:17:56 +08:00

232 lines
7.1 KiB
TypeScript

import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import * as request from 'supertest';
import { AppModule } from '../src/app/app.module';
import { bootstrapApp } from '../src/bootstrap';
import { loginAdmin } from './test-helpers';
describe('Data management and media upload flow', () => {
let app: INestApplication;
let adminToken: string;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
bootstrapApp(app);
await app.init();
adminToken = await loginAdmin(app);
});
afterAll(async () => {
await app.close();
});
it('creates a drama, configures episode slots, completes a direct upload, and exposes approved published content to app APIs', async () => {
const drama = await request(app.getHttpServer())
.post('/api/admin/v1/dramas')
.set('Authorization', `Bearer ${adminToken}`)
.send({
title: 'Upload Flow Drama',
description: 'Drama created by admin before uploading episodes.',
coverUrl: 'https://cdn.example.com/upload-flow-cover.jpg',
type: 'romance',
region: 'TH',
language: 'th',
status: 'draft',
})
.expect(201);
const dramaId = drama.body.data.id as number;
const slots = await request(app.getHttpServer())
.post(`/api/admin/v1/dramas/${dramaId}/episodes/batch`)
.set('Authorization', `Bearer ${adminToken}`)
.send({ episodeCount: 3 })
.expect(201);
expect(slots.body.data.list).toHaveLength(3);
expect(slots.body.data.list[0]).toMatchObject({
dramaId,
episodeNo: 1,
title: '第1集',
publishStatus: 'draft',
reviewStatus: 'not_submitted',
});
const episodeId = slots.body.data.list[0].id as number;
const uploadTask = await request(app.getHttpServer())
.post('/api/admin/v1/media/upload-tasks')
.set('Authorization', `Bearer ${adminToken}`)
.send({
episodeId,
fileName: 'episode-1.mp4',
contentType: 'video/mp4',
fileSize: 1024,
})
.expect(201);
expect(uploadTask.body.data).toMatchObject({
jobId: expect.any(String),
uploadUrl: expect.stringContaining('mock-byteplus-upload'),
bucket: expect.any(String),
objectKey: expect.stringContaining('episode-1.mp4'),
status: 'pending',
});
const completed = await request(app.getHttpServer())
.patch(`/api/admin/v1/media/upload-tasks/${uploadTask.body.data.jobId}/complete`)
.set('Authorization', `Bearer ${adminToken}`)
.send({})
.expect(200);
expect(completed.body.data).toMatchObject({
jobId: uploadTask.body.data.jobId,
episodeId,
status: 'reviewing',
byteplusVid: expect.stringMatching(/^mock_vid_/),
width: 1080,
height: 1920,
durationSeconds: 180,
});
await request(app.getHttpServer())
.post(`/api/admin/v1/media/upload-tasks/${uploadTask.body.data.jobId}/review/retry`)
.set('Authorization', `Bearer ${adminToken}`)
.expect(201);
await request(app.getHttpServer())
.post('/api/admin/v1/media/review-status/sync')
.set('Authorization', `Bearer ${adminToken}`)
.expect(201);
await request(app.getHttpServer())
.patch(`/api/admin/v1/episodes/${episodeId}`)
.set('Authorization', `Bearer ${adminToken}`)
.send({
title: '正式第1集',
coverUrl: 'https://cdn.example.com/episode-1-cover.jpg',
freeType: 'free',
publishStatus: 'published',
})
.expect(200);
await request(app.getHttpServer())
.patch(`/api/admin/v1/dramas/${dramaId}`)
.set('Authorization', `Bearer ${adminToken}`)
.send({ status: 'published' })
.expect(200);
const appDetail = await request(app.getHttpServer())
.get(`/api/app/v1/dramas/${dramaId}`)
.expect(200);
expect(appDetail.body.data).toMatchObject({
id: dramaId,
title: 'Upload Flow Drama',
totalEpisodes: 3,
publishedEpisodes: 1,
});
const appEpisodes = await request(app.getHttpServer())
.get(`/api/app/v1/dramas/${dramaId}/episodes`)
.expect(200);
expect(appEpisodes.body.data.list).toEqual([
expect.objectContaining({
id: episodeId,
title: '正式第1集',
reviewStatus: 'approved',
publishStatus: 'published',
byteplusVid: completed.body.data.byteplusVid,
}),
]);
});
it('syncs TikTok mock analytics and returns overview plus drama and episode metrics', async () => {
const drama = await request(app.getHttpServer())
.post('/api/admin/v1/dramas')
.set('Authorization', `Bearer ${adminToken}`)
.send({
title: 'Analytics Drama',
coverUrl: 'https://cdn.example.com/analytics-cover.jpg',
type: 'action',
status: 'published',
})
.expect(201);
const episode = await request(app.getHttpServer())
.post('/api/admin/v1/episodes')
.set('Authorization', `Bearer ${adminToken}`)
.send({
dramaId: drama.body.data.id,
episodeNo: 1,
title: 'Analytics Episode',
coverUrl: 'https://cdn.example.com/analytics-episode.jpg',
videoUrl: 'https://cdn.example.com/analytics-episode.mp4',
byteplusVid: 'mock_vid_analytics',
durationSeconds: 180,
reviewStatus: 'approved',
publishStatus: 'published',
status: 'published',
})
.expect(201);
const sync = await request(app.getHttpServer())
.post('/api/admin/v1/data/sync')
.set('Authorization', `Bearer ${adminToken}`)
.send({ dramaId: drama.body.data.id })
.expect(201);
expect(sync.body.data).toMatchObject({
status: 'completed',
dramaCount: expect.any(Number),
episodeCount: expect.any(Number),
});
const overview = await request(app.getHttpServer())
.get('/api/admin/v1/data/overview')
.set('Authorization', `Bearer ${adminToken}`)
.expect(200);
expect(overview.body.data).toMatchObject({
totalPlays: expect.any(Number),
totalLikes: expect.any(Number),
totalShares: expect.any(Number),
totalComments: expect.any(Number),
});
const dramaMetrics = await request(app.getHttpServer())
.get('/api/admin/v1/data/dramas')
.set('Authorization', `Bearer ${adminToken}`)
.expect(200);
expect(dramaMetrics.body.data.list).toEqual(
expect.arrayContaining([
expect.objectContaining({
dramaId: drama.body.data.id,
title: 'Analytics Drama',
plays: expect.any(Number),
}),
]),
);
const episodeMetrics = await request(app.getHttpServer())
.get(`/api/admin/v1/data/episodes?dramaId=${drama.body.data.id}`)
.set('Authorization', `Bearer ${adminToken}`)
.expect(200);
expect(episodeMetrics.body.data.list).toEqual([
expect.objectContaining({
dramaId: drama.body.data.id,
episodeId: episode.body.data.id,
title: 'Analytics Episode',
plays: expect.any(Number),
}),
]);
});
});