chore: 初始化TikTok短剧后端项目基础架构
创建了完整的NestJS项目结构,包含配置文件、核心模块、工具类、拦截器、过滤器等基础代码,实现了健康检查、用户认证、管理员RBAC、剧集管理、订单支付、媒体管理等核心功能的基础框架,配置了ESLint、TypeScript、Jest等开发工具链。
This commit is contained in:
131
test/orders-playback.e2e-spec.ts
Normal file
131
test/orders-playback.e2e-spec.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { bootstrapApp } from '../src/bootstrap';
|
||||
import { loginAdmin } from './test-helpers';
|
||||
|
||||
describe('Auth, orders, and playback', () => {
|
||||
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('logs in with TikTok identity, creates an order, and unlocks paid playback through an idempotent payment webhook', async () => {
|
||||
const login = await request(app.getHttpServer())
|
||||
.post('/api/app/v1/auth/tiktok-login')
|
||||
.send({
|
||||
code: 'test-code',
|
||||
mockOpenId: 'open-user-1',
|
||||
nickname: 'Tester',
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
expect(login.body.data).toMatchObject({
|
||||
accessToken: expect.any(String),
|
||||
user: {
|
||||
id: expect.any(Number),
|
||||
tiktokOpenId: 'open-user-1',
|
||||
nickname: 'Tester',
|
||||
},
|
||||
isNewUser: true,
|
||||
});
|
||||
|
||||
const token = login.body.data.accessToken;
|
||||
const drama = await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/dramas')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
title: 'Paid Drama',
|
||||
coverUrl: 'https://cdn.example.com/paid.jpg',
|
||||
type: 'paid',
|
||||
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: 'Paid Episode',
|
||||
coverUrl: 'https://cdn.example.com/paid-ep.jpg',
|
||||
videoUrl: 'https://cdn.example.com/paid-ep.mp4',
|
||||
durationSeconds: 300,
|
||||
isPaid: true,
|
||||
unlockPrice: 199,
|
||||
status: 'published',
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get(`/api/app/v1/episodes/${episode.body.data.id}/play`)
|
||||
.expect(401);
|
||||
|
||||
const lockedPlay = await request(app.getHttpServer())
|
||||
.get(`/api/app/v1/episodes/${episode.body.data.id}/play`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.expect(200);
|
||||
|
||||
expect(lockedPlay.body.data).toMatchObject({
|
||||
episodeId: episode.body.data.id,
|
||||
isLocked: true,
|
||||
videoUrl: null,
|
||||
});
|
||||
|
||||
const order = await request(app.getHttpServer())
|
||||
.post('/api/app/v1/orders')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({ episodeId: episode.body.data.id })
|
||||
.expect(201);
|
||||
|
||||
expect(order.body.data).toMatchObject({
|
||||
orderNo: expect.any(String),
|
||||
status: 'pending',
|
||||
amount: 199,
|
||||
});
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post('/api/webhooks/tiktok/payments')
|
||||
.send({
|
||||
orderNo: order.body.data.orderNo,
|
||||
providerTransactionId: 'txn-1',
|
||||
status: 'paid',
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post('/api/webhooks/tiktok/payments')
|
||||
.send({
|
||||
orderNo: order.body.data.orderNo,
|
||||
providerTransactionId: 'txn-1',
|
||||
status: 'paid',
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
const unlockedPlay = await request(app.getHttpServer())
|
||||
.get(`/api/app/v1/episodes/${episode.body.data.id}/play`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.expect(200);
|
||||
|
||||
expect(unlockedPlay.body.data).toMatchObject({
|
||||
episodeId: episode.body.data.id,
|
||||
isLocked: false,
|
||||
videoUrl: 'https://cdn.example.com/paid-ep.mp4',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user