diff --git a/Dockerfile b/Dockerfile index 8e8ce26..390de8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,8 @@ COPY --from=builder /app/dist ./dist/ RUN npm ci +RUN npm run prisma:deploy + EXPOSE 8080 CMD ["npm", "run", "prod:start"] \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 415c926..73f91fb 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,6 +2,7 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', + setupFilesAfterEnv: ['/setupTest.ts'], transform: { '^.+\\.(t|j)sx?$': '@swc/jest' } diff --git a/setupTest.ts b/setupTest.ts new file mode 100644 index 0000000..e77699e --- /dev/null +++ b/setupTest.ts @@ -0,0 +1,7 @@ +import prisma from './src/clients/prisma-client' +import redis from './src/clients/redis-client' + +afterAll(async () => { + redis.disconnect() + await prisma.$disconnect() +}) diff --git a/src/@types/express.d.ts b/src/@types/express.d.ts index 217306c..84c48a0 100644 --- a/src/@types/express.d.ts +++ b/src/@types/express.d.ts @@ -1,3 +1,4 @@ +/* eslint-disable */ import * as express from 'express' import jwtPayload from '../interfaces/jwt' diff --git a/src/app.ts b/src/app.ts index 696d113..3c3a13e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -7,11 +7,8 @@ import limiter from './middlewares/rate-limit' const app = express() -// TODO: Disable image resize when it's a post attachment -// TODO: Add user-upload-picture tests -// TODO: Apply http-errors lib on the controllers -// TODO: Automatically apply the newest migration when starting up the docker -// TODO: Refactor some parts of the code +// TODO: find a way to declare global variables for better refactor on test +// TODO: must pass userMock as a global app.use(express.json()) app.use(express.urlencoded({ extended: true })) diff --git a/src/db.ts b/src/clients/prisma-client.ts similarity index 100% rename from src/db.ts rename to src/clients/prisma-client.ts diff --git a/src/clients/redis-client.ts b/src/clients/redis-client.ts new file mode 100644 index 0000000..f1f62ff --- /dev/null +++ b/src/clients/redis-client.ts @@ -0,0 +1,9 @@ +import RedisClient from 'ioredis' + +const redisPassword = process.env.REDIS_PASSWORD ?? '' +const redisHost = process.env.REDIS_HOST ?? '' +const redisPort = process.env.REDIS_PORT ?? '' + +const redis = new RedisClient(`redis://:${redisPassword}@${redisHost}:${redisPort}/0`) + +export default redis diff --git a/src/config/clients/s3-client.ts b/src/clients/s3-client.ts similarity index 100% rename from src/config/clients/s3-client.ts rename to src/clients/s3-client.ts diff --git a/src/config/multer.ts b/src/config/multer.ts index e070d01..f3788e7 100644 --- a/src/config/multer.ts +++ b/src/config/multer.ts @@ -1,7 +1,7 @@ import multer from 'multer' import { Request } from 'express' import path from 'path' -import s3 from './clients/s3-client' +import s3 from '../clients/s3-client' import multerS3 from 'multer-s3' const tempFolder = path.resolve(__dirname, '..', '..', 'temp', 'uploads') diff --git a/src/controllers/posts/post-create.ts b/src/controllers/posts/post-create.ts index a242e6d..7034f28 100644 --- a/src/controllers/posts/post-create.ts +++ b/src/controllers/posts/post-create.ts @@ -1,5 +1,6 @@ import { post } from '../../services/index' -import { Request, Response } from 'express' +import type { Request, Response } from 'express' +import { badRequest } from '../../lib/http-errors' async function postCreateController (req: Request, res: Response): Promise { const { content } = req.body @@ -8,10 +9,7 @@ async function postCreateController (req: Request, res: Response): Promise const result = await post.create(content, id) if (result instanceof Error) { - res.status(400).json({ - error: result.message - }) - return + return badRequest(res, result.message) } res.json(result) diff --git a/src/controllers/posts/post-delete.ts b/src/controllers/posts/post-delete.ts index ee61c06..8f3bedd 100644 --- a/src/controllers/posts/post-delete.ts +++ b/src/controllers/posts/post-delete.ts @@ -1,5 +1,6 @@ import { post } from '../../services/index' -import { Request, Response } from 'express' +import type { Request, Response } from 'express' +import { badRequest } from '../../lib/http-errors' async function postDeleteController (req: Request, res: Response): Promise { const userId = req.user?.id ?? '' @@ -8,10 +9,7 @@ async function postDeleteController (req: Request, res: Response): Promise const result = await post.delete(postId, userId) if (result instanceof Error) { - res.status(400).json({ - error: result.message - }) - return + return badRequest(res, result.message) } res.json(result) diff --git a/src/controllers/posts/post-info.ts b/src/controllers/posts/post-info.ts index f69f3e6..6947e4e 100644 --- a/src/controllers/posts/post-info.ts +++ b/src/controllers/posts/post-info.ts @@ -1,23 +1,18 @@ import { post } from '../../services/index' -import { Request, Response } from 'express' +import type { Request, Response } from 'express' +import { badRequest } from '../../lib/http-errors' async function postInfoController (req: Request, res: Response): Promise { const id = req.query.id as string if (id === undefined) { - res.status(400).json({ - error: 'Missing username' - }) - return + return badRequest(res, 'Missing post id') } const result = await post.info(id) if (result instanceof Error) { - res.status(400).json({ - error: result.message - }) - return + return badRequest(res, result.message) } res.json(result) diff --git a/src/controllers/posts/post-update.ts b/src/controllers/posts/post-update.ts index 7d5f3ea..8e356e6 100644 --- a/src/controllers/posts/post-update.ts +++ b/src/controllers/posts/post-update.ts @@ -1,5 +1,6 @@ import { post } from '../../services/index' -import { Request, Response } from 'express' +import type { Request, Response } from 'express' +import { badRequest } from '../../lib/http-errors' async function postUpdateController (req: Request, res: Response): Promise { const { postId, content } = req.body @@ -8,10 +9,7 @@ async function postUpdateController (req: Request, res: Response): Promise const result = await post.update(postId, content, userId) if (result instanceof Error) { - res.status(400).json({ - error: result.message - }) - return + return badRequest(res, result.message) } res.json(result) diff --git a/src/controllers/users/user-auth.ts b/src/controllers/users/user-auth.ts index 63cfe06..261582b 100644 --- a/src/controllers/users/user-auth.ts +++ b/src/controllers/users/user-auth.ts @@ -1,5 +1,6 @@ import { user } from '../../services/index' import type { Request, Response } from 'express' +import { badRequest } from '../../lib/http-errors' async function userAuthController (req: Request, res: Response): Promise { const { email, password } = req.body @@ -7,10 +8,7 @@ async function userAuthController (req: Request, res: Response): Promise { const result = await user.auth(email, password) if (result instanceof Error) { - res.status(400).json({ - error: result.message - }) - return + return badRequest(res, result.message) } res.json(result) diff --git a/src/controllers/users/user-delete.ts b/src/controllers/users/user-delete.ts index 59d4f7d..7611450 100644 --- a/src/controllers/users/user-delete.ts +++ b/src/controllers/users/user-delete.ts @@ -1,15 +1,13 @@ import { user } from '../../services' -import { Request, Response } from 'express' +import type { Request, Response } from 'express' +import { badRequest } from '../../lib/http-errors' async function userDeleteController (req: Request, res: Response): Promise { const userId = req.user?.id ?? '' const result = await user.delete(userId) if (result instanceof Error) { - res.status(400).json({ - error: result.message - }) - return + return badRequest(res, result.message) } res.json(result) diff --git a/src/controllers/users/user-info.ts b/src/controllers/users/user-info.ts index a6825ad..808c914 100644 --- a/src/controllers/users/user-info.ts +++ b/src/controllers/users/user-info.ts @@ -1,23 +1,18 @@ import { user } from '../../services' import type { Request, Response } from 'express' +import { badRequest } from '../../lib/http-errors' async function userInfoController (req: Request, res: Response): Promise { const username = req.query.u as string if (username === undefined) { - res.status(400).json({ - error: 'Missing username' - }) - return + return badRequest(res, 'Missing username') } const result = await user.info(username.toLowerCase()) if (result instanceof Error) { - res.status(400).json({ - error: result.message - }) - return + return badRequest(res, result.message) } res.json(result) diff --git a/src/controllers/users/user-signup.ts b/src/controllers/users/user-signup.ts index b53a0fa..6f3b20f 100644 --- a/src/controllers/users/user-signup.ts +++ b/src/controllers/users/user-signup.ts @@ -1,5 +1,6 @@ import { user } from '../../services' import type { Request, Response } from 'express' +import { badRequest } from '../../lib/http-errors' async function userSignupController (req: Request, res: Response): Promise { const { username, email, password } = req.body @@ -7,10 +8,7 @@ async function userSignupController (req: Request, res: Response): Promise const result = await user.signup(username, email, password) if (result instanceof Error) { - res.status(400).json({ - error: result.message - }) - return + return badRequest(res, result.message) } res.json(result) diff --git a/src/controllers/users/user-update.ts b/src/controllers/users/user-update.ts index b2ec1bc..28fd09e 100644 --- a/src/controllers/users/user-update.ts +++ b/src/controllers/users/user-update.ts @@ -1,5 +1,6 @@ import { user } from '../../services' -import { Request, Response } from 'express' +import type { Request, Response } from 'express' +import { badRequest } from '../../lib/http-errors' async function userUpdateController (req: Request, res: Response): Promise { const { email, displayName, username } = req.body @@ -8,10 +9,7 @@ async function userUpdateController (req: Request, res: Response): Promise const result = await user.update({ id, email, displayName, username }) if (result instanceof Error) { - res.status(400).json({ - error: result.message - }) - return + return badRequest(res, result.message) } res.json(result) diff --git a/src/controllers/users/user-upload-picture.ts b/src/controllers/users/user-upload-picture.ts index 138bc26..e4820e9 100644 --- a/src/controllers/users/user-upload-picture.ts +++ b/src/controllers/users/user-upload-picture.ts @@ -1,5 +1,6 @@ -import userUploadPictureService from '../../services/users/user-upload-picture' -import { Request, Response } from 'express' +/* eslint-disable @typescript-eslint/restrict-template-expressions */ +import { user } from '../../services/index' +import type { Request, Response } from 'express' import { badRequest } from '../../lib/http-errors' let url @@ -12,7 +13,6 @@ async function userUploadPictureController (req: Request, res: Response): Promis const userId = req.user?.id ?? '' if (process.env.NODE_ENV === 'development') { - /* eslint-disable */ // @ts-expect-error property `key` doesn't exists in @types/express url = `http://${process.env.AWS_BUCKET ?? ''}.s3.localhost.localstack.cloud:4566/${req.file.key}` } else { @@ -20,7 +20,7 @@ async function userUploadPictureController (req: Request, res: Response): Promis url = req.file.location } - const result = await userUploadPictureService(userId, url) + const result = await user.uploadPicture(userId, url) if (result instanceof Error) { return badRequest(res, result.message) diff --git a/src/lib/compress-image.ts b/src/lib/compress-image.ts index ea3af57..4d02e10 100644 --- a/src/lib/compress-image.ts +++ b/src/lib/compress-image.ts @@ -1,5 +1,5 @@ import sharp from 'sharp' -import s3 from '../config/clients/s3-client' +import s3 from '../clients/s3-client' import { GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3' export default async function compressImage (imageName: string): Promise { @@ -12,8 +12,7 @@ export default async function compressImage (imageName: string): Promise { if (req.headers.authorization === undefined || req.headers.authorization.length === 0) { - res.status(401).json({ - error: 'Missing token' - }) - return + return unauthorized(res, 'Missing token') } const token = req.headers.authorization.split(' ')[1] @@ -25,10 +23,7 @@ async function ensureAuthenticated (req: Request, res: Response, next: NextFunct }) if (decoded == null) { - res.status(401).json({ - error: 'Invalid token' - }) - return + return unauthorized(res, 'Invalid token') } const user = await prisma.user.findFirst({ @@ -38,19 +33,14 @@ async function ensureAuthenticated (req: Request, res: Response, next: NextFunct }) if (user == null) { - res.status(401).json({ - error: 'Invalid user' - }) - return + return unauthorized(res, 'User does not exists') } req.user = decoded return next() } catch (error) { - res.status(401).json({ - error: `JWT Error: ${(error as Error).message}` - }) + unauthorized(res, `JWT Error: ${(error as Error).message}`) } } diff --git a/src/middlewares/rate-limit.ts b/src/middlewares/rate-limit.ts index 5f92cb0..e1a4af4 100644 --- a/src/middlewares/rate-limit.ts +++ b/src/middlewares/rate-limit.ts @@ -1,12 +1,6 @@ import rateLimit from 'express-rate-limit' import RedisStore from 'rate-limit-redis' -import RedisClient from 'ioredis' - -const redisPassword = process.env.REDIS_PASSWORD ?? '' -const redisHost = process.env.REDIS_HOST ?? '' -const redisPort = process.env.REDIS_PORT ?? '' - -const client = new RedisClient(`redis://:${redisPassword}@${redisHost}:${redisPort}/0`) +import redis from '../clients/redis-client' let maxConnections @@ -26,7 +20,7 @@ const limiter = rateLimit({ // Store configuration store: new RedisStore({ // @ts-expect-error - `call` function is not present in @types/ioredis - sendCommand: async (...args: string[]) => await client.call(...args) + sendCommand: async (...args: string[]) => await redis.call(...args) }) }) diff --git a/src/middlewares/upload-image.ts b/src/middlewares/upload-image.ts index 68d20cc..5f3e295 100644 --- a/src/middlewares/upload-image.ts +++ b/src/middlewares/upload-image.ts @@ -1,34 +1,25 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { Response, Request, NextFunction } from 'express' +import type { Response, Request, NextFunction } from 'express' import multer from 'multer' import multerConfig from '../config/multer' import compressImage from '../lib/compress-image' +import { badRequest } from '../lib/http-errors' function uploadImage (req: Request, res: Response, next: NextFunction) { const upload = multer(multerConfig).single('image') upload(req, res, async (cb: multer.MulterError | Error | any) => { if (req.user == null) { - return res.status(400).json({ - error: 'You must be logged in to upload a profile picture' - }) + return badRequest(res, 'You must be logged in to upload a profile picture') } if (cb instanceof multer.MulterError || cb instanceof Error) { - return res.status(400).json({ - error: cb.message - }) + return badRequest(res, cb.message) } // @ts-expect-error property `key` does not exists in types - const compressed = await compressImage(req.file?.key) - - if (compressed instanceof Error) { - return res.status(500).json({ - error: compressed.message - }) - } + await compressImage(req.file?.key) next() }) diff --git a/src/services/index.ts b/src/services/index.ts index a60df62..a2b1203 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -3,6 +3,7 @@ import userDeleteService from './users/user-delete' import userInfoService from './users/user-info' import userSignupService from './users/user-signup' import userUpdateService from './users/user-update' +import userUploadPictureService from './users/user-upload-picture' import postCreateService from './posts/post-create' import postDeleteService from './posts/post-delete' @@ -15,7 +16,8 @@ const user = { delete: userDeleteService, info: userInfoService, signup: userSignupService, - update: userUpdateService + update: userUpdateService, + uploadPicture: userUploadPictureService } // Post services diff --git a/src/services/posts/post-create.ts b/src/services/posts/post-create.ts index 0930b87..c971e14 100644 --- a/src/services/posts/post-create.ts +++ b/src/services/posts/post-create.ts @@ -1,4 +1,4 @@ -import prisma from '../../db' +import prisma from '../../clients/prisma-client' async function postCreateService (content: string, authorId: string): Promise { const user = await prisma.user.findFirst({ where: { id: authorId } }) diff --git a/src/services/posts/post-delete.ts b/src/services/posts/post-delete.ts index 438e500..350dc88 100644 --- a/src/services/posts/post-delete.ts +++ b/src/services/posts/post-delete.ts @@ -1,4 +1,4 @@ -import prisma from '../../db' +import prisma from '../../clients/prisma-client' async function postDeleteService (postId: string, userId: string): Promise { const post = await prisma.post.findFirst({ where: { id: postId } }) diff --git a/src/services/posts/post-info.ts b/src/services/posts/post-info.ts index 0853b0d..96334e2 100644 --- a/src/services/posts/post-info.ts +++ b/src/services/posts/post-info.ts @@ -1,4 +1,4 @@ -import prisma from '../../db' +import prisma from '../../clients/prisma-client' async function postInfoService (id: string): Promise { const post = await prisma.post.findFirst({ diff --git a/src/services/posts/post-update.ts b/src/services/posts/post-update.ts index 078d13c..4667cde 100644 --- a/src/services/posts/post-update.ts +++ b/src/services/posts/post-update.ts @@ -1,4 +1,4 @@ -import prisma from '../../db' +import prisma from '../../clients/prisma-client' async function postUpdateService (postId: string, content: string, userId: string): Promise { const post = await prisma.post.findFirst({ where: { id: postId } }) diff --git a/src/services/users/user-auth.ts b/src/services/users/user-auth.ts index f4aa859..61510e8 100644 --- a/src/services/users/user-auth.ts +++ b/src/services/users/user-auth.ts @@ -1,6 +1,6 @@ import * as bcrypt from 'bcrypt' import jsonwebtoken from 'jsonwebtoken' -import prisma from '../../db' +import prisma from '../../clients/prisma-client' async function userAuthService (email: string, password: string): Promise { const user = await prisma.user.findFirst({ diff --git a/src/services/users/user-delete.ts b/src/services/users/user-delete.ts index ea249d7..40754a2 100644 --- a/src/services/users/user-delete.ts +++ b/src/services/users/user-delete.ts @@ -1,4 +1,4 @@ -import prisma from '../../db' +import prisma from '../../clients/prisma-client' async function userDeleteService (userId: string): Promise { const user = await prisma.user.findFirst({ where: { id: userId } }) diff --git a/src/services/users/user-info.ts b/src/services/users/user-info.ts index 2972e80..6943274 100644 --- a/src/services/users/user-info.ts +++ b/src/services/users/user-info.ts @@ -1,4 +1,4 @@ -import prisma from '../../db' +import prisma from '../../clients/prisma-client' async function userInfoService (username: string): Promise { const user = await prisma.user.findFirst({ diff --git a/src/services/users/user-signup.ts b/src/services/users/user-signup.ts index ec0b568..5f31a31 100644 --- a/src/services/users/user-signup.ts +++ b/src/services/users/user-signup.ts @@ -1,8 +1,8 @@ import * as bcrypt from 'bcrypt' import validator from 'validator' -import prisma from '../../db' +import prisma from '../../clients/prisma-client' -const passwordRegex = /^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{8,}$/ +const passwordRegex = /^(?=.*[0-9])(?=.*[!@#$%^&*_])[a-zA-Z0-9!@#$%^&*_]{8,}$/ const usernameRegex = /^[a-zA-Z0-9_.]{5,15}$/ async function userSignupService (username: string, email: string, password: string): Promise { @@ -11,7 +11,7 @@ async function userSignupService (username: string, email: string, password: str } if (!passwordRegex.test(password)) { - return new Error('Password must have 8 characters, one number and one special character.') + return new Error('Password must have at least 8 characters, one number and one special character.') } if (password.trim().length < 8) { diff --git a/src/services/users/user-update.ts b/src/services/users/user-update.ts index 03bf82c..2ca88b5 100644 --- a/src/services/users/user-update.ts +++ b/src/services/users/user-update.ts @@ -1,5 +1,5 @@ import userPayload from '../../interfaces/user' -import prisma from '../../db' +import prisma from '../../clients/prisma-client' async function userUpdateService ({ id, email, displayName, username }: userPayload): Promise { const user = await prisma.user.findFirst({ where: { id } }) diff --git a/src/services/users/user-upload-picture.ts b/src/services/users/user-upload-picture.ts index 35a2d8e..2f3c2cd 100644 --- a/src/services/users/user-upload-picture.ts +++ b/src/services/users/user-upload-picture.ts @@ -1,4 +1,4 @@ -import prisma from '../../db' +import prisma from '../../clients/prisma-client' async function userUploadPictureService (authorId: string, url: string): Promise { const user = await prisma.user.findFirst({ where: { id: authorId } }) diff --git a/src/tests/assets/profile_picture.png b/src/tests/assets/profile_picture.png new file mode 100644 index 0000000..21e33c6 Binary files /dev/null and b/src/tests/assets/profile_picture.png differ diff --git a/src/tests/post/post-create.spec.ts b/src/tests/post/post-create.spec.ts index b901860..8d7ed6b 100644 --- a/src/tests/post/post-create.spec.ts +++ b/src/tests/post/post-create.spec.ts @@ -1,39 +1,24 @@ -import prisma from '../../db' import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' +import deleteUser from '../utils/delete-user' +import userPayload from '../../interfaces/user' -let token = ''; let username = '' +let user: userPayload describe('POST /post/create', () => { beforeAll(async () => { - const user = await signUpNewUser() - - token = user.token ?? '' - username = user.username ?? '' + user = await signUpNewUser() }) afterAll(async () => { - await prisma.post.deleteMany({ - where: { - author: { - username - } - } - }) - - await prisma.user.deleteMany({ - where: { - username - } - }) - await prisma.$disconnect() + await deleteUser(user.username ?? '') }) it('should respond with 200 status code if the user send the token and the content', async () => { const response = await request(app).post('/post/create').send({ - content: '4764ba063310b6f8bab31e8348b2188a' - }).set('Authorization', `Bearer ${token}`).expect(200) + content: 'Hello world' + }).set('Authorization', `Bearer ${user.token ?? ''}`).expect(200) expect(response.body).toEqual(expect.objectContaining({ id: expect.any(String), diff --git a/src/tests/post/post-delete.spec.ts b/src/tests/post/post-delete.spec.ts index b8004fb..40f5a94 100644 --- a/src/tests/post/post-delete.spec.ts +++ b/src/tests/post/post-delete.spec.ts @@ -1,26 +1,18 @@ -import prisma from '../../db' import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' +import deleteUser from '../utils/delete-user' +import userPayload from '../../interfaces/user' -let token = ''; let username = '' +let user: userPayload describe('DELETE /post/delete', () => { beforeAll(async () => { - const user = await signUpNewUser() - - token = user.token ?? '' - username = user.username ?? '' + user = await signUpNewUser() }) afterAll(async () => { - await prisma.user.deleteMany({ - where: { - username - } - }) - - await prisma.$disconnect() + await deleteUser(user.username ?? '') }) it('should delete the post successfully', async () => { @@ -29,13 +21,13 @@ describe('DELETE /post/delete', () => { .send({ content: 'lorem ipsum' }) - .set('Authorization', `Bearer ${token}`).expect(200) + .set('Authorization', `Bearer ${user.token ?? ''}`).expect(200) await request(app) .post('/post/delete') .send({ postId: response.body.id }) - .set('Authorization', `Bearer ${token}`).expect(200) + .set('Authorization', `Bearer ${user.token ?? ''}`).expect(200) }) }) diff --git a/src/tests/post/post-info.spec.ts b/src/tests/post/post-info.spec.ts index 7a499a3..e8a2b24 100644 --- a/src/tests/post/post-info.spec.ts +++ b/src/tests/post/post-info.spec.ts @@ -1,37 +1,28 @@ -import prisma from '../../db' import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' +import deleteUser from '../utils/delete-user' +import userPayload from '../../interfaces/user' -let postId = ''; let username = '' +let postId: string + +let user: userPayload describe('POST /post/info', () => { beforeAll(async () => { - const user = await signUpNewUser() + user = await signUpNewUser() const token = user.token ?? '' const post = await request(app).post('/post/create').send({ - content: 'nothing to see here!' + content: 'Hello world' }).set('Authorization', `Bearer ${token}`).expect(200) - username = user.username ?? '' postId = post.body.id }) afterAll(async () => { - await prisma.post.deleteMany({ - where: { - id: postId - } - }) - - await prisma.user.deleteMany({ - where: { - username - } - }) - await prisma.$disconnect() + await deleteUser(user.username ?? '') }) it('should respond with 200 status code and return some info about the post', async () => { @@ -47,7 +38,7 @@ describe('POST /post/info', () => { }) it('should respond with 400 status code if the post does not exists', async () => { - const response = await request(app).get('/post/info?id=randominfohere').expect(400) + const response = await request(app).get('/post/info?id=abc').expect(400) expect(response.body).toHaveProperty('error') }) diff --git a/src/tests/post/post-update.spec.ts b/src/tests/post/post-update.spec.ts index 9e848e7..bd19dec 100644 --- a/src/tests/post/post-update.spec.ts +++ b/src/tests/post/post-update.spec.ts @@ -1,40 +1,24 @@ -import prisma from '../../db' import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' +import deleteUser from '../utils/delete-user' +import userPayload from '../../interfaces/user' -let token = ''; let username = '' +let user: userPayload describe('PUT /post/update', () => { beforeAll(async () => { - const user = await signUpNewUser() - - username = user.username ?? '' - token = user.token ?? '' + user = await signUpNewUser() }) afterAll(async () => { - await prisma.post.deleteMany({ - where: { - author: { - username - } - } - }) - - await prisma.user.deleteMany({ - where: { - username - } - }) - - await prisma.$disconnect() + await deleteUser(user.username ?? '') }) it('should create a new post and update the content of it', async () => { const post = await request(app).post('/post/create').send({ content: 'Lorem' - }).set('Authorization', `Bearer ${token}`).expect(200) + }).set('Authorization', `Bearer ${user.token ?? ''}`).expect(200) expect(post.body).toHaveProperty('id') @@ -46,7 +30,7 @@ describe('PUT /post/update', () => { const response = await request(app) .put('/post/update') .send(fieldsToUpdate) - .set('Authorization', `Bearer ${token}`).expect(200) + .set('Authorization', `Bearer ${user.token ?? ''}`).expect(200) // Post content should be Lorem Ipsum if (post.body.content === response.body.content) { diff --git a/src/tests/user/user-auth.spec.ts b/src/tests/user/user-auth.spec.ts index a0a22c4..f4a9a3f 100644 --- a/src/tests/user/user-auth.spec.ts +++ b/src/tests/user/user-auth.spec.ts @@ -1,50 +1,37 @@ import request from 'supertest' -import prisma from '../../db' import app from '../../app' +import deleteUser from '../utils/delete-user' +import signUpNewUser from '../utils/create-user' +import userPayload from '../../interfaces/user' + +let user: userPayload describe('POST /user/auth', () => { beforeAll(async () => { - await prisma.user.create({ - data: { - username: 'dummmyuser6', - email: 'test@test.com', - password: 'pass' - } - }) + user = await signUpNewUser() }) afterAll(async () => { - await prisma.user.deleteMany({ - where: { - username: 'dummmyuser6' - } - }) - await prisma.$disconnect() + await deleteUser(user.username ?? '') }) it('should respond with a error if the user does not exists', async () => { - const response = await request(app) - .post('/user/auth') - .send({ - email: 'mm@mm.com', - password: 'aa' - }).expect(400) + const response = await request(app).post('/user/auth').send({ email: 'mm@mm.com', password: 'aa' }).expect(400) + expect(response.body).toHaveProperty('error') expect(response.body.error).toBe('User does not exists') }) it('should respond with a error if receive an invalid email or password', async () => { - const response = await request(app) - .post('/user/auth').send({ - email: 'test@test.com', - password: 'haha' - }).expect(400) + const response = await request(app).post('/user/auth').send({ email: user.email, password: 'fake_pass' }).expect(400) + expect(response.body).toHaveProperty('error') expect(response.body.error).toBe('Invalid email or password') }) it('should respond with a error if receive an empty body', async () => { const response = await request(app).post('/user/auth').send({}).expect(400) + expect(response.body).toHaveProperty('error') expect(response.body.error).toBe('Missing fields') }) diff --git a/src/tests/user/user-delete.spec.ts b/src/tests/user/user-delete.spec.ts index a12b000..bc81032 100644 --- a/src/tests/user/user-delete.spec.ts +++ b/src/tests/user/user-delete.spec.ts @@ -1,23 +1,18 @@ -import prisma from '../../db' import app from '../../app' import request from 'supertest' import signUpNewUser from '../utils/create-user' +import userPayload from '../../interfaces/user' -let token = '' +let user: userPayload describe('DELETE /user/delete', () => { beforeAll(async () => { - const user = await signUpNewUser() - token = user.token ?? '' - }) - - afterAll(async () => { - await prisma.$disconnect() + user = await signUpNewUser() }) it('should delete the user successfully', async () => { await request(app).post('/user/delete') - .set('Authorization', `Bearer ${token}`) + .set('Authorization', `Bearer ${user.token ?? ''}`) .expect(200) }) }) diff --git a/src/tests/user/user-info.spec.ts b/src/tests/user/user-info.spec.ts index 6852eb9..f27a727 100644 --- a/src/tests/user/user-info.spec.ts +++ b/src/tests/user/user-info.spec.ts @@ -1,28 +1,24 @@ -import prisma from '../../db' import app from '../../app' import request from 'supertest' +import deleteUser from '../utils/delete-user' +import signUpNewUser from '../utils/create-user' +import userPayload from '../../interfaces/user' + +let user: userPayload describe('POST /user/info', () => { + beforeAll(async () => { + user = await signUpNewUser() + }) + afterAll(async () => { - await prisma.user.deleteMany({ - where: { - username: 'dummmyuser5' - } - }) - await prisma.$disconnect() + await deleteUser(user.username ?? '') }) it('should respond with 200 status code and return the user data', async () => { - await prisma.user.create({ - data: { - username: 'dummmyuser5', - email: 'random3@email.com', - password: 'pass' - } - }) - - const response = await request(app).get('/user/info?u=dummmyuser5').expect(200) + const response = await request(app).get(`/user/info?u=${user.username ?? ''}`).expect(200) + expect(response.body).toHaveProperty('profileImage') expect(response.body).toHaveProperty('displayName') expect(response.body).toHaveProperty('username') expect(response.body).toHaveProperty('createdAt') diff --git a/src/tests/user/user-signup.spec.ts b/src/tests/user/user-signup.spec.ts index 8fe23f6..b78568b 100644 --- a/src/tests/user/user-signup.spec.ts +++ b/src/tests/user/user-signup.spec.ts @@ -1,83 +1,38 @@ import request from 'supertest' -import prisma from '../../db' import app from '../../app' +import deleteUser from '../utils/delete-user' +import signUpNewUser from '../utils/create-user' +import userPayload from '../../interfaces/user' -const mockUser = { - username: 'dummmyuser1', - email: 'random@email.com', - password: 'totallysafepass' -} +let user: userPayload describe('POST /user/signup', () => { - it('should respond with a 200 status code', async () => { - const response = await request(app).post('/user/signup').send(mockUser).expect(200) + beforeAll(async () => { + user = await signUpNewUser() + delete user.token + }) - expect(response.body).toHaveProperty('displayName') - expect(response.body).toHaveProperty('username') - expect(response.body).toHaveProperty('createdAt') + afterAll(async () => { + await deleteUser(user.username ?? '') }) it('should respond with a 400 status code if sent any invalid data', async () => { await request(app).post('/user/signup').send({ username: 'username12@', - email: mockUser.email, - password: mockUser.password + email: user.email, + password: user.password }).expect(400) }) - it('should respond with a 400 status code for an existing username', async () => { - await prisma.user.create({ - data: { - username: 'dummmyuser2', - email: 'user@email.com', - password: 'reallystrongpass' - } - }) - - const response = await request(app).post('/user/signup').send({ - username: 'dummmyuser2', - email: 'user1@email.com', - password: 'reallystrongpass' + it('should respond with a 400 status code for an existing username or email', async () => { + await request(app).post('/user/signup').send({ + username: user.username, + email: user.email, + password: user.password }).expect(400) - - expect(response.body).toHaveProperty('error') - }) - - it('should respond with a 400 status code for an existing email', async () => { - await prisma.user.create({ - data: { - username: 'dummmyuser3', - email: 'user13@email.com', - password: '1234' - } - }) - - const response = await request(app).post('/user/signup').send({ - username: 'dummmyuser4', - email: 'user13@email.com', - password: '12345' - }).expect(400) - - expect(response.body).toHaveProperty('error') }) it('should respond with a 400 status code if receive an empty body', async () => { - const response = await request(app).post('/user/signup').send({}).expect(400) - - expect(response.body).toHaveProperty('error') + await request(app).post('/user/signup').send({}).expect(400) }) }) - -afterAll(async () => { - const usersToDelete = ['dummmyuser1', 'dummmyuser2', 'dummmyuser3', 'dummmyuser4'] - - await prisma.user.deleteMany({ - where: { - username: { - in: usersToDelete - } - } - }) - - await prisma.$disconnect() -}) diff --git a/src/tests/user/user-update.spec.ts b/src/tests/user/user-update.spec.ts index dfcb917..abd5731 100644 --- a/src/tests/user/user-update.spec.ts +++ b/src/tests/user/user-update.spec.ts @@ -1,26 +1,18 @@ import request from 'supertest' import app from '../../app' -import prisma from '../../db' import signUpNewUser from '../utils/create-user' +import deleteUser from '../utils/delete-user' +import userPayload from '../../interfaces/user' -let token = ''; let username = '' +let user: userPayload describe('PUT /user/update', () => { beforeAll(async () => { - const user = await signUpNewUser() - - username = user.username ?? '' - token = user.token ?? '' + user = await signUpNewUser() }) afterAll(async () => { - await prisma.user.deleteMany({ - where: { - username - } - }) - - await prisma.$disconnect() + await deleteUser(user.username ?? '') }) it('should update the user successfully', async () => { @@ -31,7 +23,7 @@ describe('PUT /user/update', () => { const response = await request(app) .put('/user/update') .send(fieldsToUpdate) - .set('Authorization', `Bearer ${token}`).expect(200) + .set('Authorization', `Bearer ${user.token ?? ''}`).expect(200) expect(response.body).toEqual(expect.objectContaining({ displayName: expect.any(String), diff --git a/src/tests/utils/create-user.ts b/src/tests/utils/create-user.ts index 8b5d157..15a6e7f 100644 --- a/src/tests/utils/create-user.ts +++ b/src/tests/utils/create-user.ts @@ -7,20 +7,20 @@ async function signUpNewUser (): Promise { // To avoid conflicts with existing usernames or emails const username = faker.internet.userName({ lastName: 'doe' }).toLowerCase() const email = faker.internet.email() - const password = faker.internet.password() + const password = faker.internet.password() + '@1' await request(app).post('/user/signup').send({ username, email, password - }) + }).expect(200) const response = await request(app) .post('/user/auth') .send({ email, password - }) + }).expect(200) return { username, diff --git a/src/tests/utils/delete-user.ts b/src/tests/utils/delete-user.ts new file mode 100644 index 0000000..95bcb47 --- /dev/null +++ b/src/tests/utils/delete-user.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import prisma from '../../clients/prisma-client' + +export default async function deleteUser (username: string) { + await prisma.post.deleteMany({ + where: { + author: { + username + } + } + }) + + await prisma.user.deleteMany({ + where: { + username + } + }) +} diff --git a/tsconfig.json b/tsconfig.json index c5e81ec..eb99b44 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,7 +29,8 @@ /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ "typeRoots": [ - "src/@types", + "src/@types/express.d.ts", + "src/@types/global.d.ts", "node_modules/@types" ], /* Specify multiple folders that act like './node_modules/@types'. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */