Skip to content
Closed
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
22 changes: 22 additions & 0 deletions NEXT_STEPS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Next steps

From your own clone or fork of the repository:

```bash
git checkout -b feature/registration-api-tests
# copy these files in, or apply ../user-register-pr.patch
npm install
npm test
npm test -- app.mock.test.js

git add app.js app.test.js app.mock.test.js registrationApiTestSuite.js PR_NOTES.md NEXT_STEPS.md
git commit -m "Add registration API tests and mocked email validation tests"
git push origin feature/registration-api-tests
```

Then open a pull request and paste in:

- the Jest coverage table from `npm test`
- the `Time:` line from `npm test`
- the `Time:` line from `npm test -- app.mock.test.js`
- a short note that the mocked suite avoids the intentionally slow email validation
35 changes: 35 additions & 0 deletions PR_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Pull request notes

## What changed

- Completed `app.test.js` with a reusable API test suite.
- Completed `app.mock.test.js` with the same test cases and `jest.mock()` for `validateEmail`.
- Added a small guard in `app.js` so omitted `username`, `password`, or `email` return `400 Invalid User` instead of crashing validation.

## Test cases covered

- Valid registration returns `200` with JSON and the expected success body.
- Invalid username:
- shorter than 6 characters
- unauthorized characters
- Invalid password:
- shorter than 8 characters
- missing uppercase letters
- contains special characters
- Invalid email:
- missing `@`
- missing valid domain extension
- Missing fields:
- username
- password
- email

## Commands to run locally

```bash
npm install
npm test
npm test -- app.mock.test.js
```

Include the resulting Jest coverage table and the `Time:` line from both runs in the PR description.
56 changes: 27 additions & 29 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
function createApp(validateUsername, validatePassword, validateEmail) {
const express = require('express')
const cors = require('cors')

const app = express()

app.use(express.json())
app.use(cors())

app.use(express.static(__dirname + '/public'));

app.post('/users', async(req, res) => {
const { username, password, email } = req.body

const validUsername = validateUsername(username)
const validPassword = validatePassword(password)
const validEmail = validateEmail(email)

if (validUsername && validPassword && validEmail) {
res.send({userId: '1', message: "Valid User"})
} else {
res.status(400).send({error: "Invalid User"})
}
})

return app
}

module.exports = createApp;
function createApp(validateUsername, validatePassword, validateEmail) {
const express = require('express');
const cors = require('cors');
const app = express();

app.use(express.json());
app.use(cors());
app.use(express.static(__dirname + '/public'));

app.post('/users', async (req, res) => {
const {username, password, email} = req.body;
const validUsername = validateUsername(username ?? '');
const validPassword = validatePassword(password ?? '');
const validEmail = validateEmail(email ?? '');

if (validUsername && validPassword && validEmail) {
res.send({userId: '1', message: 'Valid User'});
return;
}

res.status(400).send({error: 'Invalid User'});
});

return app;
}

module.exports = createApp;
77 changes: 18 additions & 59 deletions app.mock.test.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,18 @@
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
// ...
})
const createApp = require('./app');
const validateUsername = require('./validation/validateUsername');
const validatePassword = require('./validation/validatePassword');
const runRegistrationApiTestSuite = require('./registrationApiTestSuite');

jest.mock('./validation/validateEmail', () => 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);

runRegistrationApiTestSuite(app);
58 changes: 9 additions & 49 deletions app.test.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,9 @@
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
// ...
})
const createApp = require('./app');
const validateUsername = require('./validation/validateUsername');
const validatePassword = require('./validation/validatePassword');
const validateEmail = require('./validation/validateEmail');
const runRegistrationApiTestSuite = require('./registrationApiTestSuite');

const app = createApp(validateUsername, validatePassword, validateEmail);

runRegistrationApiTestSuite(app);
Loading
Loading