diff --git a/app.mock.test.js b/app.mock.test.js index 79b9449..5143426 100644 --- a/app.mock.test.js +++ b/app.mock.test.js @@ -1,59 +1,273 @@ const createApp = require('./app') const request = require('supertest') + +// 1. Tell Jest to replace these modules with mock functions +jest.mock('./validation/validateUsername') +jest.mock('./validation/validatePassword') +jest.mock('./validation/validateEmail') + +// 2. Import the mocked versions const validateUsername = require('./validation/validateUsername') const validatePassword = require('./validation/validatePassword') +const validateEmail = require('./validation/validateEmail') + +const app = createApp(validateUsername, validatePassword, validateEmail) + +describe('Mocked Business requirements - Success', () => { + test('should return 200 when all validations pass', async () => { + // Force every mock to say "Yes, this is valid" + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(true); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password123', + email: 'student@example.com' + }) -//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); + expect(response.statusCode).toBe(200); + expect(response.body).toEqual({ + message: 'Valid User', + userId: expect.any(String) + }); }) }) -const validateEmail = require('./validation/validateEmail') -const app = createApp(validateUsername, validatePassword, validateEmail) +describe('Business requirements - Username', () => { + + test('Username: should fail if shorter than 6 characters', async () => { + validateUsername.mockReturnValue(false); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(true); -describe('given correct username and password', () => { - test('return status 200', async () => { const response = await request(app).post('/users').send({ - username: 'Username', + username: '12345', password: 'Password123', email: 'student@example.com' }) - expect(response.statusCode).toBe(200) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + expect(response.body.userId).toBeUndefined(); }) - test('returns userId', async () => { + test('Username: should fail if longer than 30 characters', async () => { + validateUsername.mockReturnValue(false); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(true); + const response = await request(app).post('/users').send({ - username: 'Username', + username: 'a'.repeat(31), // Generates a 31-char string password: 'Password123', email: 'student@example.com' }) - expect(response.body.userId).toBeDefined(); + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Username: should fail if forbidden characters (@) are used', async () => { + validateUsername.mockReturnValue(false); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(true); + + const response = await request(app).post('/users').send({ + username: 'abc@123', + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Username: should fail if left empty', async () => { + validateUsername.mockReturnValue(false); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(true); + + const response = await request(app).post('/users').send({ + username: '', + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Username: should pass if it contains letters, numbers, and periods', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(true); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(200); + expect(response.body).toEqual({ + message: 'Valid User', + userId: expect.any(String) // We expect a string like "1" + }); + }) +}) + +describe('Business requirements - Password', () => { + + test('Password: should fail if shorter than 8 characters', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(false); + validateEmail.mockReturnValue(true); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Pass123', // 7 chars + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + expect(response.body.userId).toBeUndefined(); + }) + + test('Password: should fail if no lowercase letter is used', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(false); + validateEmail.mockReturnValue(true); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'PASSWORD123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Password: should fail if no uppercase letter is used', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(false); + validateEmail.mockReturnValue(true); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); }) - // test response content type? - // test response message - // test response user id value - // ... + test('Password: should fail if left empty', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(false); + validateEmail.mockReturnValue(true); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: '', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Password: should fail if no number is used', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(false); + validateEmail.mockReturnValue(true); + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'testPassword', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Password: should fail if it contains special characters', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(false); + validateEmail.mockReturnValue(true); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password@123', // contains '@' + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) }) -describe('given incorrect or missing username and password', () => { - test('return status 400', async () => { +describe('Business requirements - Email', () => { + test('Email: should fail if @ symbol is missing', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(false); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password123', + email: 'studentexample.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + expect(response.body.userId).toBeUndefined(); + }) + + test('Email: should fail if domain name is missing', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(false); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password123', + email: 'student@' // Missing domain + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Email: should fail if domain extension is missing', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(false); + const response = await request(app).post('/users').send({ - username: 'user', - password: 'password', - email: 'not-an-email' + username: 'abc.123', + password: 'Password123', + email: 'student@example' // Missing .com, .org, etc. }) - expect(response.statusCode).toBe(400) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); }) - // test response message - // test that response does NOT have userId - // test incorrect username or password according to requirements - // test missing username or password - // ... + test('Email: should fail if left empty', async () => { + validateUsername.mockReturnValue(true); + validatePassword.mockReturnValue(true); + validateEmail.mockReturnValue(false); + + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password123', + email: '' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) }) \ No newline at end of file diff --git a/app.test.js b/app.test.js index f1b561d..7e2cef9 100644 --- a/app.test.js +++ b/app.test.js @@ -24,26 +24,206 @@ describe('given correct username and password', () => { }) expect(response.body.userId).toBeDefined(); }) +}) + +describe('Detailed Response Validation', () => { + test('should return full user object and correct headers on success', async () => { + const response = await request(app).post('/users').send({ + username: 'ValidUser', + password: 'Password123', + email: 'test@example.com' + }) + + // Check Status + expect(response.statusCode).toBe(200); + // Check Header + expect(response.headers['content-type']).toMatch(/json/); + // Check Body Content + expect(response.body).toEqual( + expect.objectContaining({ + message: "Valid User", + userId: expect.any(String) + }) + ); + }) +}) + +describe('Business requirements - Username', () => { + + test('Username: should fail if shorter than 6 characters', async () => { + const response = await request(app).post('/users').send({ + username: '12345', + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + expect(response.body.userId).toBeUndefined(); + }) + + test('Username: should fail if longer than 30 characters', async () => { + const response = await request(app).post('/users').send({ + username: 'a'.repeat(31), // Generates a 31-char string + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Username: should fail if forbidden characters (@) are used', async () => { + const response = await request(app).post('/users').send({ + username: 'abc@123', + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Username: should fail if left empty', async () => { + const response = await request(app).post('/users').send({ + username: '', + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Username: should pass if it contains letters, numbers, and periods', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(200); + expect(response.body).toEqual({ + message: 'Valid User', + userId: expect.any(String) // We expect a string like "1" + }); + }) +}) + +describe('Business requirements - Password', () => { + + test('Password: should fail if shorter than 8 characters', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Pass123', // 7 chars + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + expect(response.body.userId).toBeUndefined(); + }) + + test('Password: should fail if no lowercase letter is used', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'PASSWORD123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Password: should fail if no uppercase letter is used', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'password123', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) - // test response content type? - // test response message - // test response user id value - // ... + test('Password: should fail if left empty', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: '', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Password: should fail if no number is used', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'testPassword', + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Password: should fail if it contains special characters', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password@123', // contains '@' + email: 'student@example.com' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) }) -describe('given incorrect or missing username and password', () => { - test('return status 400', async () => { +describe('Business requirements - Email', () => { + + test('Email: should fail if @ symbol is missing', async () => { const response = await request(app).post('/users').send({ - username: 'user', - password: 'password', - email: 'not-an-email' + username: 'abc.123', + password: 'Password123', + email: 'studentexample.com' }) - expect(response.statusCode).toBe(400) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + expect(response.body.userId).toBeUndefined(); + }) + + test('Email: should fail if domain name is missing', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password123', + email: 'student@' // Missing domain + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); }) - // 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 + test('Email: should fail if domain extension is missing', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password123', + email: 'student@example' // Missing .com, .org, etc. + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) + + test('Email: should fail if left empty', async () => { + const response = await request(app).post('/users').send({ + username: 'abc.123', + password: 'Password123', + email: '' + }) + + expect(response.statusCode).toBe(400); + expect(response.body).toEqual({ error: 'Invalid User' }); + }) +}) diff --git a/test_runtime1.png b/test_runtime1.png new file mode 100644 index 0000000..74f9f54 Binary files /dev/null and b/test_runtime1.png differ diff --git a/test_runtime2.png b/test_runtime2.png new file mode 100644 index 0000000..8294c67 Binary files /dev/null and b/test_runtime2.png differ