I am testing CRUD API-Operations (https://api.sap.com/api/OP_API_MAINTNOTIFICATION/overview?ReleaseInfo=2021%20FPS02), with mochajs and chai.
let chai = require("chai");
let chaiHttp = require("chai-http");
//Assertion Style
chai.should();
chai.use(chaiHttp);
describe("API TEST", function () {
it.only('Get all notifications', function (done) {
const url = "https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata/sap/API_MAINTNOTIFICATION/MaintenanceNotification";
/**/
chai.request(url)
.post('/auth/sign_in')
// send user login details
.send({
'user': 'ExampleEmail',
'password': 'ExamplePassword'
})
.end(function (err, res) {
var token = res.body.token;
console.log(token);
done();
});
chai.request(url)
.get('')
.set("Authorization", "Bearer " + token)
.end(function (err, res) {
done();
});
});
});
Issue
In order to test the GET function it is required for the user to authorize oneself
{"fault":{"faultstring":"Failed to resolve API Key variable request.header.apikey","detail":{"errorcode":"steps.oauth.v2.FailedToResolveAPIKey"}}}
I did the authorization in this manner / gotten the token but it doesn't work yet(the token is empty), as well as the path "/auth/sign_in" seemingly isn't working
Questions
How to do the Authorization for an API in mochajs in order to test said API ?
The answer to the question has been found.
Send the following authorization with any CRUD Operation:
.auth('Username', 'Password')
Credit: Setting Basic Auth in Mocha and SuperTest #yves-m
Related
Here is the situation:
I have a database which contains a user and password registered.
My assignment, for now, is to create a login form, and login with a registered uname and pw.
Uname and pw are registered in the server/database already.
ps: I did not create the server nor database.
Node server code
import express from 'express';
import cors from 'cors';
import http from 'http';
import { Sequelize } from 'sequelize';
import { Data } from './database';
import { router } from './routes/Router';
import { initialData } from './database/someData';
const closeServer = async (
server: http.Server,
sequelize: Sequelize,
signal?: string
) => {
server.close();
await sequelize.close();
process.exit();
};
const runServer = async (): Promise<void> => {
const PORT = process.env.PORT || 8082;
const app = express();
const sequelize = Data.init();
app.use(
cors({
credentials: true,
origin: 'http://localhost:3000',
})
);
app.use('/api', router);
const server = app.listen(PORT, () => {
console.log(`Starting server at ${PORT}`);
});
try {
await sequelize.authenticate();
await sequelize.sync({
force: process.env.SERVER === 'reset',
});
if (process.env.SERVER === 'reset') await initialData();
} catch (e) {
closeServer(server, sequelize);
throw e;
}
};
runServer()
.then(() => {
console.log('Run successfully');
})
.catch((ex: Error) => {
console.log('Unable to run:', ex);
});
I need help on what is that I have to do.
When I input username and pw, on the form, what are the methods to use for sending the info?
And then, when the info reaches the server, i think the username and pw need to be validated with jwt, and then check if the user and pw exists. how do i do that?
What i have understood so far is that i gotta use axios to send info to server, but thats it.
Do i need to use jwt for the login?
What is the normal flow for this kind of mechanism?
I am using react as a framework.
So there are quite few steps here.
First you have to create endpoint on your backend server for issuing jwt tokens. Jwt tokens can be used as a pass for user to login. So in your router you would add something like this:
router.post('/login', (req, res)=> {
const username = req.body.username
const password = req.body.password
// Then you make db call to verify username and password are correct.
if Credentials are valid, you would issue jwt token
jwt.sign({
// here you can save extra information of user. Also remember this information must be public since anyone can see it. Do not put user password here
email: 'email',
userId: 'id',
}, "secret")
})
After this, you need some kind of middleware on backend, so that on each user request, you check and verify this jwt token which is sent from react application. For example you could write isAuth middleware:
const jwt =require("jsonwebtoken");
export const isAuth= (req, res, next) => {
try {
// here we attach request in auth header, with Bearer "jwt token" format. So we extract jwt token and verify it
const authHeader = req.get("Authorization");
if (!authHeader) {
return res.status(401).json({ message: "no token" });
}
const token = authHeader.split(" ")[1];
let decodedToken;
decodedToken = jwt.verify(token, "secret");
if (!decodedToken) {
return res.status(401).json({ message: "Wrong token" });
}
req.userId = decodedToken.userId;
next();
} catch (err) {
console.error(err);
return res.status(401).json({ message: err });
}
};
Now you would be able to have backend endpoints like this:
// This is how you would require login on some routes
router.post("/getMyPrivateInfo", isAuth, QueryPrivatInfo)
Now on React side, you would make request for login like this:
axios.post("/login", {
username: '1',
password: "2"
})
This would return jwt token, now you would save this token in local storage.
After its saved in local storage and you make request with axios for private info you would do following
axios.post("/getMyPrivateInfo", {any request body info neeeded}, {
headers: {
Authorization: "Bearer jwtTokenFromLocalStorage"
}
})
This is how whole flow will work, hope it makes sense
I want to test a function with jest that makes a http post request to a server by using the javascript FormData. here is a minimal example of what this could look like:
import axios from "axios";
async function foo() {
let bodyFormData = new FormData();
bodyFormData.append("foo", "myfoo");
resp = await axios({
method: "post",
url: "https://postman-echo.com/post",
data: bodyFormData,
});
return resp;
}
describe("Test", () => {
it("foo test", async () => {
return foo().then(function (result) {
console.log(result);
// here some testing stuff ...
});
});
});
from my research jest cannot use the FormData(). How can i workaround that? Ive seen some solutions here on SO but all of them are somehow mocking the FormData but the probmle with that is then never a real request is send but i want to test if the real request to the real server in working. How can I do that with jest? I've also tried https://github.com/form-data/form-data but it is not working too
Actually you need to use supertest module like that
const supertest = require('supertest');
const app = require('../app');
const request = supertest(app)
describe('POST "/" root endpoint for login & authentication', function() {
it('/register method with missing parameter returns', function(done) {
request
.post('/register')
.send('username=john&password=12345') // x-www-form-urlencoded upload
.set('Accept', 'application/json')
.expect(200)
.end((err, res) => {
if(err) return done(err);
expect(res.body.message).toBe("Username or password is not specified");
return done();
});
});
});
Also you can check this post
I am using Jest to test my Node REST API.
The problem I have currently is that when I try testing my POST routes, I always receive a status code of 302 due to res.redirect("/").
Example of my POST route:
app.post("/login", async (req, res) => {
try {
let username = 'example'
...
return res.redirect("/");
} catch (error) {
return res.redirect("/");
}
});
jest test file:
'use strict';
const request = require('supertest');
const app = require('./index');
...
describe('Test', () => {
test('POST /login', () => {
return request(app)
.post('/login')
.set('username','example')
.expect(?)
});
});
How can I test that the page has redirected successfully?
As per the Express docs, you can specify a response code as such:
res.redirect(301, 'http://example.com')
The docs state "If not specified, status defaults to “302 “Found”."
Edit: HTTP codes 301 and 302 indicate successful redirection; 301 is permanent and 302 is temporary. Both are "successful" as far as a computer is concerned.
I assert the response.headers.location for redirection location. This way I can write test cases by mocking a single class function that causes different redirections.
test('Should handle "/redirectUri"', async () => {
const exchangeForAuthTokenSpy = jest.spyOn(
OAuth.prototype,
'exchangeForAuthToken',
)
exchangeForAuthTokenSpy.mockResolvedValue({
success: true,
access_token: 'access_token',
})
const app = server('', AuthRoutes)
const res = await request(app).get('/redirectUri?code=code&state=state')
expect(exchangeForAuthTokenSpy).toHaveBeenCalledTimes(1)
expect(exchangeForAuthTokenSpy).toHaveBeenCalledWith('code', 'state')
expect(res.status).toEqual(301)
expect(res.headers.location).toContain(
'/callback?code=200&token=access_token',
)
})
It is late, but could help someone. You can test like below
it('redirection test', function (redirect) {
request(app)
.get('/url')
.expect(302, redirect)
});
I'm attempting to test a NodeJS app using Mocha and Chai. I'm using Auth0 to handle the login and signups for the app. I want to be able to test that, once logged in, the user can visit a set of pages, however I'm having trouble handling the actual logging in.
The .get('/login') redirects me to the Auth0 login page, as I expect. However, despite the .send(userCredentials) providing valid login details, it doesn't seem to login. After login, I expect it to redirect to 'localhost:3000/user' but as far as I can tell the final redirect is to the Auth0 login URL, and I'm unsure if this redirect is what prevents the send, or if the fact that the redirect takes a second or two could be causing the issue.
My test file is below.
var chai = require("chai");
var chaiHTTP = require("chai-http");
var chaiAP = require("chai-as-promised");
var server = require("../app");
var listing = require('../models/RDSModel');
var should = chai.should();
var expect = chai.expect;
var request = require("supertest");
const {Client} = require('pg');
var config = require('../config');
const connection = {
user: 'Admin',
host: process.env.DB_URL,
database: 'postgres_test',
password: config.postgresPass,
port: 5432,
};
var pg = require('knex')({
client: 'pg',
connection: connection,
searchPath: ["knex", "public"]
});
chai.use(chaiHTTP);
chai.use(chaiAP);
describe("Listing.js", function() {
beforeEach("login before each test", function(done) {
const userCredentials = {
email: 'hello#email.co.uk',
password: 'this_is_a_password!'
};
request(server)
.get('/login')
.send(userCredentials)
.end(function(err, res) {
console.log(res);
expect(res).to.have.status(302);
expect(res.body.state).to.be.true;
res.body.data.should.be.an("object");
done();
})
});
describe("get /", function() {
it("#return 200 status code", function(done) {
this.timeout(5000);
chai.request(server)
.get('/Listing/')
.end(function(err, res) {
console.log(res.body);
expect(res).to.have.status(200);
expect('Location', '/Listing')
expect(res.body).to.be.an("object");
done();
})
})
});
describe("get /Ignore", function() {
it("should return 200 status code", function(done) {
this.timeout(5000);
chai.request(server)
.get('/Listing/Ignore')
.end(function(err, res) {
expect(res).to.have.status(200);
expect(res.body).to.be.an("object");
done();
})
})
})
describe("get /Report", function() {
it("should return 200 status code", function(done) {
this.timeout(5000);
chai.request(server)
.get('/Listing/Report')
.end(function(err, res) {
expect(res).to.have.status(200);
expect(res.body).to.be.an("object");
done();
})
})
})
})
The reason it gives for failure is:
1) Listing.js
"before each" hook: login before each test:
Uncaught AssertionError: expected undefined to be true
at Test.<anonymous> (test/test-listing.js:44:41)
at Test.assert (node_modules/supertest/lib/test.js:181:6)
at localAssert (node_modules/supertest/lib/test.js:131:12)
at /Users/<Name>/Documents/Node/NodeStuff/node_modules/supertest/lib/test.js:128:5
at Test.Request.callback (node_modules/supertest/node_modules/superagent/lib/node/index.js:728:3)
at IncomingMessage.<anonymous> (node_modules/supertest/node_modules/superagent/lib/node/index.js:916:18)
at IncomingMessage.EventEmitter.emit (domain.js:476:20)
at endReadableNT (_stream_readable.js:1183:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21)
The body of res is empty, as is the text, which seems odd to me.
Any help would be appreciated.
You are trying to integrate your test suite to an interactive login route. What you actually need is non-interactive login. With Auth0, this can be achieved by POST oauth/token endpoint You will need user's email as well as the verification code sent to that email address ( so that you cannot use unverified emails ) and client secret:
https://auth0.com/docs/api/authentication#authenticate-user
Or, you can use the Management Token obtained by Management API to execute actions.
I have some Sails.js API tests (using Mocha) that make use of SuperTest's .end() method to run some Chai assertions on the response.
I call the test's done() callback after the assertions, but if an assertion error is thrown, the test times out.
I can wrap the assertions in a try/finally, but this seems a bit icky:
var expect = require('chai').expect;
var request = require('supertest');
// ...
describe('should list all tasks that the user is authorized to view', function () {
it('the admin user should be able to see all tasks', function (done) {
var agent = request.agent(sails.hooks.http.app);
agent
.post('/login')
.send(userFixtures.admin())
.end(function (err, res) {
agent
.get('/api/tasks')
.expect(200)
.end(function (err, res) {
try {
var tasks = res.body;
expect(err).to.not.exist;
expect(tasks).to.be.an('array');
expect(tasks).to.have.length.of(2);
} finally {
done(err);
}
});
});
});
});
Any suggestions on how to better deal with this? Perhaps Chai HTTP might be better?
According to Supertest's documentation, you need to check for the presence of err and, if it exists, pass it to the done function. Like so
.end(function (err, res) {
if (err) return done(err);
// Any other response-related assertions here
...
// Finish the test
done();
});
You can pass out login logic from test.
// auth.js
var request = require('supertest'),
agent = request.agent;
exports.login = function(done) {
var loggedInAgent = agent(sails.hooks.http.app);
loggedInAgent
.post('/login')
.send(userFixtures.admin())
.end(function (err, res) {
loggedInAgent.saveCookies(res);
done(loggedInAgent);
});
};
And then just use it in your test:
describe('should list all tasks that the user is authorized to view', function () {
var admin;
before(function(done) {
// do not forget to require this auth module
auth.login(function(loggedInAgent) {
admin = loggedInAgent;
done();
});
});
it('the admin user should be able to see all tasks', function (done) {
admin
.get('/api/tasks')
.expect(function(res)
var tasks = res.body;
expect(tasks).to.be.an('array');
expect(tasks).to.have.length.of(2);
})
.end(done);
});
it('should have HTTP status 200', function() {
admin
.get('/api/tasks')
.expect(200, done);
});
});
With such approach you should not login your admin for each test (you can reuse your admin again and again in your describe block), and your test cases become more readable.
You should not get timeouts with this approach because .end(done) guaranties that your test will finish without errors as well with them.