Skip to content

Commit

Permalink
Wrote tests for the register user endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
OomsOoms committed Jun 10, 2024
1 parent 4f4525c commit 9437f10
Show file tree
Hide file tree
Showing 8 changed files with 737 additions and 11 deletions.
536 changes: 535 additions & 1 deletion server/package-lock.json

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
"description": "",
"main": "src/index.js",
"scripts": {
"test": "jest --coverage",
"test": "jest --coverage --detectOpenHandles",
"test:watch": "jest --watchAll",
"start": "node src/index",
"dev": "nodemon src/index",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"jest": {
"testEnvironment": "node"
},
"keywords": [],
"author": "",
"license": "ISC",
Expand All @@ -34,6 +37,8 @@
"eslint-plugin-prettier": "^5.1.3",
"globals": "^15.3.0",
"jest": "^29.7.0",
"nodemon": "^3.0.3"
"mongodb-memory-server": "^9.2.0",
"nodemon": "^3.0.3",
"supertest": "^7.0.0"
}
}
}
4 changes: 2 additions & 2 deletions server/src/api/helpers/sendEmail.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//const nodemailer = require('nodemailer');

// im getting spammed so much so im just loggin the email
module.exports = function (to, subject, text) {
//const transporter = nodemailer.createTransport({
// service: 'Gmail',
Expand All @@ -13,7 +12,7 @@ module.exports = function (to, subject, text) {
// },
//});
const mailOptions = {
//from: "", // If not set, it will be sent from the default email of the gmail account
from: process.env.FROM_ADDRESS, // If not set, it will be sent from the default email of the gmail account
to: to,
subject: subject,
text: text,
Expand All @@ -25,5 +24,6 @@ module.exports = function (to, subject, text) {
// console.log('Email sent: ', info.response);
// }
//});
// Log them because we don't want to send emails in development
console.log(mailOptions);
};
31 changes: 31 additions & 0 deletions server/src/api/helpers/tests/passwordUtils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const crypto = require('crypto');

const { hashPassword, comparePasswords } = require('../passwordUtils');

const passwordLengths = [10, 100, 1000, 10000, 100000, 1000000];

describe('Performance of hashPassword function', () => {
passwordLengths.forEach((passwordLength) => {
test(`Performance for password of length ${passwordLength}`, async () => {
const password = crypto.randomBytes(passwordLength).toString('hex');
console.time(`hashPassword for length ${passwordLength}`);
hashPassword(password);
console.timeEnd(`hashPassword for length ${passwordLength}`);
});
});
});

describe('Performance of comparePasswords function', () => {
passwordLengths.forEach((passwordLength) => {
test(`Performance for passwords of length ${passwordLength}`, async () => {
const password = crypto.randomBytes(passwordLength).toString('hex');
const hashedPassword = await hashPassword(password);

console.time(`comparePasswords for length ${passwordLength}`);
comparePasswords(password, hashedPassword);
console.timeEnd(`comparePasswords for length ${passwordLength}`);
});
});
});


2 changes: 1 addition & 1 deletion server/src/api/services/user.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async function registerUser(username, email, password) {
return {
success: true,
message:
'User created, email verification link sent and will expire in 10 minutes',
'User created successfully, email verification link sent and will expire in 10 minutes',
user: {
_id: user._id,
username: user.username,
Expand Down
19 changes: 15 additions & 4 deletions server/src/api/validations/user.validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const registerUser = [
.isLength({ min: 3 })
.withMessage('Username must be at least 3 characters long')
.bail()
.isLength({ max: 20 })
.withMessage('Username must be at most 20 characters long')
.matches(/^[a-z0-9_.-]+$/)
.withMessage(
'Username can only contain lowercase letters, numbers, underscores, hyphens, and full stops'
Expand All @@ -22,20 +24,29 @@ const registerUser = [
.bail()
.trim()
.escape()
.isEmail()
.isLength({ min: 6 })
.withMessage('Email must be at least 6 characters long')
.bail()
.isLength({ max: 71 })
.withMessage('Email must be at most 71 characters long')
.bail()
.isEmail() // this also checks the length so the values I picked were based off of this, just so i can get better error messages
.normalizeEmail()
.withMessage('Invalid email format')
.bail(),
body('password')
.trim()
.escape()
.isString()
.bail()
.notEmpty()
.withMessage('Password is required')
.bail()
.isLength({ min: 8 })
.withMessage('Password must be at least 8 characters long')
.bail()
.trim()
.escape()
.isString()
.isLength({ max: 128 })
.withMessage('Password must be at most 128 characters long')
.bail(),
];

Expand Down
2 changes: 2 additions & 0 deletions server/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ mongoose.connection.once('open', () => {
console.log(`Server is running on http://localhost:${PORT}/`);
});
});

module.exports = app;
143 changes: 143 additions & 0 deletions server/tests/registerUser.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
const request = require('supertest');
const app = require('../src/index'); // Assuming your app is defined in a separate file
const crypto = require('crypto');
const { MongoMemoryServer } = require('mongodb-memory-server');
const mongoose = require('mongoose');

async function dbConnect() {
const mongod = await MongoMemoryServer.create();
const uri = mongod.getUri();
console.log(uri);
}
async function dbDisconnect() {
await mongoose.connection.dropDatabase();
await mongoose.connection.close();
}

// Runs once before all tests
beforeAll(async () => dbConnect());
// Runs once after all tests
afterAll(async () => dbDisconnect());

describe('POST /api/users', () => {
// normal test
it('should successfully register a new user', async () => {
const response = await request(app)
.post('/api/users')
.send({
username: 'testuser',
email: '[email protected]',
password: 'testpassword',
});
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('message', 'User created successfully, email verification link sent and will expire in 10 minutes');
});

// erroneous test
it('should fail to register a new user with duplicate username', async () => {
const response = await request(app)
.post('/api/users')
.send({
username: 'testuser',
email: '[email protected]',
password: 'testpassword',
});
expect(response.status).toBe(409);
expect(response.body).toHaveProperty('error', 'Username already exists');
});

// erroneous test
it('should fail to register a new user with duplicate email', async () => {
const response = await request(app)
.post('/api/users')
.send({
username: 'testuser2',
email: '[email protected]',
password: 'testpassword',
});
expect(response.status).toBe(409);
expect(response.body).toHaveProperty('error', 'Email already exists');
});

// erroneous test
it('should fail to register a new user with missing fields', async () => {
const response = await request(app)
.post('/api/users')
.send({
username: 'testuser',
password: 'testpassword',
});
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('errors');
expect(response.body.errors).toBeInstanceOf(Array);
expect(response.body.errors[0]).toHaveProperty('msg', 'Email is required');
});

// erroneous test
it('should fail to register a new user with details that are too short', async () => {
const response = await request(app)
.post('/api/users')
.send({
username: '12',
email: '[email protected]',
password: '1234567',
});
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('errors');
expect(response.body.errors).toBeInstanceOf(Array);
expect(response.body.errors[0]).toHaveProperty('msg', 'Username must be at least 3 characters long');
expect(response.body.errors[1]).toHaveProperty('msg', 'Email must be at least 6 characters long');
expect(response.body.errors[2]).toHaveProperty('msg', 'Password must be at least 8 characters long');
});

// boundary test
it('should successfully register a new user with minimum details', async () => {
const response = await request(app)
.post('/api/users')
.send({
username: '123',
email: '[email protected]',
password: '12345678',
});
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('message', 'User created successfully, email verification link sent and will expire in 10 minutes');
});

// boundary test
it('should successfully register a new user with maximum details', async () => {
const localPart = crypto.randomBytes(32).toString('hex'); // 32 bytes * 2 characters per byte = 64 characters
const domainPart = '12.com'; // 6 characters
const email = `${localPart}@${domainPart}`; // 71 characters total
const password = crypto.randomBytes(64).toString('hex'); // 64 bytes * 2 characters per byte = 128 characters total
const response = await request(app)
.post('/api/users')
.send({
username: '12345678901234567890', // 20 characters
email: email,
password: password,
});;
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('message', 'User created successfully, email verification link sent and will expire in 10 minutes');
});

// erroneous test
it('should fail to register with defails that are too long', async () => {
const localPart = crypto.randomBytes(32).toString('hex'); // 32 bytes * 2 characters per byte = 64 characters
const domainPart = '123.com'; // 7 characters
const email = `${localPart}@${domainPart}`; // 72 characters total
const password = crypto.randomBytes(64).toString('hex') + 'a'; // 64 bytes * 2 characters per byte + 1 = 129 characters total
const response = await request(app)
.post('/api/users')
.send({
username: '123456789012345678901', // 21 characters
email: email,
password: password,
});;
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('errors');
expect(response.body.errors).toBeInstanceOf(Array);
expect(response.body.errors[0]).toHaveProperty('msg', 'Username must be at most 20 characters long');
expect(response.body.errors[1]).toHaveProperty('msg', 'Email must be at most 71 characters long');
expect(response.body.errors[2]).toHaveProperty('msg', 'Password must be at most 128 characters long');
});
});

0 comments on commit 9437f10

Please sign in to comment.