Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ft: auth (new user needs to verify one's account before login) #187419164 #65

Merged
merged 1 commit into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions __test__/cart.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,22 @@ describe("testing cart", () => {
name: "admin123",
username: "admin123",
email: "[email protected]",
isVerified:true,
password: await bcrypt.hash("password", 10),
roleId: 3,
};

const testBuyer = {
name: "buyer123",
username: "buyer123",
isVerified:true,
email: "[email protected]",
password: await bcrypt.hash("password", 10),
};
const testSeller = {
name: "seller123",
username: "seller123",
isVerified:true,
email: "[email protected]",
password: await bcrypt.hash("password", 10),
};
Expand Down
3 changes: 3 additions & 0 deletions __test__/payment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe("test stripe api payment", () => {
const testAdmin = {
name: "admin123",
username: "admin123",
isVerified:true,
email: "[email protected]",
password: await bcrypt.hash("password", 10),
roleId: 3,
Expand All @@ -37,13 +38,15 @@ describe("test stripe api payment", () => {
const testBuyer = {
name: "buyer123",
username: "buyer123",
isVerified:true,
email: "[email protected]",
password: await bcrypt.hash("password", 10),
};

const testSeller = {
name: "seller123",
username: "seller123",
isVerified:true,
email: "[email protected]",
password: await bcrypt.hash("password", 10),
};
Expand Down
48 changes: 40 additions & 8 deletions __test__/product.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ import User from "../src/sequelize/models/users";
import bcrypt from "bcryptjs";
import { Role } from "../src/sequelize/models/roles";
import redisClient from "../src/config/redis";
import { response } from "express";
import { placeOrder } from "../src/services/payment.service";
import Cart from "../src/sequelize/models/Cart";
import CartItem from "../src/sequelize/models/CartItem";
import OrderItem from "../src/sequelize/models/orderItems";
import * as userService from "../src/services/user.service"
import { generateVerificationToken } from "../src/utils/generateResetToken";

jest.mock("../src/services/mail.service", () => ({
sendEmailService: jest.fn(),
Expand All @@ -25,6 +24,7 @@ jest.mock("../src/services/mail.service", () => ({
const userData: any = {
name: "yvanna",
username: "testuser",
isVerified:true,
email: "[email protected]",
role:"seller",
password: "test1234",
Expand All @@ -41,6 +41,7 @@ const product:any = {
name: "pens",
images: ["image1.jpg", "image2.jpg", "image3.jpg", "image4.jpg"],
stockQuantity: 8,

price: 5000,
discount: 3.5,
categoryID: 1,
Expand All @@ -49,6 +50,7 @@ const product:any = {
const dummyBuyer = {
name: "test user",
username: "testUser",
isVerified:true,
email: "[email protected]",
password: "soleil00",
}
Expand Down Expand Up @@ -82,8 +84,7 @@ describe("Testing product Routes", () => {
])

await User.create(testAdmin);

const dummy = await request(app).post("/api/v1/users/register").send(dummySeller);
// await User.create(dummySeller);
await Product.destroy({});
await Category.destroy({truncate:true});
} catch (error) {
Expand All @@ -97,6 +98,7 @@ describe("Testing product Routes", () => {
await sequelize.close();
await redisClient.quit()
});

test("should return 201 and create a new user when registering successfully", async () => {
const response = await request(app)
.post("/api/v1/users/register")
Expand All @@ -110,7 +112,20 @@ describe("Testing product Routes", () => {
.send(dummyBuyer);
expect(response.status).toBe(201);
})
test('should return 201 and register a dummy buyer user', async () => {
const response = await request(app)
.post("/api/v1/users/register")
.send(dummySeller);
expect(response.status).toBe(201);
})
let buyerToken: any;
it("It should verify user account.",async()=>{
const token = generateVerificationToken('[email protected]', 60);
const response = await request(app)
.get(`/api/v1/users/verify-user?token=${token}`)
expect(response.status).toBe(200)
expect(response.body.message).toBe('User verified successfully.')
},60000)

test("should login an buyer", async () =>{
const response = await request(app).post("/api/v1/users/login").send({
Expand All @@ -127,21 +142,37 @@ describe("Testing product Routes", () => {
.send(product)
expect(response.status).toBe(401);
},2000);


it("It should verify user account.",async()=>{
const token = generateVerificationToken('[email protected]', 60);
const response = await request(app)
.get(`/api/v1/users/verify-user?token=${token}`)
expect(response.status).toBe(200)
expect(response.body.message).toBe('User verified successfully.')
},60000)
test("should login an Admin", async () =>{
const response = await request(app).post("/api/v1/users/login").send({
email: "[email protected]",
password: "password"
password: "password"
})
adminToken = response.body.token;
expect(response.status).toBe(200)
});
it("It should verify user account.",async()=>{
const token = generateVerificationToken(dummySeller.email, 60);
const response = await request(app)
.get(`/api/v1/users/verify-user?token=${token}`)
expect(response.status).toBe(200)
expect(response.body.message).toBe('User verified successfully.')
},60000)

test("should update dummyseller's role to seller", async () => {
const logDummySeller = await request(app).post("/api/v1/users/login").send({
email: dummySeller.email,
password: dummySeller.password,
});
expect(logDummySeller.status).toBe(200);
// expect(logDummySeller.status).toBe(200);
expect(logDummySeller.body.message).toBe('Logged in')
token = logDummySeller.body.token;

const seller = await userService.getUserByEmail(dummySeller.email)
Expand All @@ -154,6 +185,7 @@ describe("Testing product Routes", () => {
})
.set("Authorization", "Bearer " + adminToken);
expect(response.status).toBe(200);
expect(response.body.message).toBe('User role updated successfully');

});

Expand Down Expand Up @@ -482,7 +514,7 @@ test('It should return status 200 for removed category',async() =>{
})
it("changing product availability of product which does not exist", async ()=>{
const response = await request(app)
.patch(`/api/v1/products/${91}/status`)
.patch(`/api/v1/products/${4444444}/status`)
.set("Authorization", "Bearer " + token);
expect(response.body.message).toBe('Product not found')
})
Expand Down
52 changes: 50 additions & 2 deletions __test__/user.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { QueryTypes } from "sequelize";
// import redisClient from "../src/config/redis";
import Redis from "ioredis";
import { env } from "../src/utils/env";
import { generateResetToken } from "../src/utils/generateResetToken";
import { generateResetToken, generateVerificationToken } from "../src/utils/generateResetToken";

let redisClient: any;

Expand All @@ -43,6 +43,7 @@ const dummySeller = {
};
const userTestData = {
newPassword: "Test@123",
isVerified:true,
confirmPassword: "Test@123",
wrongPassword: "Test456",
};
Expand Down Expand Up @@ -114,6 +115,14 @@ describe("Testing user Routes", () => {
expect(response.status).toBe(201);
}, 20000);

it("It should verify user account.",async()=>{
const token = generateVerificationToken(userData.email, 60);
const response = await request(app)
.get(`/api/v1/users/verify-user?token=${token}`)
expect(response.status).toBe(200)
expect(response.body.message).toBe('User verified successfully.')
},60000)


test("should return 409 when registering with an existing email", async () => {
User.create(userData);
Expand Down Expand Up @@ -143,6 +152,7 @@ describe("Testing user Routes", () => {
password: userData.password,
});
expect(response.status).toBe(200);
expect(response.body.message).toBe("Logged in");
token = response.body.token;
});

Expand Down Expand Up @@ -209,21 +219,36 @@ describe("Testing user Routes", () => {
expect(response.body.status).toBe(401);
spyonOne.mockRestore();
}, 20000);
it("It should verify user account.",async()=>{
const token = generateVerificationToken('[email protected]', 60);
const response = await request(app)
.get(`/api/v1/users/verify-user?token=${token}`)
expect(response.status).toBe(200)
expect(response.body.message).toBe('User verified successfully.')
},60000)

test("should login an Admin", async () =>{
const response = await request(app).post("/api/v1/users/login").send({
email: "[email protected]",
password: "password"
password: "password"
})
adminToken = response.body.token;
});
it("It should verify user account.",async()=>{
const token = generateVerificationToken(dummySeller.email, 60);
const response = await request(app)
.get(`/api/v1/users/verify-user?token=${token}`)
expect(response.status).toBe(200)
expect(response.body.message).toBe('User verified successfully.')
},60000)

test("should update dummyseller's role to seller", async () => {
const logDummySeller = await request(app).post("/api/v1/users/login").send({
email: dummySeller.email,
password: dummySeller.password,
});
expect(logDummySeller.status).toBe(200);
expect(logDummySeller.body.message).toBe("Logged in");
const seller = await userServices.getUserByEmail(dummySeller.email);
const dummySellerId = seller?.id;

Expand All @@ -235,6 +260,7 @@ describe("Testing user Routes", () => {
.set("Authorization", "Bearer " + adminToken);

expect(response.status).toBe(200);
// expect(response.body.message).toBe('User role updated successfully');

});

Expand Down Expand Up @@ -563,6 +589,28 @@ describe('Patch /api/v1/users/reset-password', () => {
},60000);
});

describe("Verifying user account",()=>{
it("It should verify user account.",async()=>{
await User.create(userData)
const token = generateVerificationToken(userData.email, 60);
const response = await request(app)
.get(`/api/v1/users/verify-user?token=${token}`)
expect(response.status).toBe(200)
expect(response.body.message).toBe('User verified successfully.')
},60000)

it("It should send a verification link.",async()=>{
const response = await request(app)
.post('/api/v1/users/verify-user-email')
.send({
email:userData.email
})
expect(response.status).toBe(409)
expect(response.body.message).toBe("User is already verified.")
},60000)

})

afterAll(async () => {
try {
await sequelize.query('TRUNCATE TABLE profiles, users CASCADE');
Expand Down
30 changes: 29 additions & 1 deletion src/controllers/userControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const createUserController = async (req: Request, res: Response) => {
}
return res.status(201).json({
status: 201,
message: "User successfully created."
message: "User successfully created. check your inbox to verify your email"
});
} catch (err: any) {
if (err.name === "UnauthorizedError" && err.message === "User already exists") {
Expand Down Expand Up @@ -194,6 +194,7 @@ export const handleSuccess = async (req: Request, res: Response) => {
name: user.displayName,
email: user.emails[0].value,
username: user.name.familyName,
isVerified:true,
//@ts-ignore
password: null
});
Expand Down Expand Up @@ -405,3 +406,30 @@ export const resetPasswordController = async (req: Request, res: Response): Prom
return res.status(500).json({ message: 'Internal server error.' });
}
};

export const verifyUserEmailController = async (req: Request, res: Response) => {
try {
// Extract email from the request body
const {email} = req.body;

const { error } = Emailschema.validate(req.body);
if (error) {
const cleanErrorMessage = error.details.map(detail => detail.message.replace(/['"]/g, '').trim()).join(', ')
return res.status(400).json({ message: cleanErrorMessage });
}
const result = await userService.verifyUserEmail(email);
res.status(result.status).json({ message: result.message });
} catch (error) {
res.status(500).json({ error: 'Failed to send verification email.' });
}
};
export const verifyUserController = async (req: Request, res: Response): Promise<void> => {
try {
// Extract token from the request query
const token = req.query.token as string;
const result = await userService.verifyNewUser(token);
res.status(result.status).json({ message: result.message });
} catch (error) {
res.status(500).json({ error: 'Failed to verify new user.' });
}
};
5 changes: 4 additions & 1 deletion src/docs/swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
sendResetLink,
updateForgotPassword,
verifyUserAccessToken,
verifyUserEmail,
} from "./users";
import {
getProducts,
Expand All @@ -39,7 +40,6 @@ import { getCategories, addCategories, getSingleCategory, updateCategories, dele
deleteRole
} from "./roledoc";
import { AddToWishes, deleteWish, getWishes, getWishesByProduct, wishSchema } from "./wishes";
import { joinChats } from "./chats";
import { addItemToCartDoc, clearAllProductFromCartDoc, removeProductFromCartDoc, updateProductQuantityDoc, viewCartDoc } from "./cart";
import { getAllNotifications, readNotification } from "./notifications";
import { homepage } from "./home";
Expand Down Expand Up @@ -115,6 +115,9 @@ const options = {
"/api/v1/users/reset-password": {
patch: updateForgotPassword,
},
"/api/v1/users/verify-user-email": {
post: verifyUserEmail,
},

"/api/v1/users/me": {
post: verifyUserAccessToken,
Expand Down
Loading
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.