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

238 lines
7.2 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('Core drama platform modules', () => {
let app: INestApplication;
let adminToken: string;
let userToken: string;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
bootstrapApp(app);
await app.init();
adminToken = await loginAdmin(app);
const login = await request(app.getHttpServer())
.post('/api/app/v1/auth/tiktok-login')
.send({
code: 'core-modules-code',
mockOpenId: 'core-open-id',
nickname: 'Core User',
avatarUrl: 'https://cdn.example.com/avatar.png',
})
.expect(201);
userToken = login.body.data.accessToken;
});
afterAll(async () => {
await app.close();
});
it('creates albums and episodes with TikTok, media, review, publish, and pricing fields', async () => {
const cover = await request(app.getHttpServer())
.post('/api/admin/v1/media/covers')
.set('Authorization', `Bearer ${adminToken}`)
.send({
fileName: 'cover.jpg',
url: 'https://cdn.example.com/cover.jpg',
})
.expect(201);
const uploadJob = await request(app.getHttpServer())
.post('/api/admin/v1/media/upload-jobs')
.set('Authorization', `Bearer ${adminToken}`)
.send({
jobId: 'job-001',
byteplusVid: 'v02-byteplus-001',
status: 'completed',
rawResponse: { provider: 'byteplus' },
})
.expect(201);
expect(uploadJob.body.data).toMatchObject({
id: expect.any(Number),
jobId: 'job-001',
byteplusVid: 'v02-byteplus-001',
status: 'completed',
rawResponse: { provider: 'byteplus' },
});
const album = await request(app.getHttpServer())
.post('/api/admin/v1/dramas')
.set('Authorization', `Bearer ${adminToken}`)
.send({
tiktokAlbumId: 'tk-album-001',
title: 'Core Album',
description: 'Album for core schema.',
coverId: cover.body.data.id,
coverUrl: cover.body.data.url,
type: 'revenge',
status: 'draft',
onlineVersion: 1,
})
.expect(201);
expect(album.body.data).toMatchObject({
id: expect.any(Number),
tiktokAlbumId: 'tk-album-001',
title: 'Core Album',
coverId: cover.body.data.id,
coverUrl: 'https://cdn.example.com/cover.jpg',
status: 'draft',
onlineVersion: 1,
});
const episode = await request(app.getHttpServer())
.post('/api/admin/v1/episodes')
.set('Authorization', `Bearer ${adminToken}`)
.send({
albumId: album.body.data.id,
tiktokEpisodeId: 'tk-episode-001',
seq: 1,
title: 'Core Episode 1',
byteplusVid: 'v02-byteplus-001',
coverId: cover.body.data.id,
coverUrl: cover.body.data.url,
videoUrl: 'https://cdn.example.com/episode.mp4',
freeType: 'paid',
price: 299,
reviewStatus: 'pending',
publishStatus: 'draft',
durationSeconds: 300,
})
.expect(201);
expect(episode.body.data).toMatchObject({
id: expect.any(Number),
albumId: album.body.data.id,
tiktokEpisodeId: 'tk-episode-001',
seq: 1,
byteplusVid: 'v02-byteplus-001',
coverId: cover.body.data.id,
freeType: 'paid',
price: 299,
reviewStatus: 'pending',
publishStatus: 'draft',
});
});
it('supports review submission, publish status changes, player auth, trade orders, webhook unlocks, and unlock records', async () => {
const album = await request(app.getHttpServer())
.post('/api/admin/v1/dramas')
.set('Authorization', `Bearer ${adminToken}`)
.send({
title: 'Player Album',
coverUrl: 'https://cdn.example.com/player-cover.jpg',
type: 'player',
status: 'published',
onlineVersion: 1,
})
.expect(201);
const episode = await request(app.getHttpServer())
.post('/api/admin/v1/episodes')
.set('Authorization', `Bearer ${adminToken}`)
.send({
albumId: album.body.data.id,
seq: 1,
title: 'Paid Player Episode',
byteplusVid: 'v02-player-001',
coverUrl: 'https://cdn.example.com/player-episode.jpg',
videoUrl: 'https://cdn.example.com/player-episode.mp4',
freeType: 'paid',
price: 399,
reviewStatus: 'approved',
publishStatus: 'published',
durationSeconds: 300,
})
.expect(201);
await request(app.getHttpServer())
.post(`/api/admin/v1/dramas/${album.body.data.id}/submit-review`)
.set('Authorization', `Bearer ${adminToken}`)
.expect(201);
await request(app.getHttpServer())
.patch(`/api/admin/v1/episodes/${episode.body.data.id}/publish-status`)
.set('Authorization', `Bearer ${adminToken}`)
.send({ publishStatus: 'published' })
.expect(200);
const lockedPlayer = await request(app.getHttpServer())
.get(`/api/app/v1/player/episodes/${episode.body.data.id}`)
.set('Authorization', `Bearer ${userToken}`)
.expect(200);
expect(lockedPlayer.body.data).toMatchObject({
albumId: album.body.data.id,
episodeId: episode.body.data.id,
vid: 'v02-player-001',
hasPermission: false,
playAuthToken: null,
});
const order = await request(app.getHttpServer())
.post('/api/app/v1/orders')
.set('Authorization', `Bearer ${userToken}`)
.send({ episodeId: episode.body.data.id })
.expect(201);
expect(order.body.data).toMatchObject({
id: expect.any(Number),
albumId: album.body.data.id,
episodeId: episode.body.data.id,
tradeOrderId: expect.stringMatching(/^TRADE/),
amount: 399,
status: 'pending',
});
await request(app.getHttpServer())
.post('/api/webhooks/tiktok/payments')
.send({
tradeOrderId: order.body.data.tradeOrderId,
tiktokOrderId: 'tk-order-001',
providerTransactionId: 'txn-core-001',
status: 'paid',
signature: 'dev-signature',
})
.expect(201);
const unlockedPlayer = await request(app.getHttpServer())
.get(`/api/app/v1/player/episodes/${episode.body.data.id}`)
.set('Authorization', `Bearer ${userToken}`)
.expect(200);
expect(unlockedPlayer.body.data).toMatchObject({
albumId: album.body.data.id,
episodeId: episode.body.data.id,
vid: 'v02-player-001',
hasPermission: true,
playAuthToken: expect.stringMatching(/^play_auth_/),
});
const unlocks = await request(app.getHttpServer())
.get('/api/admin/v1/unlock-records')
.set('Authorization', `Bearer ${adminToken}`)
.expect(200);
expect(unlocks.body.data.list).toEqual(
expect.arrayContaining([
expect.objectContaining({
userId: expect.any(Number),
episodeId: episode.body.data.id,
source: 'payment',
}),
]),
);
});
});