Created new routes. Fixed small bug in the tests.

This commit is contained in:
Hackntosh 2023-08-22 13:52:50 -03:00
parent bb9532c265
commit 5057f58a12
20 changed files with 172 additions and 75 deletions

View file

@ -1,3 +0,0 @@
{
"json.schemaDownload.enable": true
}

View file

@ -10,6 +10,9 @@ import router from './routes'
const app = express()
// TODO: test socket io, emit notifications when create one.
// TODO: test update name, email and password routes.
// TODO: start to create the client, or a barebone to test socket io
// TODO: see how we create authentication with socket io.
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

View file

@ -29,6 +29,8 @@ usersRouter.put(
uploadFile,
user.uploadPicture
)
usersRouter.put('/update', authenticated, user.update)
usersRouter.put('/update-email', authenticated, user.updateEmail)
usersRouter.put('/update-name', authenticated, user.updateName)
usersRouter.put('/update-password', authenticated, user.updatePassword)
export default usersRouter

View file

@ -7,7 +7,9 @@ import userLikeCommentController from './like-comment'
import userLikePostController from './like-post'
import userSearchController from './search-user'
import userSignupController from './signup'
import userUpdateController from './update'
import userUpdateEmailController from './update-email'
import userUpdateNameController from './update-name'
import userUpdatePasswordController from './update-password'
import userUploadPictureController from './upload-picture'
const user = {
@ -20,7 +22,9 @@ const user = {
likePost: userLikePostController,
searchUser: userSearchController,
signup: userSignupController,
update: userUpdateController,
updateEmail: userUpdateEmailController,
updateName: userUpdateNameController,
updatePassword: userUpdatePasswordController,
uploadPicture: userUploadPictureController
} as const

View file

@ -2,16 +2,16 @@ import user from 'services/users'
import type { Request, Response } from 'express'
import handleResponse from 'helpers/handle-response'
async function userUpdateController (
async function userUpdateEmailController (
req: Request,
res: Response
): Promise<void> {
const { email, displayName, username } = req.body
const { email } = req.body
const id = req.user?.id ?? ''
const result = await user.update({ id, email, displayName, username })
const result = await user.updateEmail({ id, email })
handleResponse(res, result)
}
export default userUpdateController
export default userUpdateEmailController

View file

@ -0,0 +1,17 @@
import user from 'services/users'
import type { Request, Response } from 'express'
import handleResponse from 'helpers/handle-response'
async function userUpdateNameController (
req: Request,
res: Response
): Promise<void> {
const { displayName, username } = req.body
const id = req.user?.id ?? ''
const result = await user.updateName({ id, displayName, username })
handleResponse(res, result)
}
export default userUpdateNameController

View file

@ -0,0 +1,17 @@
import user from 'services/users'
import type { Request, Response } from 'express'
import handleResponse from 'helpers/handle-response'
async function userUpdatePasswordController (
req: Request,
res: Response
): Promise<void> {
const { currentPassword, newPassword } = req.body
const id = req.user?.id ?? ''
const result = await user.updatePassword(id, currentPassword, newPassword)
handleResponse(res, result)
}
export default userUpdatePasswordController

View file

@ -7,7 +7,9 @@ import userLikeCommentService from './like-comment'
import userLikePostService from './like-post'
import userSearchService from './search-user'
import userSignupService from './signup'
import userUpdateService from './update'
import userUpdateEmailService from './update-email'
import userUpdateNameService from './update-name'
import userUpdatePasswordService from './update-password'
import userUploadPictureService from './upload-picture'
const user = {
@ -20,7 +22,9 @@ const user = {
likePost: userLikePostService,
searchUser: userSearchService,
signup: userSignupService,
update: userUpdateService,
updateEmail: userUpdateEmailService,
updateName: userUpdateNameService,
updatePassword: userUpdatePasswordService,
uploadPicture: userUploadPictureService
} as const

View file

@ -21,10 +21,6 @@ async function userSignupService ({
)
}
if (password.trim().length < 8) {
return new Error('Password too short')
}
if (!usernameRegex.test(username)) {
return new Error(
'Username not allowed. Only alphanumerics characters (uppercase and lowercase words), underscore, dots and it must be between 5 and 15 characters'

View file

@ -0,0 +1,42 @@
import type User from 'interfaces/user'
import prisma from 'clients/prisma-client'
async function userUpdateEmailService ({
id,
email
}: User): Promise<Record<string, unknown> | Error> {
const user = await prisma.user.findFirst({ where: { id } })
if (user === null) {
return new Error('User not found')
}
if (user.id !== id) {
return new Error('Forbidden')
}
if (email !== undefined && email.trim() !== user.email) {
const existingUser = await prisma.user.findFirst({ where: { email } })
if (existingUser != null && existingUser.email !== user.email) {
return new Error('Email already in use')
}
}
await prisma.user.update({
where: {
id
},
data: {
email: email ?? user.email
},
select: {
displayName: true,
username: true,
createdAt: true
}
})
return { message: 'Successfully updated user email' }
}
export default userUpdateEmailService

View file

@ -1,9 +1,8 @@
import type User from 'interfaces/user'
import prisma from 'clients/prisma-client'
async function userUpdateService ({
async function userUpdateNameService ({
id,
email,
displayName,
username
}: User): Promise<Record<string, unknown> | Error> {
@ -13,15 +12,8 @@ async function userUpdateService ({
return new Error('User not found')
}
// Check if the provided email or username is different from the current user's data
// if they are different, additional queries are made to check if the new email or username is already in use.
if (email !== undefined && email.trim() !== user.email) {
const existingUser = await prisma.user.findFirst({ where: { email } })
if (existingUser != null && existingUser.email !== user.email) {
return new Error('Email already in use')
}
}
// Check if the provided username is different from the current user's data
// if different, queries are made to check if the new username is already in use.
if (username !== undefined && username.trim() !== user.username) {
const existingUser = await prisma.user.findFirst({ where: { username } })
@ -34,14 +26,11 @@ async function userUpdateService ({
return new Error('Forbidden')
}
// TODO: /user/change-password | /user/change-email
const updatedUser = await prisma.user.update({
where: {
id
},
data: {
email: email ?? user.email,
displayName: displayName ?? user.displayName,
username: username ?? user.username
},
@ -55,4 +44,4 @@ async function userUpdateService ({
return updatedUser
}
export default userUpdateService
export default userUpdateNameService

View file

@ -0,0 +1,56 @@
import * as bcrypt from 'bcrypt'
import prisma from 'clients/prisma-client'
const passwordRegex = /^(?=.*[0-9])(?=.*[!@#$%^&*_])[a-zA-Z0-9!@#$%^&*_]{8,}$/
async function userUpdatePasswordService (
id: string,
currentPassword: string,
newPassword: string
): Promise<Record<string, unknown> | Error> {
if (!passwordRegex.test(newPassword)) {
return new Error(
'New password must have at least 8 characters, one number and one special character.'
)
}
const user = await prisma.user.findFirst({ where: { id } })
if (user === null) {
return new Error('User not found')
}
if (user.id !== id) {
return new Error('Forbidden')
}
const validPassword = await bcrypt.compare(
currentPassword.replace(/ /g, ''),
user.password
)
if (!validPassword) {
return new Error('Invalid password')
}
const salt = await bcrypt.genSalt(15)
const hashedPassword = await bcrypt.hash(newPassword.replace(/ /g, ''), salt)
await prisma.user.update({
where: {
id
},
data: {
password: hashedPassword
},
select: {
displayName: true,
username: true,
createdAt: true
}
})
return { message: 'Successfully updated user password' }
}
export default userUpdatePasswordService

View file

@ -12,7 +12,8 @@ describe('POST /post/create', () => {
})
afterAll(async () => {
await deleteUser(user.username ?? '')
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await deleteUser(user.username!)
})
it('should respond with 200 status code if the user send the token and the content', async () => {

View file

@ -12,7 +12,8 @@ describe('DELETE /post/delete', () => {
})
afterAll(async () => {
await deleteUser(user.username ?? '')
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await deleteUser(user.username!)
})
it('should delete the post successfully', async () => {

View file

@ -26,7 +26,8 @@ describe('POST /post/info', () => {
})
afterAll(async () => {
await deleteUser(user.username ?? '')
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await deleteUser(user.username!)
})
it('should respond with 200 status code and return some info about the post', async () => {

View file

@ -12,7 +12,8 @@ describe('PUT /post/update', () => {
})
afterAll(async () => {
await deleteUser(user.username ?? '')
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await deleteUser(user.username!)
})
it('should create a new post and update the content of it', async () => {

View file

@ -12,7 +12,8 @@ describe('POST /user/auth', () => {
})
afterAll(async () => {
await deleteUser(user.username ?? '')
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await deleteUser(user.username!)
})
it('should respond with a error if the user does not exists', async () => {

View file

@ -12,7 +12,8 @@ describe('POST /user/info', () => {
})
afterAll(async () => {
await deleteUser(user.username ?? '')
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await deleteUser(user.username!)
})
it('should respond with 200 status code and return the user data', async () => {

View file

@ -13,7 +13,8 @@ describe('POST /user/signup', () => {
})
afterAll(async () => {
await deleteUser(user.username ?? '')
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await deleteUser(user.username!)
})
it('should respond with a 400 status code if sent any invalid data', async () => {

View file

@ -1,37 +0,0 @@
import request from 'supertest'
import app from '../../app'
import signUpNewUser from '../utils/create-user'
import deleteUser from '../utils/delete-user'
import type User from 'interfaces/user'
let user: User
describe('PUT /user/update', () => {
beforeAll(async () => {
user = await signUpNewUser()
})
afterAll(async () => {
await deleteUser(user.username ?? '')
})
it('should update the user successfully', async () => {
const fieldsToUpdate = {
displayName: 'Cookie'
}
const response = await request(app)
.put('/user/update')
.send(fieldsToUpdate)
.set('Authorization', `Bearer ${user.token ?? ''}`)
.expect(200)
expect(response.body).toEqual(
expect.objectContaining({
displayName: expect.any(String),
username: expect.any(String),
createdAt: expect.any(String)
})
)
})
})