Express route test with Supertest and Mocha failing - javascript

I have a set of tests to verify that my Express serves routes properly. For one of my routes launch.js, I receive two different errors, and sometimes the test randomly passes with long (425ms+) response times. Is there a better way to approach this?
launch.js
const authUtils = require('../../lib/authUtils');
const express = require('express');
const VError = require('verror');
const router = express.Router();
router.get('/', (req, res) => {
/**
* Request conformance object
*/
authUtils.getConformance((error, response, body) => {
// If error with HTTP request
if (error) {
throw new VError('Conformance object request failed', error);
// If error from auth server
} else if (response.body.message) {
throw new VError('Conformance object request failed', response.body.message);
// If request for conformance object succeeds
} else {
// Parse conformance object for tokenUri and authUri
authUtils.parseConformanceUris(body, (authUri, tokenUri, parsingError) => {
// Ensure URIs can be parsed from response
if (error) {
throw new VError('Issue while parsing conformance object', parsingError);
} else {
/**
* Data values needed later for /redirect
* #type {{state: string, tokenUri: string}}
*/
const launchData = {
state: authUtils.getUniqueCode(),
tokenUri,
};
// Build URI to request token from auth server
authUtils.buildGetTokenUri(authUri, launchData.state, (getTokenUri) => {
// Store state and tokenUri to session and redirect browser
authUtils.storeLaunchData(req, res, launchData, getTokenUri);
});
}
});
}
});
});
module.exports = router;
index.spec.js
const request = require('supertest');
const app = require('../index');
describe('Express server routes', () => {
describe('GET /launch', () => {
it('responds with HTTP 302', (done) => {
request(app).get('/launch').expect(302, done);
});
});
});
subject.getConformance
/**
* Utility function to request conformance object from auth server
* #param callback
*/
const getConformance = (callback) => {
request({ url: process.env.CONFORMANCE_URI, json: true, method: 'get' }, (error, response, body) => {
if (!error && response.statusCode === 200) {
callback(null, response, body);
} else {
callback(error, response, null);
}
});
};
Error 1
Uncaught TypeError: Cannot read property 'message' of null
at subject.getConformance (test/authUtils.spec.js:28:27)
at Request.request [as _callback] (lib/authUtils.js:7:374)
at Request.self.callback (node_modules/request/request.js:186:22)
at Request. (node_modules/request/request.js:1163:10)
at IncomingMessage. (node_modules/request/request.js:1085:12)
at endReadableNT (_stream_readable.js:1059:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)
Error 2
Uncaught AssertionError: expected 'body' to equal undefined
at subject.getConformance (test/authUtils.spec.js:43:16)
at Request.request [as _callback] (lib/authUtils.js:7:374)
at Request.self.callback (node_modules/request/request.js:186:22)
at Request. (node_modules/request/request.js:1163:10)
at IncomingMessage. (node_modules/request/request.js:1085:12)
at endReadableNT (_stream_readable.js:1059:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)

Assuming that the app object is requiring from a expressjs server, try
.get('/launch')
.expect(302)
.end(function (err, res) {
res.status.should.be.equal(302);
done();
});
If your index.js file is not a server then you need to configure the app object from a valid server. Are you exporting the app from index.js where app is
var express = require("express")
, app = express();
?

Related

Sending request to webserver using axios

I want to send an array of strings over localhost 3000 with route start then send back a response with status 200 and eventually a map attached to response.body Currently i have this
Client code:
const axios = require('axios');
let listOfNames = ['mikey'];
axios.post(''http://localhost:3000/start'', {
data: { names: listOfNames }
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
Server code:
const express = require('express');
const app = express()
const port = 3000
var listOfNames = [];
app.post('/start', async (req, res) => {
listOfNames = req.params.listOfNames;
res.status(200).send("Names added");
});
app.listen(port, () => {
console.log('request recieved');
});
I get this error presemably from how the request is being sent, any help?
TypeError [ERR_INVALID_URL]: Invalid URL
at new NodeError (node:internal/errors:393:5)
at URL.onParseError (node:internal/url:565:9)
at new URL (node:internal/url:645:5)
at dispatchHttpRequest (C:\Users\cmb\rectangleHealth\node_modules\axios\dist\node\axios.cjs:23
94:20)
at new Promise (<anonymous>)
at http (C:\Users\cmb\rectangleHealth\node_modules\axios\dist\node\axios.cjs:2330:10)
at Axios.dispatchRequest (C:\Users\cmb\rectangleHealth\node_modules\axios\dist\node\axios.cjs:
3260:10)
at Axios.request (C:\Users\cmb\rectangleHealth\node_modules\axios\dist\node\axios.cjs:3610:33)
at Axios.httpMethod [as post] (C:\Users\cmb\rectangleHealth\node_modules\axios\dist\node\axios
.cjs:3649:19)
at Function.wrap [as post] (C:\Users\cmb\rectangleHealth\node_modules\axios\dist\node\axios.cj
s:27:15) {
input: '/start',
code: 'ERR_INVALID_URL'
}
Edit: New error ECONNRESET error emerging from applied fixes
AxiosError: read ECONNRESET
at AxiosError.from (C:\Users\cmb\rectangleHealth\node_modules\axios\dist\node\axios.cjs:789:14
)
at RedirectableRequest.handleRequestError (C:\Users\cmb\rectangleHealth\node_modules\axios\dis
t\node\axios.cjs:2744:25)
at RedirectableRequest.emit (node:events:513:28)
at eventHandlers.<computed> (C:\Users\cmb\rectangleHealth\node_modules\follow-redirects\index.
js:14:24)
at ClientRequest.emit (node:events:513:28)
at Socket.socketErrorListener (node:_http_client:494:9)
at Socket.emit (node:events:513:28)
at emitErrorNT (node:internal/streams/destroy:151:8)
at emitErrorCloseNT (node:internal/streams/destroy:116:3)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
syscall: 'read',
code: 'ECONNRESET',
errno: -4077,
The console also outputs a 2 json objects called request and config that cannot fit into this post.
I noticed 2 things errors in your code:
First, check your url is correct, instead of
''http://localhost:3000/start'' (you have multiple single quotes wrapping the url)
try,
"http://localhost:3000/start" or 'http://localhost:3000/start' (wrap it in proper double quotes or single quotes)
Second, You are passing the data in your api call as request body and not as request parameters, but you are trying to access it in the parameters of your api.
You should try accessing the request's body on the server side instead of parameters,
app.post('/start', async (req, res) => {
listOfNames = req.body.listOfNames;
res.status(200).send("Names added");
});
Else you might probably face issue while accessing the data in api as well.

Https request Post from an Angular App to ExpressJS Node.js Server results in request write() end() is not a function and socket hang up Error

I am trying to invoke an Authorization Token API to get an access token.
The trigger is from an angular application via a button click.
The angular service code is below:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { User } from './../auth/user';
import { AuthResponse } from './../auth/auth-response';
import { tap } from 'rxjs/operators';
import { Observable, BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class AuthService {
AUTH_SERVER = "http://localhost:3000";
authSubject = new BehaviorSubject(false);
constructor(private httpClient: HttpClient) { }
register(user: User): Observable<AuthResponse> {
return this.httpClient.post<AuthResponse>(`${this.AUTH_SERVER}/register`, user).pipe(
tap((res: AuthResponse ) => {
if (res.token) {
console.log("ACCESS_TOKEN : "+ res.token.access_token);
localStorage.set("ACCESS_TOKEN", res.token.access_token);
localStorage.set("EXPIRES_IN", res.token.token_type);
localStorage.set("ACCESS_TOKEN", res.token.expires_in);
localStorage.set("EXPIRES_IN", res.token.refresh_token);
this.authSubject.next(true);
}
})
);
}
}
And the NodeJS Backend service code is below:
const express = require('express')
const https = require('https')
const app = express()
const router = express.Router();
const cors = require('cors');
const bodyParser = require("body-parser")
const api_helper = require('./util/api_helper')
const port = 3000
app.use(cors());
router.use(bodyParser.urlencoded({ extended: false }));
router.use(bodyParser.json());
router.get('/', (req, res) => {
res.status(200).send('Welcome to Make REST API Calls to Authorisation Server In Express!');
});
router.post('/register', (req, res) => {
console.log('nodejs user name = '+req.body.username);
console.log('nodejs password = '+req.body.password);
var client_id = 'xxxx';
var client_secret = 'yyyyyy';
var auth_header = 'Basic ' + Buffer.from(client_id + ':' + client_secret).toString('base64');
const data = "grant_type=password&username=ddddd&password=eeeeee&client_id=fff&client_secret=Joe75";
const options = {
hostname: 'linux-2222',
port: 8543,
path: '/xxxx/oauth2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-formurlencoded',
'Content-Length': data.length
}
};
const requestVar = https.request(options, (res) => {
console.log(`statusCode: ${res.statusCode}`)
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
});
});
requestVar.write(data);
requestVar.end();
req.on('error', (error) => {
console.log('error is ' + error);
});
});
app.use(router);
app.listen(port, () => console.log(`Node server listening on port ${port}!`))
The error I am getting is below:
Entering the server endpoint
nodejs user name = xxx
nodejs password = yyyy
(node:6285) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
**TypeError: req.write is not a function**
at /Users/admin/Development/mod/integrator/src/app/app.js:78:7
at invokeCallback (/Users/admin/Development/mod/integrator/node_modules/raw-body/index.js:224:16)
events.js:288
throw er; // Unhandled 'error' event
^
Error: socket hang up
at connResetException (internal/errors.js:604:14)
at TLSSocket.socketOnEnd (_http_client.js:460:23)
at TLSSocket.emit (events.js:323:22)
at endReadableNT (_stream_readable.js:1204:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
Emitted 'error' event on ClientRequest instance at:
at TLSSocket.socketOnEnd (_http_client.js:460:9)
at TLSSocket.emit (events.js:323:22)
at endReadableNT (_stream_readable.js:1204:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21) {
code: 'ECONNRESET'
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! integrator#0.0.1 start: `NODE_TLS_REJECT_UNAUTHORIZED='0' node ./src/app/contractor_lifecycle_app.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the integrator#0.0.1 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/admin/.npm/_logs/2020-03-28T11_37_38_559Z-debug.log
The nodejs console shows errors with :
TypeError: req.write is not a function and Error: socket hang up.
FYI, I am getting the access token in postname using the relevant http request options. And I was able to use the access token to get a protected resource in postman.
But, I cannot even consume the authorization endpoint via nodejs express.
Please, I need someone to help, any ideas will do.
It appears the problem is with the https.request logic, but I dont know exactly where.
NOTE: The request parameter from the angular app is being successfuly logged in the console in the nodejs https request post function.
The req in the following line:
router.post('/register', (req, res) => {
was overriding the following:
req.write(data);
req.end();
The request write() and end() are methods of the object returned from https.request(options, (res) => {
So, I have updated the code by assigning the https.request(options, (res) => { .. to a new varibale called requestVar.
And now I am getting my acccess token from the authorization server.
I have updated original post with the fix.
Hurray.

ExpressJS Node.js Angular Error: getaddrinfo ENOTFOUND While Getting OAuth2 API Authorisation Token

I have written an angular application, which onclick of a button makes an HTTP post request to an Node/Express.js endpoint for authorisation token.
Within postman, I have done the following successfully:
Configured the relevant credentials for the basic OAuth2 authorisation token and getting an access token
I am calling a restful endpoint with a access token and creating a user on the ldap directory.
In the angular app, I have created the following key components:
User interface
AuthResponse interface
Auth Service
UI Component
Below is the logic excerpts for the Auth Service in the angular frontend app:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { User } from './../auth/user';
import { AuthResponse } from './../auth/auth-response';
import { tap } from 'rxjs/operators';
import { Observable, BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class AuthService {
AUTH_SERVER = "http://localhost:3000";
authSubject = new BehaviorSubject(false);
constructor(private httpClient: HttpClient) { }
register(user: User): Observable<AuthResponse> {
return this.httpClient.post<AuthResponse>(`${this.AUTH_SERVER}/register`, user).pipe(
tap((res: AuthResponse ) => {
if (res.token) {
console.log("ACCESS_TOKEN : "+ res.token.access_token);
localStorage.set("ACCESS_TOKEN", res.token.access_token);
localStorage.set("EXPIRES_IN", res.token.token_type);
localStorage.set("ACCESS_TOKEN", res.token.expires_in);
localStorage.set("EXPIRES_IN", res.token.refresh_token);
this.authSubject.next(true);
}
})
);
}
}
Below is the code in the angular view component
register() {
console.log('fetching registration token');
this.authService.register(this.user).subscribe((res) => {
//this.router.navigateByUrl('home');
});
}
Below is the html fragment which call contains the button calling the register endpoint:
<div>
<button (click)="register()" >Register</button>
</div>
Below is my expressjs app.js
const express = require('express')
const http = require('https')
const app = express()
const router = express.Router();
const cors = require('cors');
const bodyParser = require("body-parser")
const api_helper = require('./util/api_helper')
const port = 3000
app.use(cors());
router.use(bodyParser.urlencoded({ extended: false }));
router.use(bodyParser.json());
router.get('/', (req, res) => {
res.status(200).send('Welcome to Make REST API Calls to Authorisation Server In Express!');
});
router.get('/getAPIResponse', (req, res) => {
api_helper.make_API_call('https://jsonplaceholder.typicode.com/todos/1')
.then(response => {
res.json(response)
})
.catch(error => {
res.send(error)
})
})
router.post('/register', (req, res) => {
console.log('Entering the server endpoint ');
//following variables are being printed to my browser console successfully
console.log('angular sent user name = '+req.body.username);
console.log('angular sent password = '+req.body.password);
var client_id = 'xxxx';
var client_secret = 'yyyyyy';
var auth_header = 'Basic ' + Buffer.from(client_id + ':' + client_secret).toString('base64');
const data = "grant_type=password&username=joe&password=joepass&client_id=xxxx&client_secret=yyyyyy";
var authURI = "https://[hostname]:[port-no]/osp/a/idm/auth/oauth2/token";
const options = {
hostname: authURI,
form: data,
port: [port-no],
method: 'POST',
headers: {
'Content-Type': 'application/x-www-formurlencoded'
//'Authorization': auth_header
}
};
http.request(options, (res) => {
// const req = http.request(options, (res) => {
console.log(`statusCode: ${res.statusCode}`)
res.setHeader( 'Access-Control-Allow-Headers', 'Accept,Accept-Language,Content-Language,Content-Type')
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
});
});
req.on('error', (error) => {
console.log('error is ' + error);
});
});
app.use(router);
app.listen(port, () => console.log(`Node server listening on port ${port}!`))
The following endpoint in my nodejs app.js works:
router.get('/getTestResponse', (req, res) => {
api_helper.make_API_call('https://jsonplaceholder.typicode.com/todos/1')
.then(response => {
res.json(response)
})
.catch(error => {
res.send(error)
})
})
Its using the following helper function:
const request = require('request')
module.exports = {
/*
** This method returns a promise
** which gets resolved or rejected based
** on the result from the API
*/
make_API_call : function(url){
return new Promise((resolve, reject) => {
request(url, { json: true }, (err, res, body) => {
if (err) reject(err)
resolve(body)
});
})
}
}
Problem is I get the following error when I click the register button from the angular app.
But, the nodejs /register endpoint is being triggered, i.e. its logging the username and password retrieved from the request object to the console:
Entering the server endpoint
events.js:288
throw er; // Unhandled 'error' event
^
Error: getaddrinfo ENOTFOUND https://[hostname]:[port-no]/osp/a/idm/auth/oauth2/token
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:64:26)
Emitted 'error' event on ClientRequest instance at:
at TLSSocket.socketErrorListener (_http_client.js:426:9)
at TLSSocket.emit (events.js:311:20)
at emitErrorNT (internal/streams/destroy.js:92:8)
at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
at processTicksAndRejections (internal/process/task_queues.js:84:21) {
errno: 'ENOTFOUND',
code: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'https://[hostname]:[port-no]/osp/a/idm/auth/oauth2/token'
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! registration-express-app#0.0.1 start: `node ./src/app/app.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the registration-express-app#0.0.1 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/admin/.npm/_logs/2020-03-28T08_56_14_947Z-debug.log
The problem was the http.request options. Basically, the hostname: should be just hostname, not https://[hostname]:[port-no]/xxx/auth/oauth2/token!
Then use the path attribute for the remainder of the endpoint URI i.e./xxx/auth/oauth2/token .
That progressed things, but I am now getting another error "Error: self signed certificate in certificate chain"
I will close this question on that note, to ensure others can gain from my experience.
Giving back to the community.
See some nice reference below:
https://node.readthedocs.io/en/latest/api/https/

Nextjs when using promises inside app.prepare().then() Error: getaddrinfo ENOTFOUND undefined

I am setting up a new server using nextJS and I would like to have a graphql endpoint within my app. This setup works perfectly when using 2 servers, however we only have access to 1 server so now I am moving the graphql server into my express server.
Server.js
import express from 'express'
import next from 'next'
import pg from 'pg'
import expressGraphiqlMiddleware from 'express-graphiql-middleware'
import requestProxy from 'express-request-proxy'
import ConnectionFilterPlugin from 'postgraphile-plugin-connection-filter'
const { ApolloServer } = require('apollo-server-express')
const { makeSchemaAndPlugin } = require('postgraphile-apollo-server')
....
app
.prepare()
.then(async () => {
const { schema, plugin } = await makeSchemaAndPlugin(
pgPool,
'public', // PostgreSQL schema to use
{
appendPlugins: [ConnectionFilterPlugin],
graphiql: true,
// PostGraphile options, see:
// https://www.graphile.org/postgraphile/usage-library/
}
)
...
const expressServer = express()
...
expressServer.get('/p/:id', (req, res) => {
const actualPage = '/post'
const queryParams = { title: req.params.id }
app.render(req, res, actualPage, queryParams)
})
...
//Scraping Tools
scrape(expressServer)
expressServer.get('*', (req, res) => {
return handle(req, res)
})
...
expressServer.listen(3001, err => {
if (err) throw err
console.log('> Ready on http://localhost:3001')
})
})
.catch(ex => {
console.info('error')
console.error(ex.stack)
process.exit(1)
})
I have simplified my example, but the key point here is: if I remove the async/await/promise from the current then() I will get the following error:
Error: Apollo Server requires either an existing schema, modules or typeDefs
As for course, schema and plugin will not be defined, however if I enable these promises I get: as the error
ror.js
generate SourceMap
error
Error: getaddrinfo ENOTFOUND undefined
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:60:26)
Waiting for the debugger to disconnect...

NodeJS HTTPs request returns Socket Hang up

const https = require("https");
const fs = require("fs");
const options = {
hostname: "en.wikipedia.org",
port: 443,
path: "/wiki/George_Washington",
method: "GET",
// ciphers: 'DES-CBC3-SHA'
};
const req = https.request(options, (res) => {
let responseBody = "";
console.log("Response started");
console.log(`Server Status: ${res.statusCode} `);
console.log(res.headers);
res.setEncoding("UTF-8");
res.once("data", (chunk) => {
console.log(chunk);
});
res.on("data", (chunk) => {
console.log(`--chunk-- ${chunk.length}`);
responseBody += chunk;
});
res.on("end", () => {
fs.writeFile("gw.html", responseBody, (err) => {
if (err) throw err;
console.log("Downloaded file");
});
});
});
req.on("error", (err) => {
console.log("Request problem", err);
});
returns
// Request problem { Error: socket hang up
// at createHangUpError (_http_client.js:330:15)
// at TLSSocket.socketOnEnd (_http_client.js:423:23)
// at TLSSocket.emit (events.js:165:20)
// at endReadableNT (_stream_readable.js:1101:12)
// at process._tickCallback (internal/process/next_tick.js:152:19) code: 'ECONNRESET' }
http.request() opens a new tunnel to the server. It returns a Writable stream which allows you to send data to the server, and the callback gets called with the stream that the server responds with. Now the error you encountered (ECONNRESET) basically means that the tunnel was closed. That usually happens when an error occured on a low level (very unlikely) or the tunnel timed out because no data was received. In your case the server only responded when you sent something to it, even if it was an empty package, so all you have to do is to end the stream, causing it to get flushed as an empty packet to the server, which causes it to respond:
req.end();
You might want to have a look at the request package which allows you to avoid dealing with such low-level things.

Categories