Skip to content

Commit

Permalink
Test command-line switches, --host in particular
Browse files Browse the repository at this point in the history
  • Loading branch information
gagern authored and rvagg committed Jun 29, 2016
1 parent 128c6fd commit 67a521e
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 1 deletion.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"tap": "~2.3.1"
},
"scripts": {
"test": "tap test/*.js"
"test": "tap test/*.js test/cli/*-test.js"
},
"repository": {
"type": "git",
Expand Down
37 changes: 37 additions & 0 deletions test/cli/basic-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
var test = require('tap').test
var common = require('./common')
var serve = common.serve

test('Basic cli operation', function (t) {
serve([], function (req) {

req('/st.js', function (er, res, body) {
t.ifError(er) &&
t.equal(res.statusCode, 200) &&
t.equal(body.toString(), common.stExpect)
})

}, function (er, stdout, stderr) {
t.ifError(er)
t.match(stdout, /^listening at http:\/\/(\[::\]|0\.0\.0\.0):[0-9]+\n$/)
t.equal(stderr, '')
t.end()
})
})

test('Listening on localhost only', function (t) {
serve(["--localhost"], function (req) {

req('/st.js', function (er, res, body) {
t.ifError(er) &&
t.equal(res.statusCode, 200) &&
t.equal(body.toString(), common.stExpect)
})

}, function (er, stdout, stderr) {
t.ifError(er)
t.match(stdout, /^listening at http:\/\/localhost:[0-9]+\n$/)
t.equal(stderr, '')
t.end()
})
})
110 changes: 110 additions & 0 deletions test/cli/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use strict'

var path = require('path')
var fs = require('fs')
var request = require('request')
var child_process = require('child_process')
var bl = require('bl')

var port = process.env.PORT || 1337

var server
var stdout = bl()
var stderr = bl()

var stExpect = fs.readFileSync(require.resolve('../../st.js'), 'utf8')


// Run server with given command line arguments,
// then allow cbRequests to schedule a bunch of requests,
// finally call cbDone.
// cbRequests gets the req function as an argument.

function serve (args, cbRequests, cbDone) {
args = [require.resolve('../../bin/server.js')].concat(args || [])
var server = child_process.spawn(process.execPath, args, {
cwd: path.dirname(path.dirname(__dirname)),
stdio: ['ignore', 'pipe', 'pipe'],
env: { LANG: 'C', LC_ALL: 'C' }
})
var stdout = bl()
var stderr = bl()
server.stdout.pipe(stdout)
server.stderr.pipe(stderr)
var thingsToDo = 4 // cbRequests, exit, stdout, stderr
var code = null
var signal = null
var cbReqEr = null
var outputSeen = false
server.once('error', function (er) {
thingsToDo = -10 // only call cbDone once
cbDone(er)
})
server.once('exit', function (c, s) {
code = c
signal = s
if (!outputSeen) {
outputSeen = true
--thingsToDo
}
then()
})
stdout.once('finish', then)
stderr.once('finish', then)
server.stdout.once('data', function () {
if (outputSeen) return
outputSeen = true
try {
cbRequests(req)
} catch (er) {
cbReqEr = er
} finally {
then()
}
})

function then () {
--thingsToDo
if (thingsToDo === 3) { // all requests done, one way or another
server.kill()
} else if (thingsToDo === 0) {
var er = null
if (cbReqEr)
er = cbReqEr
else if (signal !== null && signal !== 'SIGTERM')
er = Error("Terminated by signal " + signal)
else if (code !== null && code !== 0)
er = Error("Exited with code " + code)
var o = stdout.toString(), e = stderr.toString()
if (er) console.info(o), console.error(e)
cbDone(er, o, e)
}
}

function req (url, headers, cb) {
if (typeof headers === 'function') {
cb = headers
headers = {}
}
if (!/:\/\//.test(url)) {
url = 'http://localhost:' + port + url
}
++thingsToDo
request({
encoding: null,
url: url,
headers: headers
}, function () {
try {
cb.apply(null, arguments)
} finally {
then()
}
})
}

}

module.exports.port = port
module.exports.stExpect = stExpect
module.exports.serve = serve
87 changes: 87 additions & 0 deletions test/cli/host-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
var os = require('os')
var tap = require('tap')
var test = tap.test
var common = require('./common')
var serve = common.serve

var otherAddress = (function () {
var ifaces = os.networkInterfaces()
for (var iface in ifaces) {
var addrs = ifaces[iface]
for (var i = 0; i < addrs.length; ++i) {
var addr = addrs[i].address
if (/^127\./.test(addr) || /^::1$/.test(addr)) // loopback device
continue
if (/^fe80:/.test(addr)) // link-local address
continue
return addr
}
}
return null
})()
if (!otherAddress) {
tap.fail('No non-loopback network address found', {skip: true})
test = function () {}
} else {
tap.comment('Using ' + otherAddress + ' as non-localhost address')
}

function addr2url (addr, path) {
if (/:/.test(addr)) addr = '[' + addr + ']'
addr = 'http://' + addr + ':' + common.port
if (path) addr += path
return addr
}

function testServer (name, args, addr, canConnect, cannotConnect) {
test(name, function (t) {
serve(args, function (req) {
canConnect.forEach(checkConnections(t, req, true))
cannotConnect.forEach(checkConnections(t, req, false))
}, function (err, stdout, stderr) {
t.ifError(err)
t.equal(stderr, '')
if (addr) {
t.equal(stdout, 'listening at ' + addr2url(addr) + '\n')
}
t.end()
})
})
}

function checkConnections (t, req, canConnect) {
return function (addr) {
var url = addr2url(addr, '/st.js')
req(url, function (er, res, body) {
if (canConnect) {
t.ifError(er, url) && t.equal(res.statusCode, 200, url)
} else {
t.ok(er, url)
}
})
}
}

testServer(
'Listening on all ports by default',
[], null,
['127.0.0.1', 'localhost', otherAddress], []
)

testServer(
'Restricted to localhost',
['--localhost'], 'localhost',
['127.0.0.1', 'localhost'], [otherAddress]
)

testServer(
'Restricted to non-local host',
['--host', otherAddress], otherAddress,
[otherAddress], ['127.0.0.1']
)

testServer(
'Restricted to IPv4',
['--host', '127.0.0.1'], '127.0.0.1',
['127.0.0.1'], ['::1']
)

0 comments on commit 67a521e

Please sign in to comment.