parent
5147882e6a
commit
f647b8e7fb
@ -1,22 +0,0 @@ |
||||
import { Test, type TestingModule } from '@nestjs/testing'; |
||||
import { AppController } from './app.controller'; |
||||
import { AppService } from './app.service'; |
||||
|
||||
describe('AppController', () => { |
||||
let appController: AppController; |
||||
|
||||
beforeEach(async () => { |
||||
const app: TestingModule = await Test.createTestingModule({ |
||||
controllers: [AppController], |
||||
providers: [AppService], |
||||
}).compile(); |
||||
|
||||
appController = app.get<AppController>(AppController); |
||||
}); |
||||
|
||||
describe('root', () => { |
||||
it('should return "Hello World!"', () => { |
||||
expect(appController.getHello()).toBe('Hello World!'); |
||||
}); |
||||
}); |
||||
}); |
@ -1,12 +0,0 @@ |
||||
import { Controller, Get } from '@nestjs/common'; |
||||
import { AppService } from './app.service'; |
||||
|
||||
@Controller() |
||||
export class AppController { |
||||
constructor(private readonly appService: AppService) {} |
||||
|
||||
@Get() |
||||
getHello(): string { |
||||
return this.appService.getHello(); |
||||
} |
||||
} |
@ -0,0 +1,93 @@ |
||||
import { Test, type TestingModule } from '@nestjs/testing'; |
||||
import { ValidationPipe, type INestApplication } from '@nestjs/common'; |
||||
import request from 'supertest'; |
||||
import { AppModule } from './../src/app.module'; |
||||
import { createTestOmdbEnrichedDataService } from './utils/testHelpers'; |
||||
|
||||
describe('AppController (e2e)', () => { |
||||
let app: INestApplication; |
||||
|
||||
beforeAll(async () => { |
||||
const enrichedDataService = await createTestOmdbEnrichedDataService(); |
||||
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({ |
||||
imports: [AppModule], |
||||
}) |
||||
.overrideProvider('enrichedDataService') |
||||
.useValue(enrichedDataService) |
||||
.compile(); |
||||
|
||||
app = moduleFixture.createNestApplication(); |
||||
app.useGlobalPipes(new ValidationPipe({ transform: true })); |
||||
await app.init(); |
||||
}); |
||||
|
||||
it('/api/movies/tt0061852 (GET)', () => { |
||||
return request(app.getHttpServer()) |
||||
.get('/api/movies/tt0061852') |
||||
.expect(200) |
||||
.expect((response) => { |
||||
expect(response.body).toMatchObject({ |
||||
title: 'The Jungle Book', |
||||
}); |
||||
}); |
||||
}); |
||||
|
||||
it('/api/movies/11528860 (GET)', () => { |
||||
return request(app.getHttpServer()) |
||||
.get('/api/movies/11528860') |
||||
.expect(200) |
||||
.expect((response) => { |
||||
expect(response.body).toMatchObject({ |
||||
title: 'The Jungle Book', |
||||
}); |
||||
}); |
||||
}); |
||||
|
||||
it('/api/movies (GET)', () => { |
||||
return request(app.getHttpServer()).get('/api/movies').expect(400); |
||||
}); |
||||
|
||||
it('/api/movies?writers=George Lucas (GET)', () => { |
||||
return request(app.getHttpServer()) |
||||
.get('/api/movies?writers=George Lucas') |
||||
.expect(200) |
||||
.expect((response) => { |
||||
expect(response.body).toMatchObject([ |
||||
{ title: 'Indiana Jones and the Last Crusade' }, |
||||
{ title: 'Star Wars: Episode IV - A New Hope' }, |
||||
]); |
||||
}); |
||||
}); |
||||
|
||||
it('/api/movies?writers=George Lucas&actors=Mark Hamill (GET)', () => { |
||||
return request(app.getHttpServer()) |
||||
.get('/api/movies?writers=George Lucas&actors=Mark Hamill') |
||||
.expect(200) |
||||
.expect((response) => { |
||||
expect(response.body).toMatchObject([ |
||||
{ title: 'Star Wars: Episode IV - A New Hope' }, |
||||
]); |
||||
}); |
||||
}); |
||||
|
||||
it('/api/movies?productionYear=1967 (GET)', () => { |
||||
return request(app.getHttpServer()) |
||||
.get('/api/movies?productionYear=1967') |
||||
.expect(200) |
||||
.expect((response) => { |
||||
expect(response.body).toMatchObject([ |
||||
{ title: 'The Jungle Book' }, |
||||
]); |
||||
}); |
||||
}); |
||||
|
||||
it('/api/movies?productionYear=1800 (GET)', () => { |
||||
return request(app.getHttpServer()) |
||||
.get('/api/movies?productionYear=1800') |
||||
.expect(200) |
||||
.expect((response) => { |
||||
expect(response.body).toMatchObject([]); |
||||
}); |
||||
}); |
||||
}); |
@ -1,11 +1,16 @@ |
||||
import { Module } from '@nestjs/common'; |
||||
import { AppController } from './app.controller'; |
||||
import { AppService } from './app.service'; |
||||
import { MoviesController } from './movies.controller'; |
||||
import { createTestOmdbEnrichedDataService } from './utils/testHelpers'; |
||||
|
||||
@Module({ |
||||
imports: [], |
||||
controllers: [AppController], |
||||
providers: [AppService], |
||||
controllers: [MoviesController], |
||||
providers: [ |
||||
{ |
||||
provide: 'enrichedDataService', |
||||
useFactory: createTestOmdbEnrichedDataService, |
||||
}, |
||||
], |
||||
}) |
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export class AppModule {} |
||||
|
@ -1,8 +0,0 @@ |
||||
import { Injectable } from '@nestjs/common'; |
||||
|
||||
@Injectable() |
||||
export class AppService { |
||||
getHello(): string { |
||||
return 'Hello World!'; |
||||
} |
||||
} |
@ -1,8 +1,10 @@ |
||||
import { ValidationPipe } from '@nestjs/common'; |
||||
import { NestFactory } from '@nestjs/core'; |
||||
import { AppModule } from './app.module'; |
||||
|
||||
async function bootstrap() { |
||||
const app = await NestFactory.create(AppModule); |
||||
app.useGlobalPipes(new ValidationPipe({ transform: true })); |
||||
await app.listen(3000); |
||||
} |
||||
void bootstrap(); |
||||
|
@ -0,0 +1,48 @@ |
||||
import { Test, type TestingModule } from '@nestjs/testing'; |
||||
import { MoviesController } from './movies.controller'; |
||||
import { createTestOmdbEnrichedDataService } from './utils/testHelpers'; |
||||
|
||||
describe('AppController', () => { |
||||
let moviesController: MoviesController; |
||||
|
||||
beforeEach(async () => { |
||||
const app: TestingModule = await Test.createTestingModule({ |
||||
controllers: [MoviesController], |
||||
providers: [ |
||||
{ |
||||
provide: 'enrichedDataService', |
||||
useFactory: createTestOmdbEnrichedDataService, |
||||
}, |
||||
], |
||||
}).compile(); |
||||
|
||||
moviesController = app.get<MoviesController>(MoviesController); |
||||
}); |
||||
|
||||
describe('root', () => { |
||||
it('should return movie by internal id', () => { |
||||
expect(moviesController.getMovie('11528860')).toMatchObject({ |
||||
title: 'The Jungle Book', |
||||
}); |
||||
}); |
||||
|
||||
it('should return movie by imdb id', () => { |
||||
expect(moviesController.getMovie('tt0061852')).toMatchObject({ |
||||
title: 'The Jungle Book', |
||||
}); |
||||
}); |
||||
|
||||
it('should throw an error for empty search query', () => { |
||||
expect(() => moviesController.searchMovies({})).toThrow(); |
||||
}); |
||||
|
||||
it('should return movies matching writer', () => { |
||||
expect( |
||||
moviesController.searchMovies({ writers: 'George Lucas' }), |
||||
).toMatchObject([ |
||||
{ title: 'Indiana Jones and the Last Crusade' }, |
||||
{ title: 'Star Wars: Episode IV - A New Hope' }, |
||||
]); |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,102 @@ |
||||
import { |
||||
BadRequestException, |
||||
Controller, |
||||
Get, |
||||
Inject, |
||||
NotFoundException, |
||||
Param, |
||||
Query, |
||||
} from '@nestjs/common'; |
||||
import { EnrichedDataService } from './types'; |
||||
import { IsIn, IsInt, IsOptional, IsString, Length } from 'class-validator'; |
||||
import { Type } from 'class-transformer'; |
||||
|
||||
const ENTRY_TYPES = ['movie', 'series', 'episode'] as const; |
||||
|
||||
class SearchDto { |
||||
@IsString() |
||||
@Length(1) |
||||
@IsOptional() |
||||
actors?: string; |
||||
|
||||
@IsString() |
||||
@Length(2, 2) |
||||
@IsOptional() |
||||
availableLanguages?: string; |
||||
|
||||
@IsString() |
||||
@Length(1) |
||||
@IsOptional() |
||||
directors?: string; |
||||
|
||||
@IsString() |
||||
@Length(1) |
||||
@IsOptional() |
||||
localTitle?: string; |
||||
|
||||
@IsString() |
||||
@Length(2, 2) |
||||
@IsOptional() |
||||
originalLanguage?: string; |
||||
|
||||
@IsString() |
||||
@Length(1) |
||||
@IsOptional() |
||||
productionCountries?: string; |
||||
|
||||
@IsInt() |
||||
@Type(() => Number) |
||||
@IsOptional() |
||||
productionYear?: number; |
||||
|
||||
@IsString() |
||||
@Length(1) |
||||
@IsOptional() |
||||
studios?: string; |
||||
|
||||
@IsString() |
||||
@Length(1) |
||||
@IsOptional() |
||||
title?: string; |
||||
|
||||
@IsString() |
||||
@IsIn(ENTRY_TYPES) |
||||
@IsOptional() |
||||
type?: (typeof ENTRY_TYPES)[number]; |
||||
|
||||
@IsString() |
||||
@Length(1) |
||||
@IsOptional() |
||||
writers?: string; |
||||
} |
||||
|
||||
@Controller('/api/movies') |
||||
export class MoviesController { |
||||
constructor( |
||||
@Inject('enrichedDataService') |
||||
private readonly enrichedDataService: EnrichedDataService, |
||||
) {} |
||||
|
||||
@Get() |
||||
searchMovies(@Query() query: SearchDto) { |
||||
if (!Object.values(query).some((filterValue) => filterValue)) { |
||||
throw new BadRequestException('Query should not be empty'); |
||||
} |
||||
|
||||
const result = this.enrichedDataService.getSearchResults(query); |
||||
return result; |
||||
} |
||||
|
||||
@Get(':id') |
||||
getMovie(@Param('id') id: string) { |
||||
const result = /^\d+$/.test(id) |
||||
? this.enrichedDataService.getMetadataByInternalId(parseInt(id, 10)) |
||||
: this.enrichedDataService.getMetadataByImdbId(id); |
||||
|
||||
if (!result) { |
||||
throw new NotFoundException(); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
} |
Loading…
Reference in new issue