Error: expected 200 “OK”, got 401 “Unauthorized” when trying to login using mochaAsk Question

问题:

I'm trying to use passport passport-jwt and passport-local to provide user authentication for a secure API and the testing is causing me some issues. I'm currently trying to test that when I call /meals with an Authorization header set that a list of meals comes back.

Currently in mocha I'm getting Error: expected 200 "OK", got 401 "Unauthorized" when I try and run my suite and I can't understand why.

Code snippets below:

meals.test.js

// NPM imports
const request = require('supertest');
const expect = require('expect');
const {ObjectID} = require('mongodb');

// Local files
const app = require('./../app');
const {Meal} = require('./../models/meal');
const {meals, validUsers, populateUsers, populateMeals} = require('./seed/seed');

function createAuthenticatedRequest(loginDetails, done) {
    let authenticatedRequest = request.agent(app);
    authenticatedRequest
        .post('/login')
        .send(loginDetails)
        .end(function (error, response) {
            if (error) {
                throw error;
            }
            done(authenticatedRequest)
        });
}

beforeEach(populateMeals);
beforeEach(populateUsers);


describe('GET /meals', () => {
    it('should get all the meals', (done) => {
        createAuthenticatedRequest({email: validUsers[0].email, password: 'secret'}, function (request) {
            console.log()
            request
                .get('/meals')
                .expect(200)
                .expect((res) => {
                    expect(res.body.length).toBe(2);
                })
                .end(done);
        })
    });
});

describe('POST /meals', () => {
    it('should create a new meal', (done) => {
        let mealName = 'Casserole';
        let cookedWeight = 1000;
        let servings = 5;
        let portionSize = 200;

        request(app)
            .post('/meals')
            .send({
                mealName,
                cookedWeight,
                servings
            })
            .expect(200)
            .expect((res) => {
                expect(res.body.mealName).toBe(mealName);
                expect(res.body.cookedWeight).toBe(cookedWeight);
                expect(res.body.servings).toBe(servings);
                expect(res.body.portionSize).toBe(portionSize);
            })
            .end((err) => {
                if (err) {
                    return done(err);
                }
                Meal.find({mealName}).then((meals) => {
                    expect(meals.length).toBe(1);
                    expect(meals[0].mealName).toBe(mealName);
                    done();
                }).catch((e) => done(e))
        });
    });

    it('should not create a meal with incorrect data', (done) => {
        let mealName = 'Casserole';
        let cookedWeight = 'falafel';
        let servings = 5;
        request(app)
            .post('/meals')
            .send({
                mealName,
                cookedWeight,
                servings
            })
            .expect(400, done);
    });

    it('should not create a meal with no data sent', (done) => {
        request(app)
            .post('/meals')
            .send({})
            .expect(400, done);
    });
});

describe('GET /meals/:id', () => {
    it('should return a specific meal', (done) => {
        request(app)
            .get(`/meals/${meals[0]._id.toHexString()}`)
            .expect(200)
            .expect((res) => {
                expect(res.body.meal.mealName).toBe(meals[0].mealName);
            })
            .end(done);
    });
    it('should return a 404 if not found', (done) => {
        request(app)
            .get('/meals/asd')
            .expect(404)
            .end(done);
    });
    it('should return a 404 if doc not found', (done) => {
        let hexId = new ObjectID().toHexString();

        request(app)
            .get(`/meals/${hexId}`)
            .expect(404)
            .end(done);
    });
});

describe('DELETE /meals/:id', () => {
    it('should delete a meal', (done) => {
        let hexId = meals[0]._id.toHexString();
        request(app)
            .delete(`/meals/${meals[0]._id.toHexString()}`)
            .expect(200)
            .expect((res) => {
                expect(res.body.meal._id).toBe(hexId);
            })
            .end((err) => {
                if(err) {
                    return done(err)
                }
                Meal.findById(hexId).then((meal) => {
                    expect(meal).toNotExist();
                    done();
                }).catch((e) => done(e));
            })
    });

    it('should return a 404 if meal not found', (done) => {
        let hexId = new ObjectID().toHexString();
        request(app)
            .delete(`/meals/${hexId}`)
            .expect(404)
            .end(done);
    });

    it('should return a 404 if malformed id is sent', (done) => {
        request(app)
            .delete('/meals/123abc')
            .expect(404)
            .end(done);
    });

});

describe('PATCH /meals/:id', () => {
    it('should update a meal if a valid request is sent', (done) => {
        let id = meals[0]._id.toHexString();
        let mealName = 'Testing update route';

        request(app)
            .patch(`/meals/${id}`)
            .send({
                mealName
            })
            .expect(200)
            .expect((res) => {
                expect(res.body.meal.mealName).toBe(mealName);
            })
            .end(done);
    });

    it('should return a 404 if meal is not found', (done) => {
        let id = new ObjectID().toHexString();
        let mealName = 'Testing update route';
        request(app)
            .patch(`/meals/${id}`)
            .send({ mealName })
            .expect(404)
            .end(done);
    });

    it('should return a 404 if malformed id is sent', (done) => {
        request(app)
            .patch('/meals/123abc')
            .expect(404)
            .end(done);
    });
});

routes/meals.js

require('./../config/passport');

const express = require('express');
const router = express.Router();
const passport = require('passport');

const AuthenticationController = 
require('./../controllers/authentication/authentication');

// Middleware to require login/auth
const requireAuth = passport.authenticate('jwt', { session: false });
const requireLogin = passport.authenticate('local', { session: false });


const {listMeals, getMeal, createMeal, updateMeal, deleteMeal} = 
require('./../controllers/meals/meals');

router.get('/', requireAuth, listMeals);

router.post('/', createMeal);

router.get('/:id', getMeal);

router.delete('/:id', deleteMeal);

router.patch('/:id',  updateMeal);

module.exports = router;

controllers/meals/meals.js

const {ObjectID} = require('mongodb');
const _ = require('lodash');

const {Meal} = require('./../../models/meal');

let listMeals = (req, res) => {
    console.log(req.headers);
    Meal.find({

    }).then((meals) => {
        res.send(meals)
    }, (e) => {
        res.status(400).send(e);
    });

};

let getMeal = (req, res) => {
    let id = req.params.id;
    if(!ObjectID.isValid(id)) {
        return res.sendStatus(404);
    }
    Meal.findOne({
        _id : id
    }).then((meal) => {
        if(!meal) {
            return res.sendStatus(404);
        }
        res.status(200).send({meal})
    }, () => {
        res.status(404).send();
    });

};

let createMeal = (req, res) => {
    let meal = new Meal({
        mealName : req.body.mealName,
        cookedWeight : req.body.cookedWeight,
        servings : req.body.servings,
        portionSize: req.body.cookedWeight / req.body.servings
    });

    meal.save().then((doc) => {
        res.send(doc)
    }, (e) => {
        res.status(400).send(e);
    });
};

let deleteMeal = (req, res) => {
    let id = req.params.id;
    if(!ObjectID.isValid(id)) {
        return res.sendStatus(404);
    }
    Meal.findOneAndRemove({
        _id : id
    }).then((meal) => {
        if(!meal) {
            return res.sendStatus(404);
        }
        res.status(200).send({meal});
    }).catch((e) => {
        res.sendStatus(400).send(e);
    });
};

let updateMeal = (req, res) => {
    let id = req.params.id;
    let body = _.pick(req.body, ['mealName', 'servings', 'cookedWeight']);
    if(!ObjectID.isValid(id)) {
        return res.sendStatus(404);
    }
    Meal.findOneAndUpdate({
        _id : id
    }, { $set : body}, {new : true}).then((meal) => {
        if(!meal) {
            return res.sendStatus(404);
        }
        res.status(200).send({meal});
    }).catch((e) => {
        res.status(400).send(e);
    })
};

module.exports = {
    createMeal,
    listMeals,
    getMeal,
    deleteMeal,
    updateMeal
};

Let me know if any more information is needed

回答1:


So after two days of poking and prodding I found a solution, posting here for future reference of other people, created the following function to create a Authorization header and return it for use for the request.

// Auxiliary function.
function createLoginToken(server, loginDetails, done) {
    request(server)
        .post('/login')
        .send(loginDetails)
        .end(function(error, response) {
            if (error) {
                throw error;
            }
            let loginToken = response.body.token;
            done(loginToken);
        });
}

It's then called as follows:

it('should get all the meals', (done) => {
    createLoginToken(app, { email: validUsers[0].email, password: 'secret'}, function(header) {
        request(app)
            .get('/meals')
            .set('Authorization', header)
            .expect(200)
            .expect((res) => {
                expect(res.body.length).toBe(2);
            })
            .end(done);
    });
});

Massive thanks to Juha Hytönen for his blog post pointing me in the right direction

标签: authentication mocha passport
© 2014 TuiCode, Inc.