1. 新增频道模块,包含类型定义、实体、DTO、控制器和服务 2. 为短剧添加频道ID和频道名字段,支持关联频道 3. 新增短剧删除API和权限配置 4. 调整tsconfig和依赖配置,更新权限列表 5. 新增相关测试用例验证频道与短剧关联逻辑
716 lines
22 KiB
TypeScript
716 lines
22 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('Dramas and episodes', () => {
|
|
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('lists the default channel for admin selectors', async () => {
|
|
const response = await request(app.getHttpServer())
|
|
.get('/api/admin/v1/channels')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.expect(200);
|
|
|
|
expect(response.body.data.list).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
id: expect.any(Number),
|
|
name: '默认频道',
|
|
code: 'default',
|
|
isDefault: true,
|
|
status: 'active',
|
|
}),
|
|
]),
|
|
);
|
|
});
|
|
|
|
it('assigns dramas to the default channel when no channel is selected', async () => {
|
|
const dramaResponse = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Default Channel Drama',
|
|
coverUrl: 'https://cdn.example.com/default-channel.jpg',
|
|
type: 'romance',
|
|
status: 'draft',
|
|
})
|
|
.expect(201);
|
|
|
|
expect(dramaResponse.body.data).toMatchObject({
|
|
title: 'Default Channel Drama',
|
|
channelId: expect.any(Number),
|
|
channelName: '默认频道',
|
|
});
|
|
|
|
const detail = await request(app.getHttpServer())
|
|
.get(`/api/admin/v1/dramas/${dramaResponse.body.data.id}`)
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.expect(200);
|
|
|
|
expect(detail.body.data).toMatchObject({
|
|
id: dramaResponse.body.data.id,
|
|
channelId: dramaResponse.body.data.channelId,
|
|
channelName: '默认频道',
|
|
});
|
|
});
|
|
|
|
it('allows creating a drama without a type', async () => {
|
|
const response = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'No Type Drama',
|
|
coverUrl: 'https://cdn.example.com/no-type.jpg',
|
|
status: 'draft',
|
|
})
|
|
.expect(201);
|
|
|
|
expect(response.body.data).toMatchObject({
|
|
title: 'No Type Drama',
|
|
type: '',
|
|
channelName: '默认频道',
|
|
});
|
|
});
|
|
|
|
it('deletes a drama and its episodes from admin APIs', async () => {
|
|
const drama = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Delete Drama Test',
|
|
coverUrl: 'https://cdn.example.com/delete-drama.jpg',
|
|
type: 'action',
|
|
status: 'draft',
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/episodes')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
dramaId: drama.body.data.id,
|
|
episodeNo: 1,
|
|
title: 'Delete Episode',
|
|
coverUrl: 'https://cdn.example.com/delete-episode.jpg',
|
|
durationSeconds: 300,
|
|
status: 'draft',
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.delete(`/api/admin/v1/dramas/${drama.body.data.id}`)
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.expect(200);
|
|
|
|
await request(app.getHttpServer())
|
|
.get(`/api/admin/v1/dramas/${drama.body.data.id}`)
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.expect(404);
|
|
|
|
const list = await request(app.getHttpServer())
|
|
.get('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.query({ title: 'Delete Drama Test' })
|
|
.expect(200);
|
|
|
|
expect(list.body.data.list).toEqual([]);
|
|
});
|
|
|
|
it('supports assigning a drama to a selected channel and rejects missing channels', async () => {
|
|
const channelResponse = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/channels')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
name: '测试频道',
|
|
code: 'test-channel',
|
|
sortOrder: 10,
|
|
})
|
|
.expect(201);
|
|
|
|
const dramaResponse = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Selected Channel Drama',
|
|
coverUrl: 'https://cdn.example.com/selected-channel.jpg',
|
|
type: 'action',
|
|
status: 'draft',
|
|
channelId: channelResponse.body.data.id,
|
|
})
|
|
.expect(201);
|
|
|
|
expect(dramaResponse.body.data).toMatchObject({
|
|
title: 'Selected Channel Drama',
|
|
channelId: channelResponse.body.data.id,
|
|
channelName: '测试频道',
|
|
});
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Missing Channel Drama',
|
|
coverUrl: 'https://cdn.example.com/missing-channel.jpg',
|
|
type: 'action',
|
|
status: 'draft',
|
|
channelId: 999999,
|
|
})
|
|
.expect(404);
|
|
});
|
|
|
|
it('updates and deletes non-default channels but keeps the default channel protected', async () => {
|
|
const created = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/channels')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
name: '待编辑频道',
|
|
code: 'editable-channel',
|
|
sortOrder: 1,
|
|
})
|
|
.expect(201);
|
|
|
|
const updated = await request(app.getHttpServer())
|
|
.patch(`/api/admin/v1/channels/${created.body.data.id}`)
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
name: '已编辑频道',
|
|
sortOrder: 2,
|
|
})
|
|
.expect(200);
|
|
|
|
expect(updated.body.data).toMatchObject({
|
|
id: created.body.data.id,
|
|
name: '已编辑频道',
|
|
sortOrder: 2,
|
|
});
|
|
|
|
await request(app.getHttpServer())
|
|
.delete(`/api/admin/v1/channels/${created.body.data.id}`)
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.expect(200);
|
|
|
|
const channels = await request(app.getHttpServer())
|
|
.get('/api/admin/v1/channels')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.expect(200);
|
|
|
|
expect(channels.body.data.list).not.toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ code: 'editable-channel' }),
|
|
]),
|
|
);
|
|
|
|
const defaultChannel = channels.body.data.list.find(
|
|
(channel: { code: string }) => channel.code === 'default',
|
|
);
|
|
await request(app.getHttpServer())
|
|
.delete(`/api/admin/v1/channels/${defaultChannel.id}`)
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.expect(422);
|
|
});
|
|
|
|
it('allows admin APIs to create a drama and episodes with release metadata', async () => {
|
|
const dramaResponse = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'City Revenge',
|
|
description: 'A short drama series.',
|
|
coverUrl: 'https://cdn.example.com/cover.jpg',
|
|
portraitCoverUrl: 'https://cdn.example.com/portrait.jpg',
|
|
landscapeCoverUrl: 'https://cdn.example.com/landscape.jpg',
|
|
type: 'revenge',
|
|
tags: ['urban', 'revenge'],
|
|
region: 'US',
|
|
language: 'en',
|
|
year: 2026,
|
|
status: 'published',
|
|
sortOrder: 10,
|
|
isRecommended: true,
|
|
})
|
|
.expect(201);
|
|
|
|
expect(dramaResponse.body.data).toMatchObject({
|
|
id: expect.any(Number),
|
|
title: 'City Revenge',
|
|
status: 'published',
|
|
type: 'revenge',
|
|
});
|
|
|
|
const dramaId = dramaResponse.body.data.id;
|
|
const episodeResponse = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/episodes')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
dramaId,
|
|
episodeNo: 1,
|
|
title: 'Episode 1',
|
|
description: 'Opening episode.',
|
|
coverUrl: 'https://cdn.example.com/ep1.jpg',
|
|
videoUrl: 'https://cdn.example.com/ep1.mp4',
|
|
trialDurationSeconds: 60,
|
|
durationSeconds: 300,
|
|
isPaid: true,
|
|
unlockPrice: 199,
|
|
publishAt: '2026-07-01T00:00:00.000Z',
|
|
nextReleaseAt: '2026-07-02T00:00:00.000Z',
|
|
status: 'published',
|
|
})
|
|
.expect(201);
|
|
|
|
expect(episodeResponse.body.data).toMatchObject({
|
|
id: expect.any(Number),
|
|
dramaId,
|
|
episodeNo: 1,
|
|
nextReleaseAt: '2026-07-02T00:00:00.000Z',
|
|
});
|
|
});
|
|
|
|
it('rejects duplicate episode numbers under the same drama', async () => {
|
|
const drama = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Duplicate Episode Test',
|
|
coverUrl: 'https://cdn.example.com/cover.jpg',
|
|
type: 'romance',
|
|
status: 'draft',
|
|
})
|
|
.expect(201);
|
|
|
|
const payload = {
|
|
dramaId: drama.body.data.id,
|
|
episodeNo: 1,
|
|
title: 'Episode 1',
|
|
coverUrl: 'https://cdn.example.com/ep1.jpg',
|
|
videoUrl: 'https://cdn.example.com/ep1.mp4',
|
|
durationSeconds: 300,
|
|
status: 'draft',
|
|
};
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/episodes')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send(payload)
|
|
.expect(201);
|
|
|
|
const duplicate = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/episodes')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send(payload)
|
|
.expect(409);
|
|
|
|
expect(duplicate.body).toMatchObject({
|
|
code: 409,
|
|
data: {},
|
|
message: 'Episode number already exists in this drama',
|
|
});
|
|
});
|
|
|
|
it('creates episode slots when a new drama declares total episodes', async () => {
|
|
const drama = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Preconfigured Slots Drama',
|
|
coverUrl: 'https://cdn.example.com/preconfigured-slots.jpg',
|
|
type: 'romance',
|
|
status: 'draft',
|
|
totalEpisodes: 2,
|
|
})
|
|
.expect(201);
|
|
|
|
expect(drama.body.data).toMatchObject({
|
|
title: 'Preconfigured Slots Drama',
|
|
totalEpisodes: 2,
|
|
publishedEpisodes: 0,
|
|
});
|
|
|
|
const episodes = await request(app.getHttpServer())
|
|
.get(`/api/admin/v1/dramas/${drama.body.data.id}/episodes`)
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.expect(200);
|
|
|
|
expect(episodes.body.data.list).toEqual([
|
|
expect.objectContaining({
|
|
dramaId: drama.body.data.id,
|
|
episodeNo: 1,
|
|
title: '第1集',
|
|
publishStatus: 'draft',
|
|
}),
|
|
expect.objectContaining({
|
|
dramaId: drama.body.data.id,
|
|
episodeNo: 2,
|
|
title: '第2集',
|
|
publishStatus: 'draft',
|
|
}),
|
|
]);
|
|
});
|
|
|
|
it('updates an episode from the drama detail episodes endpoint', async () => {
|
|
const drama = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Nested Episode Update Drama',
|
|
coverUrl: 'https://cdn.example.com/nested-update.jpg',
|
|
type: 'action',
|
|
status: 'draft',
|
|
totalEpisodes: 1,
|
|
})
|
|
.expect(201);
|
|
|
|
const episodes = await request(app.getHttpServer())
|
|
.get(`/api/admin/v1/dramas/${drama.body.data.id}/episodes`)
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.expect(200);
|
|
const episodeId = episodes.body.data.list[0].id;
|
|
|
|
const updated = await request(app.getHttpServer())
|
|
.patch(`/api/admin/v1/dramas/${drama.body.data.id}/episodes/${episodeId}`)
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Updated Nested Episode',
|
|
description: 'Updated from drama detail.',
|
|
durationSeconds: 240,
|
|
freeType: 'paid',
|
|
price: 199,
|
|
})
|
|
.expect(200);
|
|
|
|
expect(updated.body.data).toMatchObject({
|
|
id: episodeId,
|
|
dramaId: drama.body.data.id,
|
|
title: 'Updated Nested Episode',
|
|
description: 'Updated from drama detail.',
|
|
durationSeconds: 240,
|
|
freeType: 'paid',
|
|
isPaid: true,
|
|
price: 199,
|
|
});
|
|
});
|
|
|
|
it('returns only published dramas and published episodes to app APIs', async () => {
|
|
const published = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Published Drama',
|
|
coverUrl: 'https://cdn.example.com/pub.jpg',
|
|
portraitCoverUrl: 'https://cdn.example.com/pub-portrait.jpg',
|
|
landscapeCoverUrl: 'https://cdn.example.com/pub-landscape.jpg',
|
|
type: 'action',
|
|
status: 'published',
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Draft Drama',
|
|
coverUrl: 'https://cdn.example.com/draft.jpg',
|
|
type: 'action',
|
|
status: 'draft',
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/episodes')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
dramaId: published.body.data.id,
|
|
episodeNo: 1,
|
|
title: 'Published Episode',
|
|
coverUrl: 'https://cdn.example.com/ep1.jpg',
|
|
videoUrl: 'https://cdn.example.com/ep1.mp4',
|
|
durationSeconds: 300,
|
|
status: 'published',
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/episodes')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
dramaId: published.body.data.id,
|
|
episodeNo: 2,
|
|
title: 'Draft Episode',
|
|
coverUrl: 'https://cdn.example.com/ep2.jpg',
|
|
videoUrl: 'https://cdn.example.com/ep2.mp4',
|
|
durationSeconds: 300,
|
|
status: 'draft',
|
|
})
|
|
.expect(201);
|
|
|
|
const dramas = await request(app.getHttpServer())
|
|
.get('/api/app/v1/dramas')
|
|
.expect(200);
|
|
|
|
expect(dramas.body.data.list).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
title: 'Published Drama',
|
|
plays: 0,
|
|
}),
|
|
]),
|
|
);
|
|
const publishedDrama = dramas.body.data.list.find(
|
|
(item: { title: string }) => item.title === 'Published Drama',
|
|
);
|
|
expect(publishedDrama).not.toHaveProperty('portraitCoverUrl');
|
|
expect(publishedDrama).not.toHaveProperty('landscapeCoverUrl');
|
|
expect(dramas.body.data.list).not.toEqual(
|
|
expect.arrayContaining([expect.objectContaining({ title: 'Draft Drama' })]),
|
|
);
|
|
|
|
const episodes = await request(app.getHttpServer())
|
|
.get(`/api/app/v1/dramas/${published.body.data.id}/episodes`)
|
|
.expect(200);
|
|
|
|
expect(episodes.body.data.list).toEqual([
|
|
expect.objectContaining({
|
|
episodeNo: 1,
|
|
title: 'Published Episode',
|
|
}),
|
|
]);
|
|
});
|
|
|
|
it('filters published dramas by title and defaults app page size to 10', async () => {
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Searchable Moon Drama',
|
|
coverUrl: 'https://cdn.example.com/searchable-moon.jpg',
|
|
type: 'romance',
|
|
status: 'published',
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Hidden Sun Drama',
|
|
coverUrl: 'https://cdn.example.com/hidden-sun.jpg',
|
|
type: 'romance',
|
|
status: 'published',
|
|
})
|
|
.expect(201);
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.get('/api/app/v1/dramas')
|
|
.query({ title: 'Moon' })
|
|
.expect(200);
|
|
|
|
expect(response.body.data.pagination.pageSize).toBe(10);
|
|
expect(response.body.data.list).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ title: 'Searchable Moon Drama' }),
|
|
]),
|
|
);
|
|
expect(response.body.data.list).not.toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ title: 'Hidden Sun Drama' }),
|
|
]),
|
|
);
|
|
});
|
|
|
|
it('filters admin dramas by title and recommended status', async () => {
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Recommended Search Drama',
|
|
coverUrl: 'https://cdn.example.com/recommended-search.jpg',
|
|
type: 'romance',
|
|
status: 'draft',
|
|
isRecommended: true,
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Recommended Hidden Drama',
|
|
coverUrl: 'https://cdn.example.com/recommended-hidden.jpg',
|
|
type: 'romance',
|
|
status: 'draft',
|
|
isRecommended: false,
|
|
})
|
|
.expect(201);
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.get('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.query({ title: 'Recommended', isRecommended: 'true' })
|
|
.expect(200);
|
|
|
|
expect(response.body.data.list).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
title: 'Recommended Search Drama',
|
|
isRecommended: true,
|
|
plays: 0,
|
|
}),
|
|
]),
|
|
);
|
|
expect(response.body.data.list).not.toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ title: 'Recommended Hidden Drama' }),
|
|
]),
|
|
);
|
|
});
|
|
|
|
it('returns a published recommended drama list to app APIs', async () => {
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Published Recommended Drama',
|
|
coverUrl: 'https://cdn.example.com/published-recommended.jpg',
|
|
type: 'romance',
|
|
status: 'published',
|
|
isRecommended: true,
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Published Normal Drama',
|
|
coverUrl: 'https://cdn.example.com/published-normal.jpg',
|
|
type: 'romance',
|
|
status: 'published',
|
|
isRecommended: false,
|
|
})
|
|
.expect(201);
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.get('/api/app/v1/dramas/recommended')
|
|
.expect(200);
|
|
|
|
expect(response.body.data.list).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
title: 'Published Recommended Drama',
|
|
isRecommended: true,
|
|
}),
|
|
]),
|
|
);
|
|
expect(response.body.data.list).not.toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ title: 'Published Normal Drama' }),
|
|
]),
|
|
);
|
|
});
|
|
|
|
it('presearches published dramas by drama title for mini app clients', async () => {
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Skyline Keyword Saga',
|
|
description: 'A searchable drama description.',
|
|
coverUrl: 'https://cdn.example.com/skyline-drama.jpg',
|
|
type: 'romance',
|
|
status: 'published',
|
|
})
|
|
.expect(201);
|
|
|
|
const episodeOnlyDrama = await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Episode Only Drama',
|
|
coverUrl: 'https://cdn.example.com/episode-only-drama.jpg',
|
|
type: 'romance',
|
|
status: 'published',
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/episodes')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
dramaId: episodeOnlyDrama.body.data.id,
|
|
episodeNo: 1,
|
|
title: 'Skyline Episode Title Only',
|
|
description: 'This should not make the drama match.',
|
|
coverUrl: 'https://cdn.example.com/skyline-ep1.jpg',
|
|
videoUrl: 'https://cdn.example.com/skyline-ep1.mp4',
|
|
byteplusVid: 'vid_skyline_001',
|
|
durationSeconds: 180,
|
|
freeType: 'free',
|
|
price: 0,
|
|
status: 'published',
|
|
publishStatus: 'published',
|
|
reviewStatus: 'approved',
|
|
})
|
|
.expect(201);
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/api/admin/v1/dramas')
|
|
.set('Authorization', `Bearer ${adminToken}`)
|
|
.send({
|
|
title: 'Skyline Draft Drama',
|
|
coverUrl: 'https://cdn.example.com/skyline-draft-drama.jpg',
|
|
type: 'romance',
|
|
status: 'draft',
|
|
})
|
|
.expect(201);
|
|
|
|
const response = await request(app.getHttpServer())
|
|
.get('/api/app/v1/dramas/presearch')
|
|
.query({ keyword: 'skyline' })
|
|
.expect(200);
|
|
|
|
expect(response.body.data.pagination.pageSize).toBe(10);
|
|
expect(response.body.data.list).toEqual([
|
|
expect.objectContaining({
|
|
title: 'Skyline Keyword Saga',
|
|
coverUrl: 'https://cdn.example.com/skyline-drama.jpg',
|
|
status: 'published',
|
|
}),
|
|
]);
|
|
expect(response.body.data.list).not.toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ title: 'Episode Only Drama' }),
|
|
expect.objectContaining({ title: 'Skyline Draft Drama' }),
|
|
]),
|
|
);
|
|
|
|
const emptyKeyword = await request(app.getHttpServer())
|
|
.get('/api/app/v1/dramas/presearch')
|
|
.query({ keyword: ' ' })
|
|
.expect(200);
|
|
|
|
expect(emptyKeyword.body.data).toEqual({
|
|
list: [],
|
|
pagination: { page: 1, pageSize: 10, total: 0 },
|
|
});
|
|
});
|
|
});
|