node.jsjestjssupertestrestify

Jest + Supertest - API tests are using random ephemeral ports


I'm using Restify for my API and I'm trying to write tests for my endpoints. At first, I had only a test for ping and it was okay, but now, after I added a new test, supertest is trying to ephemeral ports to the test server (80201).

I've searched a lot and tried some approaches that seem to work for most people but not for me. I'm probably messing something up, but I have no clue what it could be.

Check out my code:

server.js

require('dotenv').config();

const config = require('./config');
const routes = require('./src/routes');

const cors = require('restify-cors-middleware');
const http = require('http');
const https = require('https');
const restify = require('restify');


module.exports = function () {
    http.globalAgent.keepAlive = true;
    http.globalAgent.maxSockets = 256;
    https.globalAgent.keepAlive = true;
    https.globalAgent.maxSockets = 256;

    const _cors = cors({
        preflightMaxAge: 5,
        origins: [new RegExp("^(https?:\/\/)?[-\w]+\.hackz\.co(\.\w+)?(:[\d]+)?$")],
        allowHeaders: [
            'authorization',
            'x-requested-with',
            'Content-MD5',
            'Date',
            'Accept-Version',
            'Api-Version',
            'Response-Time'
        ],
        credentials: true
    });

    const server = restify.createServer({ name: config.apiName });

    // Middlewares
    server.pre(_cors.preflight);
    server.use(_cors.actual);
    server.use(restify.plugins.fullResponse());
    server.use(restify.plugins.queryParser({ mapParams: true }));
    server.use(restify.plugins.bodyParser({ mapParams: true }));

    // Load Routes
    routes.set(server);

    server.on('error', function (req, res, route, error) {
        if (error && (error.statusCode == null || error.statusCode !== 404)) {}
    });

    // Start Server
    server.listen(config.apiPort, function () {
        console.log(`${server.name} listening at ${server.url}.\nWe're in ${config.env} environment!`);
    });

    return server;
}();

tests/config/server.js

const server = require('../..');
const request = require('supertest');

function TestServer() {
    return request(server);
}

module.exports = { TestServer };

tests/services/request.js

const { TestServer } = require("../config/server");


async function get(path, sessionkey = '', params = {}) {
    const server = TestServer();
    return await server
        .get(path)
        .query(params)
        .set("authorization", sessionkey)
        .set("content-type", "application/json")
    ;
}

async function post(path) {
    const server = TestServer();
    return await server
        .post(path)
        .set("content-type", "application/json")
    ;
}

module.exports = {
    get,
    post,
};

tests/config/setup.js

const server = require('../..');


afterAll(() => {
    return server.close()
});

src/controllers/Ping.test.js

const { get } = require('../../tests/services/request');


describe('Ping Controller', () => {
    describe('GET /ping', () => {
        it('Should return 200', async () => {
            const response = await get('/ping');
            expect(response.status).toBe(200);
        });
    });
});

src/controllers/Session.test.js

const { post } = require('../../tests/services/request');


describe('Session Controller', () => {
    const userId = 1;
    describe('POST /:userId/create', () => {
        it('Should create session successfully!', async () => {
            const response = await post(`${userId}/create`);
            expect(response.status).toBe(200);
            expect(response.body.auth).toBe(true);
        });
    });
});

package.json (scripts and Jest config)

...

"scripts": {
    "start": "node index.js",
    "test": "jest --detectOpenHandles --forceExit --coverage",
    "test:api": "npm run test -- --roots ./src/controllers"
},

...

"jest": {
    "setupFilesAfterEnv": [
      "jest-extended",
      "<rootDir>/tests/config/setup.js"
    ],
    ...
}

This is the error output:

> user-session-api@1.0.0 test
> jest --detectOpenHandles --forceExit --coverage

 FAIL  src/controllers/Session.test.js
  Session Controller
    POST /:userId/create
      ✕ Should create session successfully! (39 ms)

  ● Session Controller › POST /:userId/create › Should create session successfully!

    RangeError: Port should be >= 0 and < 65536. Received 80201.RangeError [ERR_SOCKET_BAD_PORT]: Port should be >= 0 and < 65536. Received 80201.

Things I've tried:

HELP!

UPDATE: Just realized that running npm run test "Ping.test.js" it succeeds and running npm run test "Session.test.js" (which is the new test) it fails. So there's probably something wrong with that single file.


Solution

  • OH MY GOD!

    I found the issue and, prepare yourself, the solution is ridiculous.

    The request path in my test had a typo.

    I was doing this:

    const response = await post(`${userId}/create`); // with userId as 1
    

    The path was missing an initial /, that's it haha.

    THAT'S why supertest was appending a 1 to the server port and the RangeError was raised.

    I'm hating myself right now.