michael@0: var expect = require('chai').expect; michael@0: var util = require('./util'); michael@0: michael@0: var Connection = require('../lib/connection').Connection; michael@0: michael@0: var settings = { michael@0: SETTINGS_MAX_CONCURRENT_STREAMS: 100, michael@0: SETTINGS_INITIAL_WINDOW_SIZE: 100000 michael@0: }; michael@0: michael@0: var MAX_PRIORITY = Math.pow(2, 31) - 1; michael@0: var MAX_RANDOM_PRIORITY = 10; michael@0: michael@0: function randomPriority() { michael@0: return Math.floor(Math.random() * (MAX_RANDOM_PRIORITY + 1)); michael@0: } michael@0: michael@0: function expectPriorityOrder(priorities) { michael@0: priorities.forEach(function(bucket, priority) { michael@0: bucket.forEach(function(stream) { michael@0: expect(stream._priority).to.be.equal(priority); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: describe('connection.js', function() { michael@0: describe('Connection class', function() { michael@0: describe('method ._insert(stream)', function() { michael@0: it('should insert the stream in _streamPriorities in a place determined by stream._priority', function() { michael@0: var streams = []; michael@0: var connection = Object.create(Connection.prototype, { _streamPriorities: { value: streams }}); michael@0: var streamCount = 10; michael@0: michael@0: for (var i = 0; i < streamCount; i++) { michael@0: var stream = { _priority: randomPriority() }; michael@0: connection._insert(stream, stream._priority); michael@0: expect(connection._streamPriorities[stream._priority]).to.include(stream); michael@0: } michael@0: michael@0: expectPriorityOrder(connection._streamPriorities); michael@0: }); michael@0: }); michael@0: describe('method ._reprioritize(stream)', function() { michael@0: it('should eject and then insert the stream in _streamPriorities in a place determined by stream._priority', function() { michael@0: var streams = []; michael@0: var connection = Object.create(Connection.prototype, { _streamPriorities: { value: streams }}); michael@0: var streamCount = 10; michael@0: var oldPriority, newPriority, stream; michael@0: michael@0: for (var i = 0; i < streamCount; i++) { michael@0: oldPriority = randomPriority(); michael@0: while ((newPriority = randomPriority()) === oldPriority); michael@0: stream = { _priority: oldPriority }; michael@0: connection._insert(stream, oldPriority); michael@0: connection._reprioritize(stream, newPriority); michael@0: stream._priority = newPriority; michael@0: michael@0: expect(connection._streamPriorities[newPriority]).to.include(stream); michael@0: expect(connection._streamPriorities[oldPriority] || []).to.not.include(stream); michael@0: } michael@0: michael@0: expectPriorityOrder(streams); michael@0: }); michael@0: }); michael@0: describe('invalid operation', function() { michael@0: describe('unsolicited ping answer', function() { michael@0: it('should be ignored', function() { michael@0: var connection = new Connection(util.log, 1, settings); michael@0: michael@0: connection._receivePing({ michael@0: stream: 0, michael@0: type: 'PING', michael@0: flags: { michael@0: 'PONG': true michael@0: }, michael@0: data: new Buffer(8) michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: describe('test scenario', function() { michael@0: var c, s; michael@0: beforeEach(function() { michael@0: c = new Connection(util.log.child({ role: 'client' }), 1, settings); michael@0: s = new Connection(util.log.child({ role: 'client' }), 2, settings); michael@0: c.pipe(s).pipe(c); michael@0: }); michael@0: michael@0: describe('connection setup', function() { michael@0: it('should work as expected', function(done) { michael@0: setTimeout(function() { michael@0: // If there are no exception until this, then we're done michael@0: done(); michael@0: }, 10); michael@0: }); michael@0: }); michael@0: describe('sending/receiving a request', function() { michael@0: it('should work as expected', function(done) { michael@0: // Request and response data michael@0: var request_headers = { michael@0: ':method': 'GET', michael@0: ':path': '/' michael@0: }; michael@0: var request_data = new Buffer(0); michael@0: var response_headers = { michael@0: ':status': '200' michael@0: }; michael@0: var response_data = new Buffer('12345678', 'hex'); michael@0: michael@0: // Setting up server michael@0: s.on('stream', function(server_stream) { michael@0: server_stream.on('headers', function(headers) { michael@0: expect(headers).to.deep.equal(request_headers); michael@0: server_stream.headers(response_headers); michael@0: server_stream.end(response_data); michael@0: }); michael@0: }); michael@0: michael@0: // Sending request michael@0: var client_stream = c.createStream(); michael@0: client_stream.headers(request_headers); michael@0: client_stream.end(request_data); michael@0: michael@0: // Waiting for answer michael@0: done = util.callNTimes(2, done); michael@0: client_stream.on('headers', function(headers) { michael@0: expect(headers).to.deep.equal(response_headers); michael@0: done(); michael@0: }); michael@0: client_stream.on('readable', function() { michael@0: expect(client_stream.read()).to.deep.equal(response_data); michael@0: done(); michael@0: }); michael@0: }); michael@0: }); michael@0: describe('server push', function() { michael@0: it('should work as expected', function(done) { michael@0: var request_headers = { ':method': 'get', ':path': '/' }; michael@0: var response_headers = { ':status': '200' }; michael@0: var push_request_headers = { ':method': 'get', ':path': '/x' }; michael@0: var push_response_headers = { ':status': '200' }; michael@0: var response_content = new Buffer(10); michael@0: var push_content = new Buffer(10); michael@0: michael@0: done = util.callNTimes(5, done); michael@0: michael@0: s.on('stream', function(response) { michael@0: response.headers(response_headers); michael@0: michael@0: var pushed = response.promise(push_request_headers); michael@0: pushed.headers(push_response_headers); michael@0: pushed.end(push_content); michael@0: michael@0: response.end(response_content); michael@0: }); michael@0: michael@0: var request = c.createStream(); michael@0: request.headers(request_headers); michael@0: request.end(); michael@0: request.on('headers', function(headers) { michael@0: expect(headers).to.deep.equal(response_headers); michael@0: done(); michael@0: }); michael@0: request.on('readable', function() { michael@0: expect(request.read()).to.deep.equal(response_content); michael@0: done(); michael@0: }); michael@0: request.on('promise', function(pushed, headers) { michael@0: expect(headers).to.deep.equal(push_request_headers); michael@0: pushed.on('headers', function(headers) { michael@0: expect(headers).to.deep.equal(response_headers); michael@0: done(); michael@0: }); michael@0: pushed.on('readable', function() { michael@0: expect(pushed.read()).to.deep.equal(push_content); michael@0: done(); michael@0: }); michael@0: pushed.on('end', function() { michael@0: done(); michael@0: }); michael@0: }); michael@0: }); michael@0: }); michael@0: describe('ping from client', function() { michael@0: it('should work as expected', function(done) { michael@0: c.ping(function() { michael@0: done(); michael@0: }); michael@0: }); michael@0: }); michael@0: describe('ping from server', function() { michael@0: it('should work as expected', function(done) { michael@0: s.ping(function() { michael@0: done(); michael@0: }); michael@0: }); michael@0: }); michael@0: describe('creating two streams and then using them in reverse order', function() { michael@0: it('should not result in non-monotonous local ID ordering', function() { michael@0: var s1 = c.createStream(); michael@0: var s2 = c.createStream(); michael@0: s2.headers({ ':method': 'get', ':path': '/' }); michael@0: s1.headers({ ':method': 'get', ':path': '/' }); michael@0: }); michael@0: }); michael@0: describe('creating two promises and then using them in reverse order', function() { michael@0: it('should not result in non-monotonous local ID ordering', function(done) { michael@0: s.on('stream', function(response) { michael@0: response.headers({ ':status': '200' }); michael@0: michael@0: var p1 = s.createStream(); michael@0: var p2 = s.createStream(); michael@0: response.promise(p2, { ':method': 'get', ':path': '/p2' }); michael@0: response.promise(p1, { ':method': 'get', ':path': '/p1' }); michael@0: p2.headers({ ':status': '200' }); michael@0: p1.headers({ ':status': '200' }); michael@0: }); michael@0: michael@0: var request = c.createStream(); michael@0: request.headers({ ':method': 'get', ':path': '/' }); michael@0: michael@0: done = util.callNTimes(2, done); michael@0: request.on('promise', function() { michael@0: done(); michael@0: }); michael@0: }); michael@0: }); michael@0: describe('closing the connection on one end', function() { michael@0: it('should result in closed streams on both ends', function(done) { michael@0: done = util.callNTimes(2, done); michael@0: c.on('end', done); michael@0: s.on('end', done); michael@0: michael@0: c.close(); michael@0: }); michael@0: }); michael@0: }); michael@0: });