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', }); }); });