heroku-certs
Version:
heroku ssl plugin
296 lines (249 loc) • 10.2 kB
JavaScript
/* globals describe it beforeEach afterEach cli */
let expect = require('chai').expect
let nock = require('nock')
var fs = require('fs')
var sinon = require('sinon')
let certs = require('../../../commands/certs/update.js')
let error = require('../../../lib/error.js')
let assertExit = require('../../assert_exit.js')
let shared = require('./shared.js')
let sharedSsl = require('./shared_ssl.js')
let sharedSni = require('./shared_sni.js')
let endpoint = require('../../stubs/sni-endpoints.js').endpoint
let endpointStable = require('../../stubs/sni-endpoints.js').endpoint_stable
let endpointWarning = require('../../stubs/sni-endpoints.js').endpoint_warning
let certificateDetails = require('../../stubs/sni-endpoints.js').certificate_details
let unwrap = require('../../unwrap.js')
function mockFile (fs, file, content) {
fs.readFile
.withArgs(file, 'utf-8', sinon.match.func)
.callsArgWithAsync(2, null, content)
}
describe('heroku certs:update', function () {
beforeEach(function () {
cli.mockConsole()
sinon.stub(fs, 'readFile')
nock.cleanAll()
error.exit.mock()
nock('https://api.heroku.com')
.get('/apps/example/ssl-endpoints')
.reply(200, [])
nock('https://api.heroku.com')
.get('/apps/example/sni-endpoints')
.reply(200, [endpointStable])
})
afterEach(function () {
fs.readFile.restore()
})
it('# requires confirmation', function () {
mockFile(fs, 'pem_file', 'pem content')
mockFile(fs, 'key_file', 'key content')
var thrown = false
return certs.run({app: 'example', args: ['pem_file', 'key_file'], flags: {confirm: 'notexample', bypass: true}}).catch(function (err) {
thrown = true
expect(err).to.equal('Confirmation notexample did not match example. Aborted.')
}).then(function () {
expect(thrown).to.equal(true)
})
})
it('# updates an endpoint when ssl doctor passes', function () {
mockFile(fs, 'pem_file', 'pem content')
mockFile(fs, 'key_file', 'key content')
let sslDoctor = nock('https://ssl-doctor.heroku.com', {
reqheaders: {
'content-type': 'application/octet-stream',
'content-length': '23'
}
})
.post('/resolve-chain-and-key', 'pem content\nkey content')
.reply(200, {pem: 'pem content', key: 'key content'})
let mock = nock('https://api.heroku.com')
.patch('/apps/example/sni-endpoints/tokyo-1050', {
certificate_chain: 'pem content', private_key: 'key content'
})
.reply(200, endpointStable)
return certs.run({app: 'example', args: ['pem_file', 'key_file'], flags: {name: 'tokyo-1050', confirm: 'example'}}).then(function () {
sslDoctor.done()
mock.done()
expect(cli.stderr).to.equal('Resolving trust chain... done\nUpdating SSL certificate tokyo-1050 for example... done\n')
expect(cli.stdout).to.equal(
`Updated certificate details:
${certificateDetails}
`)
})
})
it('# posts intermediaries to ssl doctor', function () {
mockFile(fs, 'pem_file', 'pem content')
mockFile(fs, 'int_file', 'int content')
mockFile(fs, 'key_file', 'key content')
let sslDoctor = nock('https://ssl-doctor.heroku.com', {
reqheaders: {
'content-type': 'application/octet-stream',
'content-length': '35'
}
})
.post('/resolve-chain-and-key', 'pem content\nint content\nkey content')
.reply(200, {pem: 'pem content\nint content', key: 'key content'})
let mock = nock('https://api.heroku.com')
.patch('/apps/example/sni-endpoints/tokyo-1050', {
certificate_chain: 'pem content\nint content', private_key: 'key content'
})
.reply(200, endpoint)
return certs.run({app: 'example', args: ['pem_file', 'int_file', 'key_file'], flags: {confirm: 'example'}}).then(function () {
sslDoctor.done()
mock.done()
expect(cli.stderr).to.equal('Resolving trust chain... done\nUpdating SSL certificate tokyo-1050 for example... done\n')
expect(cli.stdout).to.equal(
`Updated certificate details:
${certificateDetails}
`)
})
/* eslint-enable no-irregular-whitespace */
})
it('# errors out when args < 2', function () {
nock('https://api.heroku.com')
.get('/apps/example')
.reply(200, { 'space': null })
return assertExit(1, certs.run({app: 'example', args: ['pem_file'], flags: {}})).then(function () {
expect(cli.stderr).to.equal(' ▸ Usage: heroku certs:add CRT KEY\n')
expect(cli.stdout).to.equal('')
})
})
it('# propegates ssl doctor errors', function () {
mockFile(fs, 'pem_file', 'pem content')
mockFile(fs, 'key_file', 'key content')
let sslDoctor = nock('https://ssl-doctor.heroku.com', {
reqheaders: {
'content-type': 'application/octet-stream',
'content-length': '23'
}
})
.post('/resolve-chain-and-key', 'pem content\nkey content')
.reply(422, 'No certificate given is a domain name certificate.')
return certs.run({app: 'example', args: ['pem_file', 'key_file'], flags: {confirm: 'example'}})
.then(function () {
expect.fail('Expected exception')
})
.catch(function (err) {
sslDoctor.done()
expect(cli.stdout).to.equal('')
expect(cli.stderr).to.equal('Resolving trust chain... !\n')
expect(err.message).to.equal('No certificate given is a domain name certificate.')
})
})
it('# bypasses ssl doctor', function () {
mockFile(fs, 'pem_file', 'pem content')
mockFile(fs, 'key_file', 'key content')
let mock = nock('https://api.heroku.com')
.patch('/apps/example/sni-endpoints/tokyo-1050', {
certificate_chain: 'pem content', private_key: 'key content'
})
.reply(200, endpointStable)
return certs.run({app: 'example', args: ['pem_file', 'key_file'], flags: {bypass: true, confirm: 'example'}}).then(function () {
mock.done()
expect(cli.stderr).to.equal('Updating SSL certificate tokyo-1050 for example... done\n')
expect(cli.stdout).to.equal(
`Updated certificate details:
${certificateDetails}
`)
})
})
it('# bypass errors out with intermediaries', function () {
nock('https://api.heroku.com')
.get('/apps/example')
.reply(200, { 'space': null })
return assertExit(1, certs.run({app: 'example', args: ['pem_file', 'int_file', 'key_file'], flags: {bypass: true}})).then(function () {
expect(cli.stderr).to.equal(' ▸ Usage: heroku certs:add CRT KEY\n')
expect(cli.stdout).to.equal('')
})
})
it('# displays warnings', function () {
mockFile(fs, 'pem_file', 'pem content')
mockFile(fs, 'key_file', 'key content')
let mock = nock('https://api.heroku.com')
.patch('/apps/example/sni-endpoints/tokyo-1050', {
certificate_chain: 'pem content', private_key: 'key content'
})
.reply(200, endpointWarning)
return certs.run({app: 'example', args: ['pem_file', 'key_file'], flags: {bypass: true, confirm: 'example'}}).then(function () {
mock.done()
expect(unwrap(cli.stderr)).to.equal('Updating SSL certificate tokyo-1050 for example... done WARNING: ssl_cert provides no domain(s) that are configured for this Heroku app\n')
})
})
describe('shared', function () {
beforeEach(function () {
mockFile(fs, 'pem_file', 'pem content')
mockFile(fs, 'key_file', 'key content')
})
let callback = function (path, endpoint, variant) {
return nock('https://api.heroku.com', {
reqheaders: {'Accept': `application/vnd.heroku+json; version=3.${variant}`}
})
.patch(path, {
certificate_chain: 'pem content', private_key: 'key content'
})
.reply(200, endpoint)
}
let stderr = function (endpoint) {
return `Updating SSL certificate ${endpoint.name} (${endpoint.cname}) for example... done
`
}
let stdout = function (certificateDetails) {
return `Updated certificate details:
${certificateDetails}
`
}
shared.shouldHandleArgs('certs:update', 'updates an endpoint', certs, callback, {
stderr, stdout, args: ['pem_file', 'key_file'], flags: {bypass: true, confirm: 'example'}
})
sharedSsl.shouldHandleArgs('certs:update', 'updates an endpoint', certs, callback, {
stderr, stdout, args: ['pem_file', 'key_file'], flags: {bypass: true, confirm: 'example'}
})
sharedSni.shouldHandleArgs('certs:update', 'updates an endpoint', certs, callback, {
stderr, stdout, args: ['pem_file', 'key_file'], flags: {bypass: true, confirm: 'example'}
})
})
})
describe('heroku certs:update (dogwood)', function () {
beforeEach(function () {
cli.mockConsole()
sinon.stub(fs, 'readFile')
nock.cleanAll()
})
afterEach(function () {
fs.readFile.restore()
})
it('# updates an endpoint when sni-endpoints 422s', function () {
nock('https://api.heroku.com')
.get('/apps/example')
.reply(200, {
'space': {'name': 'spacely-space-1234'}
})
let mockSni = nock('https://api.heroku.com')
.get('/apps/example/sni-endpoints')
.reply(422, {
'id': 'space_app_not_supported',
'message': 'App heroku-certs-test is in a space, but space apps are not supported on this endpoint. Try `/apps/:id/ssl-endpoints` instead.'
})
nock('https://api.heroku.com')
.get('/apps/example/ssl-endpoints')
.reply(200, [endpointStable])
mockFile(fs, 'pem_file', 'pem content')
mockFile(fs, 'key_file', 'key content')
let mockPut = nock('https://api.heroku.com')
.patch('/apps/example/ssl-endpoints/tokyo-1050', {
certificate_chain: 'pem content', private_key: 'key content'
})
.reply(200, endpointStable)
return certs.run({app: 'example', args: ['pem_file', 'key_file'], flags: {name: 'tokyo-1050', confirm: 'example', bypass: true}}).then(function () {
mockSni.done()
mockPut.done()
expect(cli.stderr).to.equal('Updating SSL certificate tokyo-1050 for example... done\n')
expect(cli.stdout).to.equal(
`Updated certificate details:
${certificateDetails}
`)
})
})
})