test/admin/http-api.js

Summary

Maintainability
F
5 days
Test Coverage
const toxy = require('../..')
const expect = require('chai').expect
const supertest = require('supertest')

suite('http api', function () {
  var proxy = null
  const admin = toxy.admin()
  const ports = { proxy: 9898, admin: 9899 }
  const adminUrl = 'http://localhost:' + ports.admin

  admin.listen(ports.admin)

  function setup () {
    proxy = toxy()

    proxy
      .poison(function foo (req, res, next) { next() })
      .poison(function bar (req, res, next) { next() })
      .withRule(function bar (req, res, next) { next() })

    proxy
      .rule(function foo (req, res, next) { next() })
      .rule(function bar (req, res, next) { next() })

    proxy
      .get('/foo')
      .rule(function foo (req, res, next) { next() })
      .poison(function foo (req, res, next) { next() })
      .withRule(function foo (req, res, next) { next() })

    proxy
      .get('/bar')
      .rule(function bar (req, res, next) { next() })
      .poison(function bar (req, res, next) { next() })

    admin.manage(proxy)
    proxy.listen(ports.proxy)
  }

  function shutdown (done) {
    proxy.close(done)
  }

  test('root', function (done) {
    supertest(adminUrl)
      .get('/')
      .expect(200)
      .expect('Content-Type', 'application/json')
      .expect(function (res) {
        expect(res.body.version).to.be.equal(toxy.VERSION)
        expect(res.body.links).to.be.an('object')
        expect(res.body.links.self).to.be.deep.equal({ href: '/' })
        expect(res.body.links.servers).to.be.deep.equal({ href: '/servers' })
      })
      .end(done)
  })

  function assertServer (server) {
    expect(server.id).to.be.equal('bcc')
    expect(server.port).to.be.equal(ports.proxy)
    expect(server.totalPoisons).to.be.equal(2)
    expect(server.totalRules).to.be.equal(2)
    expect(server.totalRoutes).to.be.equal(2)
    expect(server.links).to.be.an('object')
    expect(server.links.self).to.be.deep.equal({ href: '/servers/' + server.id })
    expect(server.links.routes).to.be.deep.equal({ href: '/servers/' + server.id + '/routes' })
    expect(server.links.parent).to.be.deep.equal({ href: '/servers' })
  }

  suite('servers', function () {
    test('setup', setup)

    test('get servers', function (done) {
      supertest(adminUrl)
        .get('/servers')
        .expect(200)
        .expect(function (res) {
          res.body.forEach(assertServer)
        })
        .end(done)
    })

    test('get server', function (done) {
      supertest(adminUrl)
        .get('/servers/bcc')
        .expect(200)
        .expect(function (res) {
          assertServer(res.body)
          expect(res.body.routes).to.have.length(2)
        })
        .end(done)
    })

    test('delete server', function (done) {
      supertest(adminUrl)
        .delete('/servers/bcc')
        .expect(204)
        .end(done)
    })

    test('reset', shutdown)
  })

  function assertDirective (directive, path) {
    expect(directive.name).to.be.a('string')
    expect(directive.enabled).to.be.true
    expect(directive.links).to.be.an('object')
    expect(directive.links.self).to.be.deep.equal({ href: path })
    expect(directive.links.parent).to.be.deep.equal({ href: parentHref(path) })
  }

  function assertPoison (poison, path) {
    assertDirective(poison, path)
    expect(poison.phase).to.be.equal('incoming')
  }

  suite('server poisons', function () {
    test('setup', setup)

    test('all', function (done) {
      const path = '/servers/bcc/poisons'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          expect(res.body).to.have.length(2)
          res.body.forEach(function (directive) {
            const name = directive.name + ':' + directive.phase
            assertPoison(directive, path + '/' + name)
          })
        })
        .end(done)
    })

    test('delete by name', function (done) {
      const path = '/servers/bcc/poisons/foo:incoming'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('create', function (done) {
      const path = '/servers/bcc/poisons'
      supertest(adminUrl)
        .post(path)
        .send({ name: 'latency', options: { jitter: 1000 } })
        .expect(201)
        .end(done)
    })

    test('cannot create invalid poison', function (done) {
      const path = '/servers/bcc/poisons'
      supertest(adminUrl)
        .post(path)
        .send({ name: 'invalid' })
        .expect(404)
        .end(done)
    })

    test('get by name', function (done) {
      const path = '/servers/bcc/poisons/latency:incoming'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          assertPoison(res.body, path)
        })
        .end(done)
    })

    test('create outgoing', function (done) {
      const path = '/servers/bcc/poisons'
      supertest(adminUrl)
        .post(path)
        .send({ name: 'bandwidth', phase: 'outgoing', options: { jitter: 1000 } })
        .expect(201)
        .end(done)
    })

    test('get outgoing rule by name', function (done) {
      const path = '/servers/bcc/poisons/bandwidth:outgoing'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          assertDirective(res.body, path)
          expect(res.body.phase).to.be.equal('outgoing')
        })
        .end(done)
    })

    test('delete outgoing rule by name', function (done) {
      const path = '/servers/bcc/poisons/bandwidth:outgoing'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('delete all', function (done) {
      const path = '/servers/bcc/poisons'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('reset', shutdown)
  })

  suite('server rules', function () {
    test('setup', setup)

    test('all', function (done) {
      const path = '/servers/bcc/rules'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          expect(res.body).to.have.length(2)
          assertDirective(res.body.shift(), path + '/foo')
        })
        .end(done)
    })

    test('delete by name', function (done) {
      const path = '/servers/bcc/rules/foo'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('create', function (done) {
      const path = '/servers/bcc/rules'
      supertest(adminUrl)
        .post(path)
        .send({ name: 'method', options: 'GET' })
        .expect(201)
        .end(done)
    })

    test('get by name', function (done) {
      const path = '/servers/bcc/rules/method'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          assertDirective(res.body, path)
        })
        .end(done)
    })

    test('reset', shutdown)
  })

  function assertRoute (route, path) {
    expect(route.id).to.have.length(3)
    expect(route.path).to.be.a('string')
    expect(route.method).to.be.equal('GET')
    expect(route.links).to.be.an('object')
    expect(route.links.self).to.be.deep.equal({ href: '/servers/bcc/routes/' + route.id })
    expect(route.links.poisons).to.be.deep.equal({ href: '/servers/bcc/routes/' + route.id + '/poisons' })
    expect(route.links.rules).to.be.deep.equal({ href: '/servers/bcc/routes/' + route.id + '/rules' })
    expect(route.links.parent).to.be.deep.equal({ href: '/servers/bcc/routes' })
  }

  suite('routes', function () {
    test('setup', setup)

    test('all', function (done) {
      supertest(adminUrl)
        .get('/servers/bcc/routes')
        .expect(200)
        .expect(function (res) {
          expect(res.body).to.be.have.length(2)
          res.body.forEach(assertRoute)
        })
        .end(done)
    })

    test('delete by id', function (done) {
      supertest(adminUrl)
        .delete('/servers/bcc/routes/32f')
        .expect(204)
        .end(done)
    })

    test('create', function (done) {
      supertest(adminUrl)
        .post('/servers/bcc/routes')
        .send({ path: '/foobar', method: 'GET', forward: 'http://localhost:8080' })
        .expect(201)
        .end(done)
    })

    test('get by id', function (done) {
      supertest(adminUrl)
        .get('/servers/bcc/routes/464')
        .expect(200)
        .expect(function (res) {
          expect(res.body.id).to.be.equal('464')
          assertRoute(res.body, '/foobar')
        })
        .end(done)
    })

    test('delete all', function (done) {
      supertest(adminUrl)
        .delete('/servers/bcc/routes')
        .expect(204)
        .end(done)
    })

    test('check empty routes', function (done) {
      supertest(adminUrl)
        .get('/servers/bcc/routes')
        .expect(200)
        .expect([])
        .end(done)
    })

    test('reset', shutdown)
  })

  suite('route poisons', function () {
    test('setup', setup)

    test('all', function (done) {
      const path = '/servers/bcc/routes/32f/poisons'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          expect(res.body).to.have.length(1)
          assertPoison(res.body.shift(), path + '/foo:incoming')
        })
        .end(done)
    })

    test('get by name', function (done) {
      const path = '/servers/bcc/routes/32f/poisons/foo:incoming'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          assertPoison(res.body, path)
          expect(res.body.rules).to.have.length(1)
          const nestedRule = res.body.rules.shift()
          assertDirective(nestedRule, path + '/rules/foo')
        })
        .end(done)
    })

    test('delete by name', function (done) {
      const path = '/servers/bcc/routes/32f/poisons/foo'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('delete all', function (done) {
      const path = '/servers/bcc/routes/32f/poisons'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('reset', shutdown)
  })

  suite('route poison rules', function () {
    test('setup', setup)

    test('get all', function (done) {
      const path = '/servers/bcc/routes/32f/poisons/foo/rules'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          expect(res.body).to.have.length(1)
          assertDirective(res.body.shift(), path + '/foo')
        })
        .end(done)
    })

    test('get by name', function (done) {
      const path = '/servers/bcc/routes/32f/poisons/foo/rules/foo'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          assertDirective(res.body, path)
        })
        .end(done)
    })

    test('delete by name', function (done) {
      const path = '/servers/bcc/routes/32f/poisons/foo/rules/foo'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('delete all', function (done) {
      const path = '/servers/bcc/routes/32f/poisons/foo/rules'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('reset', shutdown)
  })

  suite('route rules', function () {
    test('setup', setup)

    test('get all', function (done) {
      const path = '/servers/bcc/routes/32f/rules'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          expect(res.body).to.have.length(1)
          const rule = res.body[0]
          assertDirective(rule, path + '/foo')
        })
        .end(done)
    })

    test('get by name', function (done) {
      const path = '/servers/bcc/routes/32f/rules/foo'
      supertest(adminUrl)
        .get(path)
        .expect(200)
        .expect(function (res) {
          assertDirective(res.body, path)
        })
        .end(done)
    })

    test('delete by name', function (done) {
      const path = '/servers/bcc/routes/32f/rules/foo'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('delete all', function (done) {
      const path = '/servers/bcc/routes/32f/rules'
      supertest(adminUrl)
        .delete(path)
        .expect(204)
        .end(done)
    })

    test('reset', shutdown)
  })
})

function parentHref (path) {
  return path.split('/').slice(0, -1).join('/')
}