DEV Community

loading...

Testing secure endpoints with integration testing

hyper
hyper is an open-source services framework, the perfect companion to serverless applications
Originally published at hyper-io.Medium on ・3 min read

Testing APIs is a non-functional requirement for successful APIs, it is part of the definition of done. Securing APIs is also a non-functional requirement.

Creating an integration test on a secure endpoint that uses session-based secure cookies can be a challenge! This post will step through the approach we took to create these integration tests even while secured by session-based cookies.

Integration Tests are test that verify a collection of components properly work together to perform a process. A common integration test is from the API Endpoint to the Service Layer.

Here is our server setup:

server.js

const express = require('express')
const session = require('express-session')
const app = express()
const auth = require('./middleware/auth')

app.use(session({
  secret: 'jack russell',
  resave: false,
  saveUinitialized: true,
  cookie: { secure: true }
}))

app.use(auth.check)

app.post('/login', (req, res) => {
  req.session.user = { name: req.query.name }
  res.status(201).json({ ok: true })
})

app.get('/movies', (req, res) => {
  res.status(200).json(['Ghostbusters', 'Grounhog Day', 'What about Bob?', 'Stripes', 'Caddyshack'])
})

app.get('/logout', (req, res) => {
  res.session.user = null
  res.json({ ok: true })
})

if (!module.parent) {
  app.listen(3000)
}

module.exports = app
Enter fullscreen mode Exit fullscreen mode

middleware/auth.js

exports.check = function (req, res, next) {
  if (req.path !== '/login' && req.session.user) {
    next()
  } else {
    res.status(401).json({ message: 'not authorized' })
  }
}
Enter fullscreen mode Exit fullscreen mode

So we have a very simple API that returns a list of movies, but you have to be logged in to the API to get the movies’ list.

Let’s write a test!

test/movies.js

const test = require('tape')
const testServer = require('@twilson63/test-server')
const fetch = require('node-fetch')

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

test('List Movies', async (t) => {
  t.plan(1)
  const server = testServer(app)
  const result = await fetch(server.url + '/movies').then(r => r.json())
  t.deepEqual(result, ['Ghostbusters', 'Grounhog Day', 'What about Bob?', 'Stripes', 'Caddyshack'])

  server.close()
})
Enter fullscreen mode Exit fullscreen mode

Great! Our test is simple, but it should serve our purpose. Let’s give it a run:

node test/movies_test.js

not ok 1 should be strictly equal
  ---
    operator: equal
    expected: |-
      ['Ghostbusters', 'Grounhog Day', 'What about Bob?', 'Stripes', 'Caddyshack']
    actual: |-
      { message: 'not authorized' }
Enter fullscreen mode Exit fullscreen mode

Oops, what is the problem?

So we can’t test our endpoint, because it is secured, so how do we work around this issue? We can use sinon 's stub feature.

const test = require('tape')
const testServer = require('@twilson63/test-server')
const fetch = require('node-fetch')
const sinon = require('sinon')

const auth = require('../middleware/auth')
sinon.stub(auth, 'check').callsFake(function (req, res, next) {
  req.user = 'bob'
  next()
})

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

test('List Movies', async (t) => {
  t.plan(1)
  const server = testServer(app)
  const result = await fetch(server.url + '/movies').then(r => r.json())
  t.deepEqual(result, ['Ghostbusters', 'Grounhog Day', 'What about Bob?', 'Stripes', 'Caddyshack'])

  server.close()
})
Enter fullscreen mode Exit fullscreen mode

The result

TAP version 13
# List Movies
ok 1 should be deeply equivalent

1..1
# tests 1
# pass 1

# ok
Enter fullscreen mode Exit fullscreen mode

What is sinon.js?

Sinon is a standalone mocking library that enables you to spy, stub, and mock code for your application, you can check it out at https://sinonjs.org/ — I would recommend not to overuse Sinon, but it can come in handy for issues like this one.

Summary

Testing around security can be tricky mocking tools like sinon come in handy to create tests that focus on testing your code.

The full example is available here: https://github.com/hyper63/testing-secure-endpoints

Discussion (0)