Skip to content

Commit

Permalink
done user and movie integration test
Browse files Browse the repository at this point in the history
  • Loading branch information
Iván García Laverde committed Aug 11, 2023
1 parent 093aafe commit 4a946a4
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 34 deletions.
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
"@types/express": "^4.17.11",
"@types/jsonwebtoken": "^8.5.1",
"@types/node": "14.18.2",
"@types/request": "^2.48.5",
"@types/superagent": "4.1.10",
"@types/supertest": "^2.0.11",
"@types/request": "^2.48.5",
"async": "^3.2.4",
"axios": "^0.26.1",
"bcrypt": "^5.1.0",
Expand All @@ -45,6 +45,7 @@
"devDependencies": {
"@types/express-serve-static-core": "^4.17.30",
"@types/jest": "^29.5.2",
"@types/lodash": "^4.14.197",
"@types/luxon": "^3.3.1",
"@types/supertest": "^2.0.12",
"apidoc": "^1.0.3",
Expand Down
30 changes: 30 additions & 0 deletions src/factories/MovieFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as Chance from 'chance';
import { EntityManager } from 'typeorm';
import { Movie } from '../entities/Movie';

export class MovieFactory {
private static chance = new Chance();

/**
* Creates a test movie
* @param {Partial<Movie>} movie
* @param {EntityManager} manager
* @returns {Promise<Movie>}
*/
public static async createMovie(movie: Partial<Movie>, manager?: EntityManager): Promise<Movie> {
let newMovie = new Movie({
tmdbId: movie.tmdbId ?? this.chance.integer({ min: 1, max: 1000000 }),
title: movie.title ?? this.chance.name(),
overview: movie.overview ?? this.chance.string(),
posterPath: movie.posterPath ?? `https://image.tmdb.org/t/p/w500/${this.chance.string({ length: 10 })}.jpg`,
releaseDate:
movie.releaseDate ??
new Date(this.chance.date({ year: this.chance.integer({ min: 1998, max: 2023 }) })),
});

Object.assign(newMovie, movie);
if (manager) newMovie = await manager.save(Movie, newMovie);

return newMovie;
}
}
38 changes: 38 additions & 0 deletions src/factories/ReviewFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as Chance from 'chance';
import { EntityManager } from 'typeorm';
import { Movie } from '../entities/Movie';
import { Review } from '../entities/Review';
import { User } from '../entities/User';

export class ReviewFactory {
private static chance = new Chance();

/**
* Creates a test review
* @param {Partial<Review>} review
* @param {EntityManager} manager
* @param {User} user
* @param {Movie} movie
* @param {EntityManager} manager
* @returns {Promise<Review>}
*/
public static async createReview(
review: Partial<Review>,
user: User,
movie: Movie,
manager?: EntityManager,
): Promise<Review> {
let newReview = new Review({
rating: review.rating ?? this.chance.integer({ min: 1, max: 10 }),
comment: review.comment ?? this.chance.string(),
movieTMDBId: movie.tmdbId,
username: user.username,
userId: user.id,
});

Object.assign(newReview, review);
if (manager) newReview = await manager.save(Review, newReview);

return newReview;
}
}
119 changes: 119 additions & 0 deletions src/services/movie/__test__/movieService.int.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import * as Chance from 'chance';
import * as _ from 'lodash';
import { DateTime } from 'luxon';
import * as request from 'supertest';
import { EntityManager } from 'typeorm';
import App from '../../../app';
import { Movie } from '../../../entities/Movie';
import { User, UserRoles } from '../../../entities/User';
import { MovieFactory } from '../../../factories/MovieFactory';
import { ReviewFactory } from '../../../factories/ReviewFactory';
import { UserFactory } from '../../../factories/UserFactory';
import * as Encryption from '../../../utils/encryption';
import { ReviewResponse } from '../../review/reviewTypes';
import { MovieController } from '../movieController';

const app = new App([new MovieController()]);
const chance = new Chance();
const server = app.getServer();
let manager: EntityManager;

beforeAll(async () => {
await app.listen();
await app.databaseConnection.resetConnections();
manager = app.getDatabaseManager();
});

beforeEach(async () => {
await app.databaseConnection.resetConnections();
});

afterAll(async () => {
await app.databaseConnection.resetConnections();
await app.databaseConnection.closeConnection();
});

describe('When sending a request', () => {
let user: User;
beforeEach(async () => {
user = await UserFactory.createUser(
{
email: '[email protected]',
role: UserRoles.USER,
password: await Encryption.getHashedPassword('very-secret-password'),
},
manager,
);

jest.spyOn(Encryption, 'decodeToken').mockReturnValue({
userId: user.id,
role: UserRoles.USER,
email: user.email,
exp: DateTime.local().plus({ hours: 1 }).toJSDate(),
});
});
describe('GET /movies, then', () => {
test('it should return a list of movies', async () => {
const movies = await Promise.all(
_.range(100).map(async () => {
return await MovieFactory.createMovie({}, manager);
}),
);

const response = await request(server).get('/movies');

expect(response.error).toBeFalsy();
expect(response.status).toBe(200);

expect(response.body).toHaveProperty('movies');
expect(response.body.movies).toHaveLength(10);
expect(response.body.pages).toBe(10);
response.body.movies.forEach((movie: Movie) => {
const dbMovie = movies.find((m) => m.id === movie.id);
expect(dbMovie).toBeDefined();
});
});
});
describe('GET /movies/:tmdbId/reviews, then', () => {
test('it should return all reviews from a movie', async () => {
const movie = await MovieFactory.createMovie(
{
tmdbId: 888,
title: 'Batman: The Dark Knight',
overview:
'The Dark Knight of Gotham City begins his war on crime with his first major enemy being the clownishly homicidal Joker.',
releaseDate: DateTime.fromISO('2008-07-16').toJSDate(),
},
manager,
);

const reviews = await Promise.all(
_.range(50).map(async () => {
const user = await UserFactory.createUser(
{
email: chance.email(),
role: UserRoles.USER,
password: await Encryption.getHashedPassword('very-secret-password'),
},
manager,
);

return await ReviewFactory.createReview({}, user, movie, manager);
}),
);

const response = await request(server).get(`/movies/${movie.tmdbId}/reviews`);

expect(response.error).toBeFalsy();
expect(response.status).toBe(200);

expect(response.body).toHaveProperty('reviews');
expect(response.body.reviews).toHaveLength(10);
expect(response.body.pages).toBe(5);
response.body.reviews.forEach((review: ReviewResponse) => {
const dbReview = reviews.find((r) => r.id === review.id);
expect(dbReview).toBeDefined();
});
});
});
});
1 change: 0 additions & 1 deletion src/services/movie/movieService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ export default class MovieService {

res.status(200).send({
movies: movies.currentPage,
pageCount: movies.currentPage.length,
pages: movies.pagesCount,
});
},
Expand Down
11 changes: 3 additions & 8 deletions src/services/movie/movieTypes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ReviewResponse } from '../review/reviewTypes';

/**
* Response for the GET /movies/{tmdbId}/reviews endpoint
*/
Expand All @@ -8,14 +10,7 @@ export interface MovieReviewsResponse {
overview: string;
posterPath: string;
releaseDate: Date;
reviews: {
id: string;
comment: string;
rating: number;
username: string;
createdAt: Date;
updatedAt: Date;
}[];
reviews: ReviewResponse[];
pageCount: number;
pages: number;
}
23 changes: 14 additions & 9 deletions src/services/review/reviewTypes.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
/**
* Review object sent in the response body
*/
export interface ReviewResponse {
id: string;
tmdbId?: number;
username?: string;
rating: number;
comment: string;
createdAt: Date;
updatedAt: Date;
}

/**
* Response for the POST /reviews endpoint
*/
export interface SubmitReviewResponse {
message: string;
review: {
id: string;
tmdbId: number;
username: string;
rating: number;
comment: string;
createdAt: Date;
updatedAt: Date;
};
review: ReviewResponse;
}
67 changes: 62 additions & 5 deletions src/services/user/__test__/userService.int.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import * as _ from 'lodash';
import { DateTime } from 'luxon';
import * as request from 'supertest';
import { EntityManager } from 'typeorm';
import App from '../../../app';
import { UserController } from '../userController';
import { User, UserRoles } from '../../../entities/User';
import * as Encryption from '../../../utils/encryption';
import { MovieFactory } from '../../../factories/MovieFactory';
import { ReviewFactory } from '../../../factories/ReviewFactory';
import { UserFactory } from '../../../factories/UserFactory';
import * as Encryption from '../../../utils/encryption';
import { ReviewResponse } from '../../review/reviewTypes';
import { UserController } from '../userController';

const app = new App([new UserController()]);
const server = app.getServer();
let manager: EntityManager;

beforeAll(async () => {
Expand All @@ -27,7 +32,7 @@ afterAll(async () => {

describe('When sending a POST request to /users/register, then', () => {
test('it should create a new user', async () => {
const response = await request(app.getServer()).post('/users/register').send({
const response = await request(server).post('/users/register').send({
username: 'ivangarl',
email: '[email protected]',
password: 'very-secret-password',
Expand Down Expand Up @@ -66,16 +71,68 @@ describe('When sending a POST request to /users/login, then', () => {
jest.spyOn(Encryption, 'decodeToken').mockReturnValue({
userId: user.id,
role: UserRoles.USER,
email: '[email protected]',
email: user.email,
exp: DateTime.local().plus({ hours: 1 }).toJSDate(),
});

const response = await request(app.getServer()).post('/users/login').send({
const response = await request(server).post('/users/login').send({
email: user.email,
password,
});

expect(response.error).toBeFalsy();
expect(response.status).toBe(200);

expect(response.body).toHaveProperty('token');
expect(response.body).toHaveProperty('email');
});
});

describe('When sending a GET request to /users/:username/reviews, then', () => {
test('it should return all reviews from a user', async () => {
const password = 'very-secret-password';
const user = await UserFactory.createUser(
{
email: '[email protected]',
role: UserRoles.USER,
password: await Encryption.getHashedPassword(password),
},
manager,
);

// create movies and reviews
const reviews = await Promise.all(
_.range(50).map(async () => {
const movie = await MovieFactory.createMovie({}, manager);
return await ReviewFactory.createReview({}, user, movie, manager);
}),
);

jest.spyOn(Encryption, 'decodeToken').mockReturnValue({
userId: user.id,
role: UserRoles.USER,
email: user.email,
exp: DateTime.local().plus({ hours: 1 }).toJSDate(),
});

const response = await request(server).get(`/users/${user.username}/reviews`);

expect(response.error).toBeFalsy();
expect(response.status).toBe(200);

expect(response.body.reviews).toHaveLength(10);
expect(response.body.id).toBe(user.id);
expect(response.body.username).toBe(user.username);
expect(response.body.email).toBe(user.email);
expect(response.body.role).toBe(user.role);
expect(response.body.pages).toBe(5);
response.body.reviews.forEach((review: ReviewResponse) => {
const dbReview = reviews.find((r) => r.id === review.id);
expect(dbReview).toBeDefined();
expect(review.id).toBe(dbReview.id);
expect(review.rating).toBe(dbReview.rating);
expect(review.comment).toBe(dbReview.comment);
expect(review.tmdbId).toBe(dbReview.movieTmdbId);
});
});
});
1 change: 0 additions & 1 deletion src/services/user/userMappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export const mapUserReviews = (user: User, reviews: Review[], pages: number): Us
updatedAt: review.updatedAt,
}))
: [],
pageCount: reviews.length,
pages,
};
};
Loading

0 comments on commit 4a946a4

Please sign in to comment.