From e2dc7f96937e5d565fea16c9f56b9f5d3e427de2 Mon Sep 17 00:00:00 2001 From: indexzero Date: Sat, 21 Jul 2012 20:46:30 -0400 Subject: [PATCH] [refactor test] Finish removing old test code. --- test/helpers/http.js | 17 ++- test/helpers/ws.js | 48 +++++++- test/http/routing-table-test.js | 2 +- test/macros/http.js | 16 +-- test/macros/ws.js | 199 +++++++++++++++++++++++++++++--- test/ws/routing-table-test.js | 123 ++++---------------- test/ws/socket.io-test.js | 4 +- test/ws/ws-test.js | 18 ++- 8 files changed, 294 insertions(+), 133 deletions(-) diff --git a/test/helpers/http.js b/test/helpers/http.js index b2a477dd8..063403bed 100644 --- a/test/helpers/http.js +++ b/test/helpers/http.js @@ -103,14 +103,29 @@ exports.createProxyServer = function (options, callback) { }); }; +// +// ### function assignPortsToRoutes (routes) +// #### @routes {Object} Routing table to assign ports to +// +// Assigns dynamic ports to the `routes` for runtime testing. +// exports.assignPortsToRoutes = function (routes) { Object.keys(routes).forEach(function (source) { routes[source] = routes[source].replace('{PORT}', helpers.nextPort); }); return routes; -} +}; +// +// ### function parseRoutes (options) +// #### @options {Object} Options to use when parsing routes +// #### @protocol {string} Protocol to use in the routes +// #### @routes {Object} Routes to parse. +// +// Returns an Array of fully-parsed URLs for the source and +// target of `options.routes`. +// exports.parseRoutes = function (options) { var protocol = options.protocol || 'http', routes = options.routes; diff --git a/test/helpers/ws.js b/test/helpers/ws.js index 92fe6184c..d31dd8c93 100644 --- a/test/helpers/ws.js +++ b/test/helpers/ws.js @@ -9,11 +9,14 @@ var assert = require('assert'), async = require('async'), io = require('socket.io'), + ws = require('ws'), http = require('./http'); // // ### function createServerPair (options, callback) // #### @options {Object} Options to create target and proxy server. +// #### @target {Object} Options for the target server. +// #### @proxy {Object} Options for the proxy server. // #### @callback {function} Continuation to respond to when complete. // // Creates http target and proxy servers @@ -24,7 +27,7 @@ exports.createServerPair = function (options, callback) { // 1. Create the target server // function createTarget(next) { - exports.createServer(options.target, next); + exports.createServer(options.target, next); }, // // 2. Create the proxy server @@ -35,7 +38,30 @@ exports.createServerPair = function (options, callback) { ], callback); }; +// +// ### function createServer (options, callback) +// #### @options {Object} Options for creating the socket.io or ws server. +// #### @raw {boolean} Enables ws.Websocket server. +// +// Creates a socket.io or ws server using the specified `options`. +// exports.createServer = function (options, callback) { + return options.raw + ? exports.createWsServer(options, callback) + : exports.createSocketIoServer(options, callback); +}; + +// +// ### function createSocketIoServer (options, callback) +// #### @options {Object} Options for creating the socket.io server +// #### @port {number} Port to listen on +// #### @input {string} Input to expect from the only socket +// #### @output {string} Output to send the only socket +// +// Creates a socket.io server on the specified `options.port` which +// will expect `options.input` and then send `options.output`. +// +exports.createSocketIoServer = function (options, callback) { var server = io.listen(options.port, callback); server.sockets.on('connection', function (socket) { @@ -46,3 +72,23 @@ exports.createServer = function (options, callback) { }); }; +// +// ### function createWsServer (options, callback) +// #### @options {Object} Options for creating the ws.Server instance +// #### @port {number} Port to listen on +// #### @input {string} Input to expect from the only socket +// #### @output {string} Output to send the only socket +// +// Creates a ws.Server instance on the specified `options.port` which +// will expect `options.input` and then send `options.output`. +// +exports.createWsServer = function (options, callback) { + var server = new ws.Server({ port: options.port }, callback); + + server.on('connection', function (socket) { + socket.on('message', function (data) { + assert.equal(data, options.input); + socket.send(options.output); + }); + }); +}; \ No newline at end of file diff --git a/test/http/routing-table-test.js b/test/http/routing-table-test.js index 432100fc8..a50caa3f9 100644 --- a/test/http/routing-table-test.js +++ b/test/http/routing-table-test.js @@ -72,7 +72,7 @@ vows.describe('node-http-proxy/http/routing-table').addBatch({ ) ], function () { request({ - uri: 'http://localhost:' + that.port, + uri: 'http://127.0.0.1:' + that.port, headers: { host: 'dynamic.com' } diff --git a/test/macros/http.js b/test/macros/http.js index 7ce333896..7a74367d3 100644 --- a/test/macros/http.js +++ b/test/macros/http.js @@ -59,7 +59,7 @@ exports.assertProxied = function (options) { output = options.output || 'hello world from ' + ports.target, req = options.request || {}; - req.uri = req.uri || 'http://localhost:' + ports.proxy; + req.uri = req.uri || 'http://127.0.0.1:' + ports.proxy; return { topic: function () { @@ -79,7 +79,7 @@ exports.assertProxied = function (options) { proxy: { forward: options.forward, target: { - host: 'localhost', + host: '127.0.0.1', port: ports.target } } @@ -110,7 +110,7 @@ exports.assertInvalidProxy = function (options) { var ports = options.ports || helpers.nextPortPair, req = options.request || {}; - req.uri = req.uri || 'http://localhost:' + ports.proxy; + req.uri = req.uri || 'http://127.0.0.1:' + ports.proxy; return { topic: function () { @@ -123,7 +123,7 @@ exports.assertInvalidProxy = function (options) { port: ports.proxy, proxy: { target: { - host: 'localhost', + host: '127.0.0.1', port: ports.target } } @@ -158,13 +158,13 @@ exports.assertForwardProxied = function (options) { "and a valid forward target": exports.assertProxied({ forward: { port: forwardPort, - host: 'localhost' + host: '127.0.0.1' } }), "and an invalid forward target": exports.assertProxied({ forward: { port: 9898, - host: 'localhost' + host: '127.0.0.1' } }) }; @@ -271,7 +271,7 @@ exports.assertProxiedToRoutes = function (options, nested) { "a request to unknown.com": exports.assertRequest({ assert: { statusCode: 404 }, request: { - uri: 'http://localhost:' + port, + uri: 'http://127.0.0.1:' + port, headers: { host: 'unknown.com' } @@ -285,7 +285,7 @@ exports.assertProxiedToRoutes = function (options, nested) { locations.forEach(function (location) { context[location.source.href] = exports.assertRequest({ request: { - uri: 'http://localhost:' + port + location.source.path, + uri: 'http://127.0.0.1:' + port + location.source.path, headers: { host: location.source.hostname } diff --git a/test/macros/ws.js b/test/macros/ws.js index 92c2b6194..b520ec2e2 100644 --- a/test/macros/ws.js +++ b/test/macros/ws.js @@ -8,15 +8,57 @@ var assert = require('assert'), io = require('socket.io-client'), + WebSocket = require('ws'), helpers = require('../helpers/index'); +// +// ### function assertSendRecieve (options) +// #### @options {Object} Options for creating this assertion. +// #### @raw {boolean} Enables raw `ws.WebSocket`. +// #### @uri {string} URI of the proxy server. +// #### @input {string} Input to assert sent to the target ws server. +// #### @output {string} Output to assert from the taget ws server. +// +// Creates a `socket.io` or raw `WebSocket` connection and asserts that +// `options.input` is sent to and `options.output` is received from the +// connection. +// +exports.assertSendReceive = function (options) { + if (!options.raw) { + return { + topic: function () { + var socket = io.connect(options.uri); + socket.on('outgoing', this.callback.bind(this, null)); + socket.emit('incoming', options.input); + }, + "should send input and receive output": function (_, data) { + assert.equal(data, options.output); + } + } + } + + return { + topic: function () { + var socket = new WebSocket(options.uri); + socket.on('message', this.callback.bind(this, null)); + socket.on('open', function () { + socket.send(options.input); + }); + }, + "should send input and recieve output": function (_, data, flags) { + assert.equal(data, options.output); + } + } +} + // // ### function assertProxied (options) // #### @options {Object} Options for this test -// #### @latency {number} Latency in milliseconds for the proxy server. -// #### @ports {Object} Ports for the request (target, proxy). -// #### @input {string} Input to assert sent to the target ws server. -// #### @output {string} Output to assert from the taget ws server. +// #### @latency {number} Latency in milliseconds for the proxy server. +// #### @ports {Object} Ports for the request (target, proxy). +// #### @input {string} Input to assert sent to the target ws server. +// #### @output {string} Output to assert from the taget ws server. +// #### @raw {boolean} Enables raw `ws.Server` usage. // // Creates a complete end-to-end test for requesting against an // http proxy. @@ -24,9 +66,14 @@ var assert = require('assert'), exports.assertProxied = function (options) { options = options || {}; - var ports = options.ports || helpers.nextPortPair, - input = options.input || 'hello world to ' + ports.target, - output = options.output || 'hello world from ' + ports.target; + var ports = options.ports || helpers.nextPortPair, + input = options.input || 'hello world to ' + ports.target, + output = options.output || 'hello world from ' + ports.target, + protocol = options.protocol || 'http'; + + if (options.raw && !options.protocol) { + protocol = 'ws'; + } return { topic: function () { @@ -34,29 +81,145 @@ exports.assertProxied = function (options) { target: { input: input, output: output, - port: ports.target + port: ports.target, + raw: options.raw }, proxy: { latency: options.latency, port: ports.proxy, proxy: { target: { - host: 'localhost', + host: '127.0.0.1', port: ports.target } } } }, this.callback); }, - "the proxy WebSocket": { - topic: function () { - var socket = io.connect('http://localhost:' + ports.proxy); - socket.on('outgoing', this.callback.bind(this, null)); - socket.emit('incoming', input); - }, - "should send input and receive output": function (_, data) { - assert.equal(data, output); - } + "the proxy Websocket connection": exports.assertSendReceive({ + uri: protocol + '://127.0.0.1:' + ports.proxy, + input: input, + output: output, + raw: options.raw + }) + }; +}; + +// +// ### function assertProxiedtoRoutes (options, nested) +// #### @options {Object} Options for this ProxyTable-based test +// #### @raw {boolean} Enables ws.Server usage. +// #### @routes {Object|string} Routes to use for the proxy. +// #### @hostnameOnly {boolean} Enables hostnameOnly routing. +// #### @nested {Object} Nested vows to add to the returned context. +// +// Creates a complete end-to-end test for requesting against an +// http proxy using `options.routes`: +// +// 1. Creates target servers for all routes in `options.routes.` +// 2. Creates a proxy server. +// 3. Ensure Websocket connections to the proxy server for all route targets +// can send input and recieve output. +// +exports.assertProxiedToRoutes = function (options, nested) { + // + // Assign dynamic ports to the routes to use. + // + options.routes = helpers.http.assignPortsToRoutes(options.routes); + + // + // Parse locations from routes for making assertion requests. + // + var locations = helpers.http.parseRoutes(options), + protocol = options.protocol || 'http', + port = helpers.nextPort, + context, + proxy; + + if (options.raw && !options.protocol) { + protocol = 'ws'; + } + + if (options.filename) { + // + // If we've been passed a filename write the routes to it + // and setup the proxy options to use that file. + // + fs.writeFileSync(options.filename, JSON.stringify({ router: options.routes })); + proxy = { router: options.filename }; + } + else { + // + // Otherwise just use the routes themselves. + // + proxy = { + hostnameOnly: options.hostnameOnly, + router: options.routes + }; + } + + // + // Create the test context which creates all target + // servers for all routes and a proxy server. + // + context = { + topic: function () { + var that = this; + + async.waterfall([ + // + // 1. Create all the target servers + // + async.apply( + async.forEach, + locations, + function createRouteTarget(location, next) { + helpers.ws.createServer({ + raw: options.raw, + port: location.target.port, + output: 'hello from ' + location.source.href, + input: 'hello to ' + location.source.href + }, next); + } + ), + // + // 2. Create the proxy server + // + async.apply( + helpers.http.createProxyServer, + { + port: port, + latency: options.latency, + routing: true, + proxy: proxy + } + ) + ], function (_, server) { + // + // 3. Set the proxy server for later use + // + that.proxyServer = server; + that.callback(); + }); + + // + // 4. Assign the port to the context for later use + // + this.port = port; } }; + + // + // Add test assertions for each of the route locations. + // + locations.forEach(function (location) { + context[location.source.href] = exports.assertSendRecieve({ + uri: protocol + '://127.0.0.1:' + port + location.source.path, + output: 'hello from ' + location.source.href, + input: 'hello to ' + location.source.href, + raw: options.raw + }); + }); + + return context; }; \ No newline at end of file diff --git a/test/ws/routing-table-test.js b/test/ws/routing-table-test.js index 2e5addf5f..1d5dbb02b 100644 --- a/test/ws/routing-table-test.js +++ b/test/ws/routing-table-test.js @@ -1,104 +1,25 @@ /* - node-http-proxy-test.js: http proxy for node.js - - Copyright (c) 2010 Charlie Robbins, Marak Squires and Fedor Indutny - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -var util = require('util'), - assert = require('assert'), - argv = require('optimist').argv, - colors = require('colors'), - request = require('request'), - vows = require('vows'), - websocket = require('../../vendor/websocket'), - helpers = require('../helpers'); - -try { - var utils = require('socket.io/lib/socket.io/utils'), - io = require('socket.io'); -} -catch (ex) { - console.error('Socket.io is required for this example:'); - console.error('npm ' + 'install'.green + ' socket.io@0.6.18'.magenta); - process.exit(1); -} - -var options = helpers.parseProtocol(), - testName = [options.source.protocols.ws, options.target.protocols.ws].join('-to-'), - runner = new helpers.TestRunner(options); - -vows.describe('node-http-proxy/routing-proxy/' + testName).addBatch({ - "When using server created by httpProxy.createServer()": { - "using proxy table with no latency": { - "when an inbound message is sent from a WebSocket client": { - topic: function () { - var that = this - headers = {}; - - runner.webSocketTestWithTable({ - io: io, - host: 'localhost', - wsprotocol: options.source.protocols.ws, - protocol: options.source.protocols.http, - router: { 'localhost' : 'localhost:8230' }, - ports: { - target: 8230, - proxy: 8231 - }, - onListen: function (socket) { - socket.on('connection', function (client) { - client.on('message', function (msg) { - that.callback(null, msg, headers); - }); - }); - }, - onWsupgrade: function (req, res) { - headers.request = req; - headers.response = res.headers; - }, - onOpen: function (ws) { - ws.send(utils.encode('from client')); - } - }); - }, - "the target server should receive the message": function (err, msg, headers) { - assert.equal(msg, 'from client'); - }, - "the origin and sec-websocket-origin headers should match": function (err, msg, headers) { - assert.isString(headers.response['sec-websocket-location']); - assert.isTrue(headers.response['sec-websocket-location'].indexOf(options.source.protocols.ws) !== -1); - assert.equal(headers.request.Origin, headers.response['sec-websocket-origin']); - } - } - } - } -}).addBatch({ - "When the tests are over": { - topic: function () { - return runner.closeServers(); + * routing-tabletest.js: Test for proxying `socket.io` and raw `WebSocket` requests using a ProxyTable. + * + * (C) 2010 Nodejitsu Inc. + * MIT LICENCE + * + */ + +var vows = require('vows'), + macros = require('../macros'), + helpers = require('../helpers/index'); + +vows.describe('node-http-proxy/ws').addBatch({ + "With a valid target server": { + "and no latency": { + "using ws": macros.ws.assertProxied(), + "using socket.io": macros.ws.assertProxied({ + raw: true + }), }, - "the servers should clean up": function () { - assert.isTrue(true); - } + // "and latency": macros.websocket.assertProxied({ + // latency: 2000 + // }) } -}).export(module); +}).export(module); \ No newline at end of file diff --git a/test/ws/socket.io-test.js b/test/ws/socket.io-test.js index e7510e53b..7d80d9182 100644 --- a/test/ws/socket.io-test.js +++ b/test/ws/socket.io-test.js @@ -10,10 +10,10 @@ var vows = require('vows'), macros = require('../macros'), helpers = require('../helpers/index'); -vows.describe('node-http-proxy/ws').addBatch({ +vows.describe('node-http-proxy/ws/socket.io').addBatch({ "With a valid target server": { "and no latency": macros.ws.assertProxied(), - // "and latency": macros.websocket.assertProxied({ + // "and latency": macros.ws.assertProxied({ // latency: 2000 // }) } diff --git a/test/ws/ws-test.js b/test/ws/ws-test.js index 532598011..9427e502e 100644 --- a/test/ws/ws-test.js +++ b/test/ws/ws-test.js @@ -4,4 +4,20 @@ * (C) 2010 Nodejitsu Inc. * MIT LICENCE * - */ \ No newline at end of file + */ + +var vows = require('vows'), + macros = require('../macros'), + helpers = require('../helpers/index'); + +vows.describe('node-http-proxy/ws/WebSocket').addBatch({ + "With a valid target server": { + "and no latency": macros.ws.assertProxied({ + raw: true + }), + // "and latency": macros.ws.assertProxied({ + // raw: true, + // latency: 2000 + // }) + } +}).export(module); \ No newline at end of file