diff --git a/app.mock.test.js b/app.mock.test.js index 79b9449..6f3cae4 100644 --- a/app.mock.test.js +++ b/app.mock.test.js @@ -1,59 +1,97 @@ -const createApp = require('./app') -const request = require('supertest') -const validateUsername = require('./validation/validateUsername') -const validatePassword = require('./validation/validatePassword') - -//Mock validateEmail to isolate tests -jest.mock('./validation/validateEmail', () => { - return jest.fn((email) => { - //Simulate real world simulation - if (!email || typeof email !== 'string') return false; - const re = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i; - return re.test(email); - }) -}) - -const validateEmail = require('./validation/validateEmail') -const app = createApp(validateUsername, validatePassword, validateEmail) - -describe('given correct username and password', () => { - test('return status 200', async () => { - const response = await request(app).post('/users').send({ - username: 'Username', - password: 'Password123', - email: 'student@example.com' - }) - expect(response.statusCode).toBe(200) - }) - - test('returns userId', async () => { - const response = await request(app).post('/users').send({ - username: 'Username', - password: 'Password123', - email: 'student@example.com' - }) - expect(response.body.userId).toBeDefined(); - }) - - // test response content type? - // test response message - // test response user id value - // ... -}) - -describe('given incorrect or missing username and password', () => { - test('return status 400', async () => { - const response = await request(app).post('/users').send({ - username: 'user', - password: 'password', - email: 'not-an-email' - }) - expect(response.statusCode).toBe(400) - }) - - // test response message - // test that response does NOT have userId - // test incorrect username or password according to requirements - // test missing username or password - // ... -}) \ No newline at end of file +const createApp = require('./app') +const request = require('supertest') +const validateUsername = require('./validation/validateUsername') +const validatePassword = require('./validation/validatePassword') + +jest.mock('./validation/validateEmail', () => { + return jest.fn((email) => { + if (!email || typeof email !== 'string') return false + const re = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i + return re.test(email) + }) +}) + +const validateEmail = require('./validation/validateEmail') +const app = createApp(validateUsername, validatePassword, validateEmail) + +describe('POST /users', () => { + test('returns 200 and the expected payload for a valid user', async () => { + const response = await request(app).post('/users').send({ + username: 'User.name123', + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(200) + expect(response.headers['content-type']).toMatch(/json/) + expect(response.body).toEqual({ + userId: '1', + message: 'Valid User' + }) + }) + + test('accepts boundary values that still satisfy the requirements', async () => { + const response = await request(app).post('/users').send({ + username: 'User.1', + password: 'Abcdefg1', + email: 'name@mail.example.org' + }) + + expect(response.statusCode).toBe(200) + expect(response.body.message).toBe('Valid User') + }) + + test.each([ + ['username is shorter than 6 characters', { + username: 'user1', + password: 'Password123', + email: 'student@example.com' + }], + ['username contains invalid characters', { + username: 'user!name', + password: 'Password123', + email: 'student@example.com' + }], + ['password is shorter than 8 characters', { + username: 'Username', + password: 'Pass12A', + email: 'student@example.com' + }], + ['password is missing an uppercase letter', { + username: 'Username', + password: 'password123', + email: 'student@example.com' + }], + ['password is missing a number', { + username: 'Username', + password: 'Password', + email: 'student@example.com' + }], + ['password contains special characters', { + username: 'Username', + password: 'Password123!', + email: 'student@example.com' + }], + ['email is missing the @ symbol', { + username: 'Username', + password: 'Password123', + email: 'studentexample.com' + }], + ['email is missing a valid domain extension', { + username: 'Username', + password: 'Password123', + email: 'student@example' + }], + ['username is missing entirely', { + password: 'Password123', + email: 'student@example.com' + }] + ])('returns 400 when %s', async (_description, payload) => { + const response = await request(app).post('/users').send(payload) + + expect(response.statusCode).toBe(400) + expect(response.headers['content-type']).toMatch(/json/) + expect(response.body).toEqual({ error: 'Invalid User' }) + expect(response.body.userId).toBeUndefined() + }) +}) diff --git a/app.test.js b/app.test.js index f1b561d..dcf7afe 100644 --- a/app.test.js +++ b/app.test.js @@ -2,48 +2,88 @@ const createApp = require('./app') const request = require('supertest') const validateUsername = require('./validation/validateUsername') const validatePassword = require('./validation/validatePassword') -const validateEmail = require('./validation/validateEmail') - -const app = createApp(validateUsername, validatePassword, validateEmail) - -describe('given correct username and password', () => { - test('return status 200', async () => { - const response = await request(app).post('/users').send({ - username: 'Username', - password: 'Password123', - email: 'student@example.com' - }) - expect(response.statusCode).toBe(200) - }) - - test('returns userId', async () => { - const response = await request(app).post('/users').send({ - username: 'Username', - password: 'Password123', - email: 'student@example.com' - }) - expect(response.body.userId).toBeDefined(); - }) - - // test response content type? - // test response message - // test response user id value - // ... -}) - -describe('given incorrect or missing username and password', () => { - test('return status 400', async () => { - const response = await request(app).post('/users').send({ - username: 'user', - password: 'password', - email: 'not-an-email' - }) - expect(response.statusCode).toBe(400) - }) - - // test response message - // test that response does NOT have userId - // test incorrect username or password according to requirements - // test missing username or password - // ... -}) \ No newline at end of file +const validateEmail = require('./validation/validateEmail') + +const app = createApp(validateUsername, validatePassword, validateEmail) + +describe('POST /users', () => { + test('returns 200 and the expected payload for a valid user', async () => { + const response = await request(app).post('/users').send({ + username: 'User.name123', + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(200) + expect(response.headers['content-type']).toMatch(/json/) + expect(response.body).toEqual({ + userId: '1', + message: 'Valid User' + }) + }) + + test('accepts boundary values that still satisfy the requirements', async () => { + const response = await request(app).post('/users').send({ + username: 'User.1', + password: 'Abcdefg1', + email: 'name@mail.example.org' + }) + + expect(response.statusCode).toBe(200) + expect(response.body.message).toBe('Valid User') + }) + + test.each([ + ['username is shorter than 6 characters', { + username: 'user1', + password: 'Password123', + email: 'student@example.com' + }], + ['username contains invalid characters', { + username: 'user!name', + password: 'Password123', + email: 'student@example.com' + }], + ['password is shorter than 8 characters', { + username: 'Username', + password: 'Pass12A', + email: 'student@example.com' + }], + ['password is missing an uppercase letter', { + username: 'Username', + password: 'password123', + email: 'student@example.com' + }], + ['password is missing a number', { + username: 'Username', + password: 'Password', + email: 'student@example.com' + }], + ['password contains special characters', { + username: 'Username', + password: 'Password123!', + email: 'student@example.com' + }], + ['email is missing the @ symbol', { + username: 'Username', + password: 'Password123', + email: 'studentexample.com' + }], + ['email is missing a valid domain extension', { + username: 'Username', + password: 'Password123', + email: 'student@example' + }], + ['username is missing entirely', { + password: 'Password123', + email: 'student@example.com' + }] + ])('returns 400 when %s', async (_description, payload) => { + const response = await request(app).post('/users').send(payload) + + expect(response.statusCode).toBe(400) + expect(response.headers['content-type']).toMatch(/json/) + expect(response.body).toEqual({ error: 'Invalid User' }) + expect(response.body.userId).toBeUndefined() + }) +}) diff --git a/validation/validatePassword.js b/validation/validatePassword.js index 44d8a8b..ab0bebe 100644 --- a/validation/validatePassword.js +++ b/validation/validatePassword.js @@ -1,7 +1,11 @@ -function validatePassword(password) { - const validLength = password.length >= 8; - const hasNumber = /[0-9]/g.test(password); - const hasUpperCaseLetters = /[A-Z]/g.test(password); +function validatePassword(password) { + if (!password || typeof password !== 'string') { + return false; + } + + const validLength = password.length >= 8; + const hasNumber = /[0-9]/g.test(password); + const hasUpperCaseLetters = /[A-Z]/g.test(password); const hasLowerCaseLetters = /[a-z]/g.test(password); const hasSpecialCharacters = /[\W]/g.test(password); diff --git a/validation/validateUsername.js b/validation/validateUsername.js index 30213b1..81d6fdb 100644 --- a/validation/validateUsername.js +++ b/validation/validateUsername.js @@ -1,7 +1,11 @@ -function validateUsername(username) { - const validLength = username.length >= 6 && username.length <=30; - const allowedcharacters = /^[a-zA-Z0-9.]+$/g.test(username); - - return validLength && allowedcharacters; -} +function validateUsername(username) { + if (!username || typeof username !== 'string') { + return false; + } + + const validLength = username.length >= 6 && username.length <=30; + const allowedcharacters = /^[a-zA-Z0-9.]+$/g.test(username); + + return validLength && allowedcharacters; +} module.exports = validateUsername;