The first thing I will do is defining User model. after this, authentication process is wating(login, sign up, etc).
Let's just put minimum fieds that we need. [name, email, role, password]
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs'); // used when hashing password.
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please add a name']
},
email: {
type: String,
required: [true,'Please add an email'],
unique: true,
match: [
/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/,
'Please add a valid email'
]
},
role: {
type: String,
enum: ['user', 'instructor'],
defalut: 'user'
},
password: {
type: String,
required: [true, 'Please add a password'],
minlength: 6,
select: false
},
createdAt: {
type: Date,
default: Date.now
}
});
UserSchema.pre('save', async function(next) { // pre middleware
if (!this.isModified('password')) {
next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});
module.exports = mongoose.model('User', UserSchema);
To do hash, we need to install 'bcryptjs'
> npm install bcryptjs
"pre('save', ~);" is a middleware process before saving user model. In this case, this hashs inputed password.
now we defiend minimum user modelSchema. so the next, we just make directorys controllers, routes.
//udemy/controllers/auth.js
const User = require('../models/User');
//udemy/routes/auth.js
const express = require('express');
const router = express.Router();
module.exports = router;
Connect route.
//udemy.server.js
//Route files
const auth = require('.routes/auth');
//Mount routers
app.use('/api/v1/auths', auth);
If http request "/api/v1/auths/" comes, goes to udemy/routes/auth.js file.
Now then It is time to make Test Code first time.
we will use mocha, chai, supertest for test. Install these.
> npm install mocha supertest chai
then put in package.json
"script": {
"start"~,
"dev":~,
"test": "mocha" // put this code.
},
Test Requirement: POST /register()
Register User
- If input valid datas client can create new user. 200 success.
- If input invalid datas, 400 error occurs.
- If input same email already exist, 409 error occurs.
Make file "/udemy/tests/user.spec.js". and bring utils.
//udemy/tests/user.speck.js
const User = require('../models/User');
const request = request("supertest");
const expect = require("chai").expect;
const app = require("../server"); // connect with application.
Next is making test code.
describe("/api/v1/auth", () =>
beforeEach(async () => {
await User.deleteMany({});
});
describe("POST /", () => {
// success register
it("[Success] register/create a user", async () => {
const res = await request(app)
.post("/api/v1/auth/register")
.send({
name: "test",
email: "test@gmail.com",
password: "123412345",
role: "user"
});
expect(res.status).to.equal(200);
expect(res.body).to.have.property("token");
});
it("[Fail] invalid input", async() => {
const res = await request(app)
.post("/api/v1/auth/register")
.send({
name: "test",
email: "testgamilcom",
password: "1234556",
role: "user"
});
it("[Fail] duplicated error", async () => {
const user = {
name: "test", email: "test@gmail.com", password: "123456", role: "user"};
await User.insertMany(user);
const res = await request(app)
.post("/api/v1/auth/register")
.send({
name: "test",
email: "test@gamil.com",
password: "123456",
role: "user"
});
expect(res.status).to.be.equal(409);
expect(res.body.error).to.be.equal('Duplicate field value entered');
});
});
});
If we do test with this console "npm test tests/user.spec.js", We got this log.
PS C:\Users\moonq\Desktop\udemy> npm test tests/user.spec.js
> udemy@1.0.0 test C:\Users\moonq\Desktop\udemy
> mocha --timeout 10000 "tests/user.spec.js"
Server running in development mode on port 5000
api/v1/auth
POST /
MongoDB Connected: cluster0-shard-00-00-lamdv.mongodb.net
POST /api/v1/auth/regitser 404 1.813 ms - 160
1) should register/create a user
0 passing (3s)
1 failing
1) api/v1/auth
POST /
should register/create a user:
AssertionError: expected 404 to equal 200
+ expected - actual
-404
+200
at Context.<anonymous> (tests\user.spec.js:27:35)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
Test is done with fail. but it is work! So let's make "auth controller" to get expecting result.
Just made "register methods" in auth Controller.
const User = require('../models/User');
const asyncHandler = require('../middlewares/async');
exports.register = asyncHandler(async(req, res, next) => {
const {name, email, role, password } = req.body;
const user = await User.create({
name,
email,
role,
password
});
res.status(200);
});
Test passed!
wowwwww~
Test Requirement: login(), logout()
- If input valid data(email, password), client gets token.
- If input invalide data(email, password), 400 error occur.
- If request GET /logout, success, and client lose token.(make token to none)
Make test code.
describe("POST /login", async () => {
it("[Success] login", async () => {
const user = {
name: "test", email: "test@gmail.com", password: "123456", role: "user"
};
await User.create(user);
const res = await request(app)
.post("/api/v1/auth/login")
.send({
email: "test@gamil.com",
password: "123456"
});
expect(res.status).to.be.equal(200);
expect(res.body).to.have.property("token");
});
it("[Fail] Incorrect email or password", async () => {
const user = { name: "test",
email: "test@gmail.com",
password: "1234566",
role: "user"
};
await User.create(user);
const res = await request(app)
.post("/api/v1/auth/login")
.send({
email: "test@gmail.com",
password: "0000000"
});
expect(res.status).to.be.equal(401);
expect(res.body.error).to.be.equal('Invalid credentials');
});
});
describe("GET /logout", async () => {
it("[Success] logout", async () => {
const user = {
name: "test", email: "test@gmail.com", password: "1234566", role: "user"
};
await User.create(user);
const res = await request(app)
.get("/api/v1/auth/logout");
expect(res.status).to.be.equal(200);
expect(res.body.token).to.be.equal('none');
});
});
Then let's make controller methods to pass test.
exports.login = asyncHandler( async (req, res, next) => {
const { email, password } = req.body;
// Validate email & password
if (!email || !password) {
return next(new ErrorResponse('Please provide an email ans password', 400));
}
// Check for user
const user = await User.findOne({ email }).select('+password');
if (!user) {
return next(new ErrorResponse('Invalid credentials', 401));
}
// check if password matches
const isMatch = await user.matchPassword(password);
if (!isMatch) {
return next(new ErrorResponse('Invalid credentials', 401));
}
sendTokenResponse(user, 200, res);
});
exports.logout = asyncHandler( async (req, res, next) => {
res.cookie('token', 'none', {
expires: new Date(Date.now() + 10 * 1000),
httpOnly: true
});
res.status(200).json({
success: true,
token: 'none'
});
});
matchPassword()
//models.user.js
UserSchema.methods.matchPassword = async function(enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
done!
Test Requirement: GET /getMe()
- If client request with valid token, client get self data.
- If client requet with invalid token, 401 error occurs
dd
'notAnymore > Udemy API' 카테고리의 다른 글
Course CRUD [TDD, express] (0) | 2020.05.29 |
---|---|
Occured Errors [TDD, Express] (0) | 2020.05.25 |