primer cambio
This commit is contained in:
69
node_modules/amqplib/test/bitset.js
generated
vendored
Normal file
69
node_modules/amqplib/test/bitset.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
'use strict';
|
||||
|
||||
const claire = require('claire');
|
||||
const {BitSet} = require('../lib/bitset');
|
||||
|
||||
const {
|
||||
forAll,
|
||||
data: arb,
|
||||
label,
|
||||
choice,
|
||||
transform
|
||||
} = claire;
|
||||
|
||||
const PosInt = transform(Math.floor, arb.Positive);
|
||||
|
||||
const EmptyBitSet = label('bitset', transform(
|
||||
size => {
|
||||
return new BitSet(size);
|
||||
},
|
||||
choice(arb.Nothing, PosInt)));
|
||||
|
||||
suite('BitSet', () => {
|
||||
|
||||
test('get bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.get(bit);
|
||||
}).asTest());
|
||||
|
||||
test('clear bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
b.clear(bit);
|
||||
return !b.get(bit);
|
||||
}).asTest());
|
||||
|
||||
test('next set of empty', forAll(EmptyBitSet)
|
||||
.satisfy(b => {
|
||||
return b.nextSetBit(0) === -1;
|
||||
}).asTest());
|
||||
|
||||
test('next set of one bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.nextSetBit(0) === bit;
|
||||
}).asTest());
|
||||
|
||||
test('next set same bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.nextSetBit(bit) === bit;
|
||||
}).asTest());
|
||||
|
||||
test('next set following bit', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.nextSetBit(bit+1) === -1;
|
||||
}).asTest());
|
||||
|
||||
test('next clear of empty', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => { return b.nextClearBit(bit) === bit; })
|
||||
.asTest());
|
||||
|
||||
test('next clear of one set', forAll(EmptyBitSet, PosInt)
|
||||
.satisfy((b, bit) => {
|
||||
b.set(bit);
|
||||
return b.nextClearBit(bit) === bit + 1;
|
||||
}).asTest());
|
||||
});
|
||||
379
node_modules/amqplib/test/callback_api.js
generated
vendored
Normal file
379
node_modules/amqplib/test/callback_api.js
generated
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var crypto = require('crypto');
|
||||
var api = require('../callback_api');
|
||||
var util = require('./util');
|
||||
var schedule = util.schedule;
|
||||
var randomString = util.randomString;
|
||||
var kCallback = util.kCallback;
|
||||
var domain = require('domain');
|
||||
|
||||
var URL = process.env.URL || 'amqp://localhost';
|
||||
|
||||
function connect(cb) {
|
||||
api.connect(URL, {}, cb);
|
||||
}
|
||||
|
||||
// Construct a node-style callback from a `done` function
|
||||
function doneCallback(done) {
|
||||
return function(err, _) {
|
||||
if (err == null) done();
|
||||
else done(err);
|
||||
};
|
||||
}
|
||||
|
||||
function ignore() {}
|
||||
|
||||
function twice(done) {
|
||||
var first = function(err) {
|
||||
if (err == undefined) second = done;
|
||||
else second = ignore, done(err);
|
||||
};
|
||||
var second = function(err) {
|
||||
if (err == undefined) first = done;
|
||||
else first = ignore, done(err);
|
||||
};
|
||||
return {first: function(err) { first(err); },
|
||||
second: function(err) { second(err); }};
|
||||
}
|
||||
|
||||
// Adapt 'done' to a callback that's expected to fail
|
||||
function failCallback(done) {
|
||||
return function(err, _) {
|
||||
if (err == null) done(new Error('Expected failure, got ' + val));
|
||||
else done();
|
||||
};
|
||||
}
|
||||
|
||||
function waitForMessages(ch, q, k) {
|
||||
ch.checkQueue(q, function(e, ok) {
|
||||
if (e != null) return k(e);
|
||||
else if (ok.messageCount > 0) return k(null, ok);
|
||||
else schedule(waitForMessages.bind(null, ch, q, k));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
suite('connect', function() {
|
||||
|
||||
test('at all', function(done) {
|
||||
connect(doneCallback(done));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('updateSecret', function() {
|
||||
test('updateSecret', function(done) {
|
||||
connect(kCallback(function(c) {
|
||||
c.updateSecret(Buffer.from('new secret'), 'no reason', doneCallback(done));
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
function channel_test_fn(method) {
|
||||
return function(name, options, chfun) {
|
||||
if (arguments.length === 2) {
|
||||
chfun = options;
|
||||
options = {};
|
||||
}
|
||||
test(name, function(done) {
|
||||
connect(kCallback(function(c) {
|
||||
c[method](options, kCallback(function(ch) {
|
||||
chfun(ch, done);
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
};
|
||||
}
|
||||
var channel_test = channel_test_fn('createChannel');
|
||||
var confirm_channel_test = channel_test_fn('createConfirmChannel');
|
||||
|
||||
suite('channel open', function() {
|
||||
|
||||
channel_test('at all', function(ch, done) {
|
||||
done();
|
||||
});
|
||||
|
||||
channel_test('open and close', function(ch, done) {
|
||||
ch.close(doneCallback(done));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('assert, check, delete', function() {
|
||||
|
||||
channel_test('assert, check, delete queue', function(ch, done) {
|
||||
ch.assertQueue('test.cb.queue', {}, kCallback(function(q) {
|
||||
ch.checkQueue('test.cb.queue', kCallback(function(ok) {
|
||||
ch.deleteQueue('test.cb.queue', {}, doneCallback(done));
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
|
||||
channel_test('assert, check, delete exchange', function(ch, done) {
|
||||
ch.assertExchange(
|
||||
'test.cb.exchange', 'topic', {}, kCallback(function(ex) {
|
||||
ch.checkExchange('test.cb.exchange', kCallback(function(ok) {
|
||||
ch.deleteExchange('test.cb.exchange', {}, doneCallback(done));
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
|
||||
channel_test('fail on check non-queue', function(ch, done) {
|
||||
var both = twice(done);
|
||||
ch.on('error', failCallback(both.first));
|
||||
ch.checkQueue('test.cb.nothere', failCallback(both.second));
|
||||
});
|
||||
|
||||
channel_test('fail on check non-exchange', function(ch, done) {
|
||||
var both = twice(done);
|
||||
ch.on('error', failCallback(both.first));
|
||||
ch.checkExchange('test.cb.nothere', failCallback(both.second));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('bindings', function() {
|
||||
|
||||
channel_test('bind queue', function(ch, done) {
|
||||
ch.assertQueue('test.cb.bindq', {}, kCallback(function(q) {
|
||||
ch.assertExchange(
|
||||
'test.cb.bindex', 'fanout', {}, kCallback(function(ex) {
|
||||
ch.bindQueue(q.queue, ex.exchange, '', {},
|
||||
doneCallback(done));
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
|
||||
channel_test('bind exchange', function(ch, done) {
|
||||
ch.assertExchange(
|
||||
'test.cb.bindex1', 'fanout', {}, kCallback(function(ex1) {
|
||||
ch.assertExchange(
|
||||
'test.cb.bindex2', 'fanout', {}, kCallback(function(ex2) {
|
||||
ch.bindExchange(ex1.exchange,
|
||||
ex2.exchange, '', {},
|
||||
doneCallback(done));
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('sending messages', function() {
|
||||
|
||||
channel_test('send to queue and consume noAck', function(ch, done) {
|
||||
var msg = randomString();
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
ch.consume(q.queue, function(m) {
|
||||
if (m.content.toString() == msg) done();
|
||||
else done(new Error("message content doesn't match:" +
|
||||
msg + " =/= " + m.content.toString()));
|
||||
}, {noAck: true, exclusive: true});
|
||||
ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
});
|
||||
});
|
||||
|
||||
channel_test('send to queue and consume ack', function(ch, done) {
|
||||
var msg = randomString();
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
ch.consume(q.queue, function(m) {
|
||||
if (m.content.toString() == msg) {
|
||||
ch.ack(m);
|
||||
done();
|
||||
}
|
||||
else done(new Error("message content doesn't match:" +
|
||||
msg + " =/= " + m.content.toString()));
|
||||
}, {noAck: false, exclusive: true});
|
||||
ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
});
|
||||
});
|
||||
|
||||
channel_test('send to and get from queue', function(ch, done) {
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e != null) return done(e);
|
||||
var msg = randomString();
|
||||
ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
waitForMessages(ch, q.queue, function(e, _) {
|
||||
if (e != null) return done(e);
|
||||
ch.get(q.queue, {noAck: true}, function(e, m) {
|
||||
if (e != null)
|
||||
return done(e);
|
||||
else if (!m)
|
||||
return done(new Error('Empty (false) not expected'));
|
||||
else if (m.content.toString() == msg)
|
||||
return done();
|
||||
else
|
||||
return done(
|
||||
new Error('Messages do not match: ' +
|
||||
msg + ' =/= ' + m.content.toString()));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var channelOptions = {};
|
||||
|
||||
channel_test('find high watermark', function(ch, done) {
|
||||
var msg = randomString();
|
||||
var baseline = 0;
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
while (ch.sendToQueue(q.queue, Buffer.from(msg))) {
|
||||
baseline++;
|
||||
};
|
||||
channelOptions.highWaterMark = baseline * 2;
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
channel_test('set high watermark', channelOptions, function(ch, done) {
|
||||
var msg = randomString();
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
var ok;
|
||||
for (var i = 0; i < channelOptions.highWaterMark; i++) {
|
||||
ok = ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
assert.equal(ok, true);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('ConfirmChannel', function() {
|
||||
|
||||
confirm_channel_test('Receive confirmation', function(ch, done) {
|
||||
// An unroutable message, on the basis that you're not allowed a
|
||||
// queue with an empty name, and you can't make bindings to the
|
||||
// default exchange. Tricky eh?
|
||||
ch.publish('', '', Buffer.from('foo'), {}, done);
|
||||
});
|
||||
|
||||
confirm_channel_test('Wait for confirms', function(ch, done) {
|
||||
for (var i=0; i < 1000; i++) {
|
||||
ch.publish('', '', Buffer.from('foo'), {});
|
||||
}
|
||||
ch.waitForConfirms(done);
|
||||
});
|
||||
|
||||
var channelOptions = {};
|
||||
|
||||
confirm_channel_test('find high watermark', function(ch, done) {
|
||||
var msg = randomString();
|
||||
var baseline = 0;
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
while (ch.sendToQueue(q.queue, Buffer.from(msg))) {
|
||||
baseline++;
|
||||
};
|
||||
channelOptions.highWaterMark = baseline * 2;
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
confirm_channel_test('set high watermark', channelOptions, function(ch, done) {
|
||||
var msg = randomString();
|
||||
ch.assertQueue('', {exclusive: true}, function(e, q) {
|
||||
if (e !== null) return done(e);
|
||||
var ok;
|
||||
for (var i = 0; i < channelOptions.highWaterMark; i++) {
|
||||
ok = ch.sendToQueue(q.queue, Buffer.from(msg));
|
||||
assert.equal(ok, true);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite("Error handling", function() {
|
||||
|
||||
/*
|
||||
I don't like having to do this, but there appears to be something
|
||||
broken about domains in Node.JS v0.8 and mocha. Apparently it has to
|
||||
do with how mocha and domains hook into error propogation:
|
||||
https://github.com/visionmedia/mocha/issues/513 (summary: domains in
|
||||
Node.JS v0.8 don't prevent uncaughtException from firing, and that's
|
||||
what mocha uses to detect .. an uncaught exception).
|
||||
|
||||
Using domains with amqplib *does* work in practice in Node.JS v0.8:
|
||||
that is, it's possible to throw an exception in a callback and deal
|
||||
with it in the active domain, and thereby avoid it crashing the
|
||||
program.
|
||||
*/
|
||||
if (util.versionGreaterThan(process.versions.node, '0.8')) {
|
||||
test('Throw error in connection open callback', function(done) {
|
||||
var dom = domain.createDomain();
|
||||
dom.on('error', failCallback(done));
|
||||
connect(dom.bind(function(err, conn) {
|
||||
throw new Error('Spurious connection open callback error');
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: refactor {error_test, channel_test}
|
||||
function error_test(name, fun) {
|
||||
test(name, function(done) {
|
||||
var dom = domain.createDomain();
|
||||
dom.run(function() {
|
||||
connect(kCallback(function(c) {
|
||||
// Seems like there were some unironed wrinkles in 0.8's
|
||||
// implementation of domains; explicitly adding the connection
|
||||
// to the domain makes sure any exception thrown in the course
|
||||
// of processing frames is handled by the domain. For other
|
||||
// versions of Node.JS, this ends up being belt-and-braces.
|
||||
dom.add(c);
|
||||
c.createChannel(kCallback(function(ch) {
|
||||
fun(ch, done, dom);
|
||||
}, done));
|
||||
}, done));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
error_test('Channel open callback throws an error', function(ch, done, dom) {
|
||||
dom.on('error', failCallback(done));
|
||||
throw new Error('Error in open callback');
|
||||
});
|
||||
|
||||
error_test('RPC callback throws error', function(ch, done, dom) {
|
||||
dom.on('error', failCallback(done));
|
||||
ch.prefetch(0, false, function(err, ok) {
|
||||
throw new Error('Spurious callback error');
|
||||
});
|
||||
});
|
||||
|
||||
error_test('Get callback throws error', function(ch, done, dom) {
|
||||
dom.on('error', failCallback(done));
|
||||
ch.assertQueue('test.cb.get-with-error', {}, function(err, ok) {
|
||||
ch.get('test.cb.get-with-error', {noAck: true}, function() {
|
||||
throw new Error('Spurious callback error');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
error_test('Consume callback throws error', function(ch, done, dom) {
|
||||
dom.on('error', failCallback(done));
|
||||
ch.assertQueue('test.cb.consume-with-error', {}, function(err, ok) {
|
||||
ch.consume('test.cb.consume-with-error', ignore, {noAck: true}, function() {
|
||||
throw new Error('Spurious callback error');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
error_test('Get from non-queue invokes error k', function(ch, done, dom) {
|
||||
var both = twice(failCallback(done));
|
||||
dom.on('error', both.first);
|
||||
ch.get('', {}, both.second);
|
||||
});
|
||||
|
||||
error_test('Consume from non-queue invokes error k', function(ch, done, dom) {
|
||||
var both = twice(failCallback(done));
|
||||
dom.on('error', both.first);
|
||||
ch.consume('', both.second);
|
||||
});
|
||||
|
||||
});
|
||||
621
node_modules/amqplib/test/channel.js
generated
vendored
Normal file
621
node_modules/amqplib/test/channel.js
generated
vendored
Normal file
@@ -0,0 +1,621 @@
|
||||
// Test the channel machinery
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var promisify = require('util').promisify;
|
||||
var Channel = require('../lib/channel').Channel;
|
||||
var Connection = require('../lib/connection').Connection;
|
||||
var util = require('./util');
|
||||
var succeed = util.succeed, fail = util.fail, latch = util.latch;
|
||||
var completes = util.completes;
|
||||
var defs = require('../lib/defs');
|
||||
var conn_handshake = require('./connection').connection_handshake;
|
||||
var OPEN_OPTS = require('./connection').OPEN_OPTS;
|
||||
|
||||
var LOG_ERRORS = process.env.LOG_ERRORS;
|
||||
|
||||
function baseChannelTest(client, server) {
|
||||
return function(done) {
|
||||
var bothDone = latch(2, done);
|
||||
var pair = util.socketPair();
|
||||
var c = new Connection(pair.client);
|
||||
|
||||
if (LOG_ERRORS) c.on('error', console.warn);
|
||||
|
||||
c.open(OPEN_OPTS, function(err, ok) {
|
||||
if (err === null) client(c, bothDone);
|
||||
else fail(bothDone);
|
||||
});
|
||||
|
||||
pair.server.read(8); // discard the protocol header
|
||||
var s = util.runServer(pair.server, function(send, wait) {
|
||||
conn_handshake(send, wait)
|
||||
.then(function() {
|
||||
server(send, wait, bothDone);
|
||||
}, fail(bothDone));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function channelTest(client, server) {
|
||||
return baseChannelTest(
|
||||
function(conn, done) {
|
||||
var ch = new Channel(conn);
|
||||
if (LOG_ERRORS) ch.on('error', console.warn);
|
||||
client(ch, done, conn);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
channel_handshake(send, wait)
|
||||
.then(function(ch) {
|
||||
return server(send, wait, done, ch);
|
||||
}).then(null, fail(done)); // so you can return a promise to let
|
||||
// errors bubble out
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function channel_handshake(send, wait) {
|
||||
return wait(defs.ChannelOpen)()
|
||||
.then(function(open) {
|
||||
assert.notEqual(0, open.channel);
|
||||
send(defs.ChannelOpenOk, {channelId: Buffer.from('')}, open.channel);
|
||||
return open.channel;
|
||||
});
|
||||
}
|
||||
|
||||
// fields for deliver and publish and get-ok
|
||||
var DELIVER_FIELDS = {
|
||||
consumerTag: 'fake',
|
||||
deliveryTag: 1,
|
||||
redelivered: false,
|
||||
exchange: 'foo',
|
||||
routingKey: 'bar',
|
||||
replyCode: defs.constants.NO_ROUTE,
|
||||
replyText: 'derp',
|
||||
};
|
||||
|
||||
function open(ch) {
|
||||
ch.allocate();
|
||||
return promisify(function(cb) {
|
||||
ch._rpc(defs.ChannelOpen, {outOfBand: ''}, defs.ChannelOpenOk, cb);
|
||||
})();
|
||||
}
|
||||
|
||||
suite("channel open and close", function() {
|
||||
|
||||
test("open", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch).then(succeed(done), fail(done));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
done();
|
||||
}));
|
||||
|
||||
test("bad server", baseChannelTest(
|
||||
function(c, done) {
|
||||
var ch = new Channel(c);
|
||||
open(ch).then(fail(done), succeed(done));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
return wait(defs.ChannelOpen)()
|
||||
.then(function(open) {
|
||||
send(defs.ChannelCloseOk, {}, open.channel);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("open, close", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch)
|
||||
.then(function() {
|
||||
return new Promise(function(resolve) {
|
||||
ch.closeBecause("Bye", defs.constants.REPLY_SUCCESS, resolve);
|
||||
});
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
return wait(defs.ChannelClose)()
|
||||
.then(function(close) {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("server close", channelTest(
|
||||
function(ch, done) {
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(504, error.code);
|
||||
assert.strictEqual(0, error.classId);
|
||||
assert.strictEqual(0, error.methodId);
|
||||
succeed(done)();
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
send(defs.ChannelClose, {
|
||||
replyText: 'Forced close',
|
||||
replyCode: defs.constants.CHANNEL_ERROR,
|
||||
classId: 0, methodId: 0
|
||||
}, ch);
|
||||
wait(defs.ChannelCloseOk)()
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("overlapping channel/server close", channelTest(
|
||||
function(ch, done, conn) {
|
||||
var both = latch(2, done);
|
||||
conn.on('error', succeed(both));
|
||||
ch.on('close', succeed(both));
|
||||
open(ch).then(function() {
|
||||
ch.closeBecause("Bye", defs.constants.REPLY_SUCCESS);
|
||||
}, fail(both));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.ChannelClose)()
|
||||
.then(function() {
|
||||
send(defs.ConnectionClose, {
|
||||
replyText: 'Got there first',
|
||||
replyCode: defs.constants.INTERNAL_ERROR,
|
||||
classId: 0, methodId: 0
|
||||
}, 0);
|
||||
})
|
||||
.then(wait(defs.ConnectionCloseOk))
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("double close", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch).then(function() {
|
||||
ch.closeBecause("First close", defs.constants.REPLY_SUCCESS);
|
||||
// NB no synchronisation, we do this straight away
|
||||
assert.throws(function() {
|
||||
ch.closeBecause("Second close", defs.constants.REPLY_SUCCESS);
|
||||
});
|
||||
}).then(succeed(done), fail(done));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.ChannelClose)()
|
||||
.then(function() {
|
||||
send(defs.ChannelCloseOk, {
|
||||
}, ch);
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
}); //suite
|
||||
|
||||
suite("channel machinery", function() {
|
||||
|
||||
test("RPC", channelTest(
|
||||
function(ch, done) {
|
||||
var rpcLatch = latch(3, done);
|
||||
open(ch).then(function() {
|
||||
|
||||
function wheeboom(err, f) {
|
||||
if (err !== null) rpcLatch(err);
|
||||
else rpcLatch();
|
||||
}
|
||||
|
||||
var fields = {
|
||||
prefetchCount: 10,
|
||||
prefetchSize: 0,
|
||||
global: false
|
||||
};
|
||||
|
||||
ch._rpc(defs.BasicQos, fields, defs.BasicQosOk, wheeboom);
|
||||
ch._rpc(defs.BasicQos, fields, defs.BasicQosOk, wheeboom);
|
||||
ch._rpc(defs.BasicQos, fields, defs.BasicQosOk, wheeboom);
|
||||
}).then(null, fail(rpcLatch));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
function sendOk(f) {
|
||||
send(defs.BasicQosOk, {}, ch);
|
||||
}
|
||||
|
||||
return wait(defs.BasicQos)()
|
||||
.then(sendOk)
|
||||
.then(wait(defs.BasicQos))
|
||||
.then(sendOk)
|
||||
.then(wait(defs.BasicQos))
|
||||
.then(sendOk)
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("Bad RPC", channelTest(
|
||||
function(ch, done) {
|
||||
// We want to see the RPC rejected and the channel closed (with an
|
||||
// error)
|
||||
var errLatch = latch(2, done);
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(505, error.code);
|
||||
assert.strictEqual(60, error.classId);
|
||||
assert.strictEqual(72, error.methodId);
|
||||
succeed(errLatch)();
|
||||
});
|
||||
|
||||
open(ch)
|
||||
.then(function() {
|
||||
ch._rpc(defs.BasicRecover, {requeue: true}, defs.BasicRecoverOk,
|
||||
function(err) {
|
||||
if (err !== null) errLatch();
|
||||
else errLatch(new Error('Expected RPC failure'));
|
||||
});
|
||||
}, fail(errLatch));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
return wait()()
|
||||
.then(function() {
|
||||
send(defs.BasicGetEmpty, {clusterId: ''}, ch);
|
||||
}) // oh wait! that was wrong! expect a channel close
|
||||
.then(wait(defs.ChannelClose))
|
||||
.then(function() {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("RPC on closed channel", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
|
||||
var close = new Promise(function(resolve) {
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(504, error.code);
|
||||
assert.strictEqual(0, error.classId);
|
||||
assert.strictEqual(0, error.methodId);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
function failureCb(resolve, reject) {
|
||||
return function(err) {
|
||||
if (err !== null) resolve();
|
||||
else reject();
|
||||
}
|
||||
}
|
||||
|
||||
var fail1 = new Promise(function(resolve, reject) {
|
||||
return ch._rpc(defs.BasicRecover, {requeue:true}, defs.BasicRecoverOk,
|
||||
failureCb(resolve, reject));
|
||||
});
|
||||
|
||||
var fail2 = new Promise(function(resolve, reject) {
|
||||
return ch._rpc(defs.BasicRecover, {requeue:true}, defs.BasicRecoverOk,
|
||||
failureCb(resolve, reject));
|
||||
});
|
||||
|
||||
Promise.all([close, fail1, fail2])
|
||||
.then(succeed(done))
|
||||
.catch(fail(done));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicRecover)()
|
||||
.then(function() {
|
||||
send(defs.ChannelClose, {
|
||||
replyText: 'Nuh-uh!',
|
||||
replyCode: defs.constants.CHANNEL_ERROR,
|
||||
methodId: 0, classId: 0
|
||||
}, ch);
|
||||
return wait(defs.ChannelCloseOk);
|
||||
})
|
||||
.then(succeed(done))
|
||||
.catch(fail(done));
|
||||
}));
|
||||
|
||||
test("publish all < single chunk threshold", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch)
|
||||
.then(function() {
|
||||
ch.sendMessage({
|
||||
exchange: 'foo', routingKey: 'bar',
|
||||
mandatory: false, immediate: false, ticket: 0
|
||||
}, {}, Buffer.from('foobar'));
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicPublish)()
|
||||
.then(wait(defs.BasicProperties))
|
||||
.then(wait(undefined)) // content frame
|
||||
.then(function(f) {
|
||||
assert.equal('foobar', f.content.toString());
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("publish content > single chunk threshold", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
completes(function() {
|
||||
ch.sendMessage({
|
||||
exchange: 'foo', routingKey: 'bar',
|
||||
mandatory: false, immediate: false, ticket: 0
|
||||
}, {}, Buffer.alloc(3000));
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicPublish)()
|
||||
.then(wait(defs.BasicProperties))
|
||||
.then(wait(undefined)) // content frame
|
||||
.then(function(f) {
|
||||
assert.equal(3000, f.content.length);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("publish method & headers > threshold", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
completes(function() {
|
||||
ch.sendMessage({
|
||||
exchange: 'foo', routingKey: 'bar',
|
||||
mandatory: false, immediate: false, ticket: 0
|
||||
}, {
|
||||
headers: {foo: Buffer.alloc(3000)}
|
||||
}, Buffer.from('foobar'));
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicPublish)()
|
||||
.then(wait(defs.BasicProperties))
|
||||
.then(wait(undefined)) // content frame
|
||||
.then(function(f) {
|
||||
assert.equal('foobar', f.content.toString());
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("publish zero-length message", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
completes(function() {
|
||||
ch.sendMessage({
|
||||
exchange: 'foo', routingKey: 'bar',
|
||||
mandatory: false, immediate: false, ticket: 0
|
||||
}, {}, Buffer.alloc(0));
|
||||
ch.sendMessage({
|
||||
exchange: 'foo', routingKey: 'bar',
|
||||
mandatory: false, immediate: false, ticket: 0
|
||||
}, {}, Buffer.alloc(0));
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
wait(defs.BasicPublish)()
|
||||
.then(wait(defs.BasicProperties))
|
||||
// no content frame for a zero-length message
|
||||
.then(wait(defs.BasicPublish))
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("delivery", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
ch.on('delivery', function(m) {
|
||||
completes(function() {
|
||||
assert.equal('barfoo', m.content.toString());
|
||||
}, done);
|
||||
});
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('barfoo'));
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("zero byte msg", channelTest(
|
||||
function(ch, done) {
|
||||
open(ch);
|
||||
ch.on('delivery', function(m) {
|
||||
completes(function() {
|
||||
assert.deepEqual(Buffer.alloc(0), m.content);
|
||||
}, done);
|
||||
});
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from(''));
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("bad delivery", channelTest(
|
||||
function(ch, done) {
|
||||
var errorAndClose = latch(2, done);
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(505, error.code);
|
||||
assert.strictEqual(60, error.classId);
|
||||
assert.strictEqual(60, error.methodId);
|
||||
succeed(errorAndClose)();
|
||||
});
|
||||
ch.on('close', succeed(errorAndClose));
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch);
|
||||
// now send another deliver without having sent the content
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch);
|
||||
return wait(defs.ChannelClose)()
|
||||
.then(function() {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("bad content send", channelTest(
|
||||
function(ch, done) {
|
||||
completes(function() {
|
||||
open(ch);
|
||||
assert.throws(function() {
|
||||
ch.sendMessage({routingKey: 'foo',
|
||||
exchange: 'amq.direct'},
|
||||
{}, null);
|
||||
});
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
done();
|
||||
}));
|
||||
|
||||
test("bad properties send", channelTest(
|
||||
function(ch, done) {
|
||||
completes(function() {
|
||||
open(ch);
|
||||
assert.throws(function() {
|
||||
ch.sendMessage({routingKey: 'foo',
|
||||
exchange: 'amq.direct'},
|
||||
{contentEncoding: 7},
|
||||
Buffer.from('foobar'));
|
||||
});
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
done();
|
||||
}));
|
||||
|
||||
test("bad consumer", channelTest(
|
||||
function(ch, done) {
|
||||
var errorAndClose = latch(2, done);
|
||||
ch.on('delivery', function() {
|
||||
throw new Error("I am a bad consumer");
|
||||
});
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(541, error.code);
|
||||
assert.strictEqual(undefined, error.classId);
|
||||
assert.strictEqual(undefined, error.methodId);
|
||||
succeed(errorAndClose)();
|
||||
});
|
||||
ch.on('close', succeed(errorAndClose));
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch, Buffer.from('barfoo'));
|
||||
return wait(defs.ChannelClose)()
|
||||
.then(function() {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("bad send in consumer", channelTest(
|
||||
function(ch, done) {
|
||||
var errorAndClose = latch(2, done);
|
||||
ch.on('close', succeed(errorAndClose));
|
||||
ch.on('error', function(error) {
|
||||
assert.strictEqual(541, error.code);
|
||||
assert.strictEqual(undefined, error.classId);
|
||||
assert.strictEqual(undefined, error.methodId);
|
||||
succeed(errorAndClose)();
|
||||
});
|
||||
|
||||
ch.on('delivery', function() {
|
||||
ch.sendMessage({routingKey: 'foo',
|
||||
exchange: 'amq.direct'},
|
||||
{}, null); // can't send null
|
||||
});
|
||||
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicDeliver, DELIVER_FIELDS, ch,
|
||||
Buffer.from('barfoo'));
|
||||
}, done);
|
||||
return wait(defs.ChannelClose)()
|
||||
.then(function() {
|
||||
send(defs.ChannelCloseOk, {}, ch);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("return", channelTest(
|
||||
function(ch, done) {
|
||||
ch.on('return', function(m) {
|
||||
completes(function() {
|
||||
assert.equal('barfoo', m.content.toString());
|
||||
}, done);
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicReturn, DELIVER_FIELDS, ch, Buffer.from('barfoo'));
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("cancel", channelTest(
|
||||
function(ch, done) {
|
||||
ch.on('cancel', function(f) {
|
||||
completes(function() {
|
||||
assert.equal('product of society', f.consumerTag);
|
||||
}, done);
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicCancel, {
|
||||
consumerTag: 'product of society',
|
||||
nowait: false
|
||||
}, ch);
|
||||
}, done);
|
||||
}));
|
||||
|
||||
function confirmTest(variety, Method) {
|
||||
return test('confirm ' + variety, channelTest(
|
||||
function(ch, done) {
|
||||
ch.on(variety, function(f) {
|
||||
completes(function() {
|
||||
assert.equal(1, f.deliveryTag);
|
||||
}, done);
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(Method, {
|
||||
deliveryTag: 1,
|
||||
multiple: false
|
||||
}, ch);
|
||||
}, done);
|
||||
}));
|
||||
}
|
||||
|
||||
confirmTest("ack", defs.BasicAck);
|
||||
confirmTest("nack", defs.BasicNack);
|
||||
|
||||
test("out-of-order acks", channelTest(
|
||||
function(ch, done) {
|
||||
var allConfirms = latch(3, function() {
|
||||
completes(function() {
|
||||
assert.equal(0, ch.unconfirmed.length);
|
||||
assert.equal(4, ch.lwm);
|
||||
}, done);
|
||||
});
|
||||
ch.pushConfirmCallback(allConfirms);
|
||||
ch.pushConfirmCallback(allConfirms);
|
||||
ch.pushConfirmCallback(allConfirms);
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicAck, {deliveryTag: 2, multiple: false}, ch);
|
||||
send(defs.BasicAck, {deliveryTag: 3, multiple: false}, ch);
|
||||
send(defs.BasicAck, {deliveryTag: 1, multiple: false}, ch);
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("not all out-of-order acks", channelTest(
|
||||
function(ch, done) {
|
||||
var allConfirms = latch(2, function() {
|
||||
completes(function() {
|
||||
assert.equal(1, ch.unconfirmed.length);
|
||||
assert.equal(3, ch.lwm);
|
||||
}, done);
|
||||
});
|
||||
ch.pushConfirmCallback(allConfirms); // tag = 1
|
||||
ch.pushConfirmCallback(allConfirms); // tag = 2
|
||||
ch.pushConfirmCallback(function() {
|
||||
done(new Error('Confirm callback should not be called'));
|
||||
});
|
||||
open(ch);
|
||||
},
|
||||
function(send, wait, done, ch) {
|
||||
completes(function() {
|
||||
send(defs.BasicAck, {deliveryTag: 2, multiple: false}, ch);
|
||||
send(defs.BasicAck, {deliveryTag: 1, multiple: false}, ch);
|
||||
}, done);
|
||||
}));
|
||||
|
||||
});
|
||||
606
node_modules/amqplib/test/channel_api.js
generated
vendored
Normal file
606
node_modules/amqplib/test/channel_api.js
generated
vendored
Normal file
@@ -0,0 +1,606 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var api = require('../channel_api');
|
||||
var util = require('./util');
|
||||
var succeed = util.succeed, fail = util.fail;
|
||||
var schedule = util.schedule;
|
||||
var randomString = util.randomString;
|
||||
var promisify = require('util').promisify;
|
||||
|
||||
var URL = process.env.URL || 'amqp://localhost';
|
||||
|
||||
function connect() {
|
||||
return api.connect(URL);
|
||||
}
|
||||
|
||||
// Expect this promise to fail, and flip the results accordingly.
|
||||
function expectFail(promise) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
return promise.then(reject).catch(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
// I'll rely on operations being rejected, rather than the channel
|
||||
// close error, to detect failure.
|
||||
function ignore () {}
|
||||
function ignoreErrors(c) {
|
||||
c.on('error', ignore); return c;
|
||||
}
|
||||
function logErrors(c) {
|
||||
c.on('error', console.warn); return c;
|
||||
}
|
||||
|
||||
// Run a test with `name`, given a function that takes an open
|
||||
// channel, and returns a promise that is resolved on test success or
|
||||
// rejected on test failure.
|
||||
function channel_test(chmethod, name, chfun) {
|
||||
test(name, function(done) {
|
||||
connect(URL).then(logErrors).then(function(c) {
|
||||
c[chmethod]().then(ignoreErrors).then(chfun)
|
||||
.then(succeed(done), fail(done))
|
||||
// close the connection regardless of what happens with the test
|
||||
.finally(function() {c.close();});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var chtest = channel_test.bind(null, 'createChannel');
|
||||
|
||||
suite("connect", function() {
|
||||
|
||||
test("at all", function(done) {
|
||||
connect(URL).then(function(c) {
|
||||
return c.close()
|
||||
;}).then(succeed(done), fail(done));
|
||||
});
|
||||
|
||||
chtest("create channel", ignore); // i.e., just don't bork
|
||||
|
||||
});
|
||||
|
||||
suite("updateSecret", function() {
|
||||
test("updateSecret", function(done) {
|
||||
connect().then(function(c) {
|
||||
c.updateSecret(Buffer.from("new secret"), "no reason")
|
||||
.then(succeed(done), fail(done))
|
||||
.finally(function() { c.close(); });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var QUEUE_OPTS = {durable: false};
|
||||
var EX_OPTS = {durable: false};
|
||||
|
||||
suite("assert, check, delete", function() {
|
||||
|
||||
chtest("assert and check queue", function(ch) {
|
||||
return ch.assertQueue('test.check-queue', QUEUE_OPTS)
|
||||
.then(function(qok) {
|
||||
return ch.checkQueue('test.check-queue');
|
||||
});
|
||||
});
|
||||
|
||||
chtest("assert and check exchange", function(ch) {
|
||||
return ch.assertExchange('test.check-exchange', 'direct', EX_OPTS)
|
||||
.then(function(eok) {
|
||||
assert.equal('test.check-exchange', eok.exchange);
|
||||
return ch.checkExchange('test.check-exchange');
|
||||
});
|
||||
});
|
||||
|
||||
chtest("fail on reasserting queue with different options",
|
||||
function(ch) {
|
||||
var q = 'test.reassert-queue';
|
||||
return ch.assertQueue(
|
||||
q, {durable: false, autoDelete: true})
|
||||
.then(function() {
|
||||
return expectFail(
|
||||
ch.assertQueue(q, {durable: false,
|
||||
autoDelete: false}));
|
||||
});
|
||||
});
|
||||
|
||||
chtest("fail on checking a queue that's not there", function(ch) {
|
||||
return expectFail(ch.checkQueue('test.random-' + randomString()));
|
||||
});
|
||||
|
||||
chtest("fail on checking an exchange that's not there", function(ch) {
|
||||
return expectFail(ch.checkExchange('test.random-' + randomString()));
|
||||
});
|
||||
|
||||
chtest("fail on reasserting exchange with different type",
|
||||
function(ch) {
|
||||
var ex = 'test.reassert-ex';
|
||||
return ch.assertExchange(ex, 'fanout', EX_OPTS)
|
||||
.then(function() {
|
||||
return expectFail(
|
||||
ch.assertExchange(ex, 'direct', EX_OPTS));
|
||||
});
|
||||
});
|
||||
|
||||
chtest("channel break on publishing to non-exchange", function(ch) {
|
||||
return new Promise(function(resolve) {
|
||||
ch.on('error', resolve);
|
||||
ch.publish(randomString(), '', Buffer.from('foobar'));
|
||||
});
|
||||
});
|
||||
|
||||
chtest("delete queue", function(ch) {
|
||||
var q = 'test.delete-queue';
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.checkQueue(q)])
|
||||
.then(function() {
|
||||
return ch.deleteQueue(q);})
|
||||
.then(function() {
|
||||
return expectFail(ch.checkQueue(q));});
|
||||
});
|
||||
|
||||
chtest("delete exchange", function(ch) {
|
||||
var ex = 'test.delete-exchange';
|
||||
return Promise.all([
|
||||
ch.assertExchange(ex, 'fanout', EX_OPTS),
|
||||
ch.checkExchange(ex)])
|
||||
.then(function() {
|
||||
return ch.deleteExchange(ex);})
|
||||
.then(function() {
|
||||
return expectFail(ch.checkExchange(ex));});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Wait for the queue to meet the condition; useful for waiting for
|
||||
// messages to arrive, for example.
|
||||
function waitForQueue(q, condition) {
|
||||
return connect(URL).then(function(c) {
|
||||
return c.createChannel()
|
||||
.then(function(ch) {
|
||||
return ch.checkQueue(q).then(function(qok) {
|
||||
function check() {
|
||||
return ch.checkQueue(q).then(function(qok) {
|
||||
if (condition(qok)) {
|
||||
c.close();
|
||||
return qok;
|
||||
}
|
||||
else schedule(check);
|
||||
});
|
||||
}
|
||||
return check();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Return a promise that resolves when the queue has at least `num`
|
||||
// messages. If num is not supplied its assumed to be 1.
|
||||
function waitForMessages(q, num) {
|
||||
var min = (num === undefined) ? 1 : num;
|
||||
return waitForQueue(q, function(qok) {
|
||||
return qok.messageCount >= min;
|
||||
});
|
||||
}
|
||||
|
||||
suite("sendMessage", function() {
|
||||
|
||||
// publish different size messages
|
||||
chtest("send to queue and get from queue", function(ch) {
|
||||
var q = 'test.send-to-q';
|
||||
var msg = randomString();
|
||||
return Promise.all([ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from(msg));
|
||||
return waitForMessages(q);
|
||||
})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: true});
|
||||
})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.equal(msg, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
chtest("send (and get) zero content to queue", function(ch) {
|
||||
var q = 'test.send-to-q';
|
||||
var msg = Buffer.alloc(0);
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, msg);
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: true});})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.deepEqual(msg, m.content);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite("binding, consuming", function() {
|
||||
|
||||
// bind, publish, get
|
||||
chtest("route message", function(ch) {
|
||||
var ex = 'test.route-message';
|
||||
var q = 'test.route-message-q';
|
||||
var msg = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertExchange(ex, 'fanout', EX_OPTS),
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
ch.bindQueue(q, ex, '', {})])
|
||||
.then(function() {
|
||||
ch.publish(ex, '', Buffer.from(msg));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: true});})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.equal(msg, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
// send to queue, purge, get-empty
|
||||
chtest("purge queue", function(ch) {
|
||||
var q = 'test.purge-queue';
|
||||
return ch.assertQueue(q, {durable: false})
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from('foobar'));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
ch.purgeQueue(q);
|
||||
return ch.get(q, {noAck: true});})
|
||||
.then(function(m) {
|
||||
assert(!m); // get-empty
|
||||
});
|
||||
});
|
||||
|
||||
// bind again, unbind, publish, get-empty
|
||||
chtest("unbind queue", function(ch) {
|
||||
var ex = 'test.unbind-queue-ex';
|
||||
var q = 'test.unbind-queue';
|
||||
var viabinding = randomString();
|
||||
var direct = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertExchange(ex, 'fanout', EX_OPTS),
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
ch.bindQueue(q, ex, '', {})])
|
||||
.then(function() {
|
||||
ch.publish(ex, '', Buffer.from('foobar'));
|
||||
return waitForMessages(q);})
|
||||
.then(function() { // message got through!
|
||||
return ch.get(q, {noAck:true})
|
||||
.then(function(m) {assert(m);});})
|
||||
.then(function() {
|
||||
return ch.unbindQueue(q, ex, '', {});})
|
||||
.then(function() {
|
||||
// via the no-longer-existing binding
|
||||
ch.publish(ex, '', Buffer.from(viabinding));
|
||||
// direct to the queue
|
||||
ch.sendToQueue(q, Buffer.from(direct));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {return ch.get(q)})
|
||||
.then(function(m) {
|
||||
// the direct to queue message got through, the via-binding
|
||||
// message (sent first) did not
|
||||
assert.equal(direct, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
// To some extent this is now just testing semantics of the server,
|
||||
// but we can at least try out a few settings, and consume.
|
||||
chtest("consume via exchange-exchange binding", function(ch) {
|
||||
var ex1 = 'test.ex-ex-binding1', ex2 = 'test.ex-ex-binding2';
|
||||
var q = 'test.ex-ex-binding-q';
|
||||
var rk = 'test.routing.key', msg = randomString();
|
||||
return Promise.all([
|
||||
ch.assertExchange(ex1, 'direct', EX_OPTS),
|
||||
ch.assertExchange(ex2, 'fanout',
|
||||
{durable: false, internal: true}),
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
ch.bindExchange(ex2, ex1, rk, {}),
|
||||
ch.bindQueue(q, ex2, '', {})])
|
||||
.then(function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
function delivery(m) {
|
||||
if (m.content.toString() === msg) resolve();
|
||||
else reject(new Error("Wrong message"));
|
||||
}
|
||||
ch.consume(q, delivery, {noAck: true})
|
||||
.then(function() {
|
||||
ch.publish(ex1, rk, Buffer.from(msg));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// bind again, unbind, publish, get-empty
|
||||
chtest("unbind exchange", function(ch) {
|
||||
var source = 'test.unbind-ex-source';
|
||||
var dest = 'test.unbind-ex-dest';
|
||||
var q = 'test.unbind-ex-queue';
|
||||
var viabinding = randomString();
|
||||
var direct = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertExchange(source, 'fanout', EX_OPTS),
|
||||
ch.assertExchange(dest, 'fanout', EX_OPTS),
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
ch.bindExchange(dest, source, '', {}),
|
||||
ch.bindQueue(q, dest, '', {})])
|
||||
.then(function() {
|
||||
ch.publish(source, '', Buffer.from('foobar'));
|
||||
return waitForMessages(q);})
|
||||
.then(function() { // message got through!
|
||||
return ch.get(q, {noAck:true})
|
||||
.then(function(m) {assert(m);});})
|
||||
.then(function() {
|
||||
return ch.unbindExchange(dest, source, '', {});})
|
||||
.then(function() {
|
||||
// via the no-longer-existing binding
|
||||
ch.publish(source, '', Buffer.from(viabinding));
|
||||
// direct to the queue
|
||||
ch.sendToQueue(q, Buffer.from(direct));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {return ch.get(q)})
|
||||
.then(function(m) {
|
||||
// the direct to queue message got through, the via-binding
|
||||
// message (sent first) did not
|
||||
assert.equal(direct, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
// This is a bit convoluted. Sorry.
|
||||
chtest("cancel consumer", function(ch) {
|
||||
var q = 'test.consumer-cancel';
|
||||
var ctag;
|
||||
var recv1 = new Promise(function (resolve, reject) {
|
||||
Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q),
|
||||
// My callback is 'resolve the promise in `arrived`'
|
||||
ch.consume(q, resolve, {noAck:true})
|
||||
.then(function(ok) {
|
||||
ctag = ok.consumerTag;
|
||||
ch.sendToQueue(q, Buffer.from('foo'));
|
||||
})]);
|
||||
});
|
||||
|
||||
// A message should arrive because of the consume
|
||||
return recv1.then(function() {
|
||||
var recv2 = Promise.all([
|
||||
ch.cancel(ctag).then(function() {
|
||||
return ch.sendToQueue(q, Buffer.from('bar'));
|
||||
}),
|
||||
// but check a message did arrive in the queue
|
||||
waitForMessages(q)])
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck:true});
|
||||
})
|
||||
.then(function(m) {
|
||||
// I'm going to reject it, because I flip succeed/fail
|
||||
// just below
|
||||
if (m.content.toString() === 'bar') {
|
||||
throw new Error();
|
||||
}
|
||||
});
|
||||
|
||||
return expectFail(recv2);
|
||||
});
|
||||
});
|
||||
|
||||
chtest("cancelled consumer", function(ch) {
|
||||
var q = 'test.cancelled-consumer';
|
||||
return new Promise(function(resolve, reject) {
|
||||
return Promise.all([
|
||||
ch.assertQueue(q),
|
||||
ch.purgeQueue(q),
|
||||
ch.consume(q, function(msg) {
|
||||
if (msg === null) resolve();
|
||||
else reject(new Error('Message not expected'));
|
||||
})])
|
||||
.then(function() {
|
||||
return ch.deleteQueue(q);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ack, by default, removes a single message from the queue
|
||||
chtest("ack", function(ch) {
|
||||
var q = 'test.ack';
|
||||
var msg1 = randomString(), msg2 = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS),
|
||||
ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from(msg1));
|
||||
ch.sendToQueue(q, Buffer.from(msg2));
|
||||
return waitForMessages(q, 2);
|
||||
})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: false})
|
||||
})
|
||||
.then(function(m) {
|
||||
assert.equal(msg1, m.content.toString());
|
||||
ch.ack(m);
|
||||
// %%% is there a race here? may depend on
|
||||
// rabbitmq-sepcific semantics
|
||||
return ch.get(q);
|
||||
})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.equal(msg2, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
// Nack, by default, puts a message back on the queue (where in the
|
||||
// queue is up to the server)
|
||||
chtest("nack", function(ch) {
|
||||
var q = 'test.nack';
|
||||
var msg1 = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from(msg1));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: false})})
|
||||
.then(function(m) {
|
||||
assert.equal(msg1, m.content.toString());
|
||||
ch.nack(m);
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q);})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.equal(msg1, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
// reject is a near-synonym for nack, the latter of which is not
|
||||
// available in earlier RabbitMQ (or in AMQP proper).
|
||||
chtest("reject", function(ch) {
|
||||
var q = 'test.reject';
|
||||
var msg1 = randomString();
|
||||
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from(msg1));
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q, {noAck: false})})
|
||||
.then(function(m) {
|
||||
assert.equal(msg1, m.content.toString());
|
||||
ch.reject(m);
|
||||
return waitForMessages(q);})
|
||||
.then(function() {
|
||||
return ch.get(q);})
|
||||
.then(function(m) {
|
||||
assert(m);
|
||||
assert.equal(msg1, m.content.toString());
|
||||
});
|
||||
});
|
||||
|
||||
chtest("prefetch", function(ch) {
|
||||
var q = 'test.prefetch';
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q),
|
||||
ch.prefetch(1)])
|
||||
.then(function() {
|
||||
ch.sendToQueue(q, Buffer.from('foobar'));
|
||||
ch.sendToQueue(q, Buffer.from('foobar'));
|
||||
return waitForMessages(q, 2);
|
||||
})
|
||||
.then(function() {
|
||||
return new Promise(function(resolve) {
|
||||
var messageCount = 0;
|
||||
function receive(msg) {
|
||||
ch.ack(msg);
|
||||
if (++messageCount > 1) {
|
||||
resolve(messageCount);
|
||||
}
|
||||
}
|
||||
return ch.consume(q, receive, {noAck: false})
|
||||
});
|
||||
})
|
||||
.then(function(c) {
|
||||
return assert.equal(2, c);
|
||||
});
|
||||
});
|
||||
|
||||
chtest('close', function(ch) {
|
||||
// Resolving promise guarantees
|
||||
// channel is closed
|
||||
return ch.close();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var confirmtest = channel_test.bind(null, 'createConfirmChannel');
|
||||
|
||||
suite("confirms", function() {
|
||||
|
||||
confirmtest('message is confirmed', function(ch) {
|
||||
var q = 'test.confirm-message';
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
return ch.sendToQueue(q, Buffer.from('bleep'));
|
||||
});
|
||||
});
|
||||
|
||||
// Usually one can provoke the server into confirming more than one
|
||||
// message in an ack by simply sending a few messages in quick
|
||||
// succession; a bit unscientific I know. Luckily we can eavesdrop on
|
||||
// the acknowledgements coming through to see if we really did get a
|
||||
// multi-ack.
|
||||
confirmtest('multiple confirms', function(ch) {
|
||||
var q = 'test.multiple-confirms';
|
||||
return Promise.all([
|
||||
ch.assertQueue(q, QUEUE_OPTS), ch.purgeQueue(q)])
|
||||
.then(function() {
|
||||
var multipleRainbows = false;
|
||||
ch.on('ack', function(a) {
|
||||
if (a.multiple) multipleRainbows = true;
|
||||
});
|
||||
|
||||
function prod(num) {
|
||||
var cs = [];
|
||||
|
||||
function sendAndPushPromise() {
|
||||
var conf = promisify(function(cb) {
|
||||
return ch.sendToQueue(q, Buffer.from('bleep'), {}, cb);
|
||||
})();
|
||||
cs.push(conf);
|
||||
}
|
||||
|
||||
for (var i=0; i < num; i++) sendAndPushPromise();
|
||||
|
||||
return Promise.all(cs).then(function() {
|
||||
if (multipleRainbows) return true;
|
||||
else if (num > 500) throw new Error(
|
||||
"Couldn't provoke the server" +
|
||||
" into multi-acking with " + num +
|
||||
" messages; giving up");
|
||||
else {
|
||||
//console.warn("Failed with " + num + "; trying " + num * 2);
|
||||
return prod(num * 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
return prod(5);
|
||||
});
|
||||
});
|
||||
|
||||
confirmtest('wait for confirms', function(ch) {
|
||||
for (var i=0; i < 1000; i++) {
|
||||
ch.publish('', '', Buffer.from('foobar'), {});
|
||||
}
|
||||
return ch.waitForConfirms();
|
||||
})
|
||||
|
||||
confirmtest('works when channel is closed', function(ch) {
|
||||
for (var i=0; i < 1000; i++) {
|
||||
ch.publish('', '', Buffer.from('foobar'), {});
|
||||
}
|
||||
return ch.close().then(function () {
|
||||
return ch.waitForConfirms()
|
||||
}).then(function () {
|
||||
assert.strictEqual(true, false, 'Wait should have failed.')
|
||||
}, function (e) {
|
||||
assert.strictEqual(e.message, 'channel closed')
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
237
node_modules/amqplib/test/codec.js
generated
vendored
Normal file
237
node_modules/amqplib/test/codec.js
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
'use strict';
|
||||
|
||||
var codec = require('../lib/codec');
|
||||
var defs = require('../lib/defs');
|
||||
var assert = require('assert');
|
||||
var ints = require('buffer-more-ints');
|
||||
var C = require('claire');
|
||||
var forAll = C.forAll;
|
||||
|
||||
// These just test known encodings; to generate the answers I used
|
||||
// RabbitMQ's binary generator module.
|
||||
|
||||
var testCases = [
|
||||
// integers
|
||||
['byte', {byte: 112}, [4,98,121,116,101,98,112]],
|
||||
['byte max value', {byte: 127}, [4,98,121,116,101,98,127]],
|
||||
['byte min value', {byte: -128}, [4,98,121,116,101,98,128]],
|
||||
['< -128 promoted to signed short', {short: -129}, [5,115,104,111,114,116,115,255,127]],
|
||||
['> 127 promoted to short', {short: 128}, [5,115,104,111,114,116,115,0,128]],
|
||||
['< 2^15 still a short', {short: 0x7fff}, [5,115,104,111,114,116,115,127,255]],
|
||||
['-2^15 still a short', {short: -0x8000}, [5,115,104,111,114,116,115,128,0]],
|
||||
['>= 2^15 promoted to int', {int: 0x8000}, [3,105,110,116,73,0,0,128,0]],
|
||||
['< -2^15 promoted to int', {int: -0x8001}, [3,105,110,116,73,255,255,127,255]],
|
||||
['< 2^31 still an int', {int: 0x7fffffff}, [3,105,110,116,73,127,255,255,255]],
|
||||
['>= -2^31 still an int', {int: -0x80000000}, [3,105,110,116,73,128,0,0,0]],
|
||||
['>= 2^31 promoted to long', {long: 0x80000000}, [4,108,111,110,103,108,0,0,0,0,128,0,0,0]],
|
||||
['< -2^31 promoted to long', {long: -0x80000001}, [4,108,111,110,103,108,255,255,255,255,127,255,255,255]],
|
||||
|
||||
// floating point
|
||||
['float value', {double: 0.5}, [6,100,111,117,98,108,101,100,63,224,0,0,0,0,0,0]],
|
||||
['negative float value', {double: -0.5}, [6,100,111,117,98,108,101,100,191,224,0,0,0,0,0,0]],
|
||||
// %% test some boundaries of precision?
|
||||
|
||||
// string
|
||||
['string', {string: "boop"}, [6,115,116,114,105,110,103,83,0,0,0,4,98,111,111,112]],
|
||||
|
||||
// buffer -> byte array
|
||||
['byte array from buffer', {bytes: Buffer.from([1,2,3,4])},
|
||||
[5,98,121,116,101,115,120,0,0,0,4,1,2,3,4]],
|
||||
|
||||
// boolean, void
|
||||
['true', {bool: true}, [4,98,111,111,108,116,1]],
|
||||
['false', {bool: false}, [4,98,111,111,108,116,0]],
|
||||
['null', {'void': null}, [4,118,111,105,100,86]],
|
||||
|
||||
// array, object
|
||||
['array', {array: [6, true, "foo"]},[5,97,114,114,97,121,65,0,0,0,12,98,6,116,1,83,0,0,0,3,102,111,111]],
|
||||
['object', {object: {foo: "bar", baz: 12}},[6,111,98,106,101,99,116,70,0,0,0,18,3,102,111,111,83,0,0,0,3,98,97,114,3,98,97,122,98,12]],
|
||||
|
||||
// exotic types
|
||||
['timestamp', {timestamp: {'!': 'timestamp', value: 1357212277527}},[9,116,105,109,101,115,116,97,109,112,84,0,0,1,60,0,39,219,23]],
|
||||
['decimal', {decimal: {'!': 'decimal', value: {digits: 2345, places: 2}}},[7,100,101,99,105,109,97,108,68,2,0,0,9,41]],
|
||||
['float', {float: {'!': 'float', value: 0.1}},[5,102,108,111,97,116,102,61,204,204,205]],
|
||||
['unsignedbyte', {unsignedbyte:{'!': 'unsignedbyte', value: 255}}, [12,117,110,115,105,103,110,101,100,98,121,116,101,66,255]],
|
||||
['unsignedshort', {unsignedshort:{'!': 'unsignedshort', value: 65535}}, [13,117,110,115,105,103,110,101,100,115,104,111,114,116,117,255,255]],
|
||||
['unsignedint', {unsignedint:{'!': 'unsignedint', value: 4294967295}}, [11,117,110,115,105,103,110,101,100,105,110,116,105,255,255,255,255]],
|
||||
];
|
||||
|
||||
function bufferToArray(b) {
|
||||
return Array.prototype.slice.call(b);
|
||||
}
|
||||
|
||||
suite("Implicit encodings", function() {
|
||||
|
||||
testCases.forEach(function(tc) {
|
||||
var name = tc[0], val = tc[1], expect = tc[2];
|
||||
test(name, function() {
|
||||
var buffer = Buffer.alloc(1000);
|
||||
var size = codec.encodeTable(buffer, val, 0);
|
||||
var result = buffer.subarray(4, size);
|
||||
assert.deepEqual(expect, bufferToArray(result));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Whole frames
|
||||
|
||||
var amqp = require('./data');
|
||||
|
||||
function roundtrip_table(t) {
|
||||
var buf = Buffer.alloc(4096);
|
||||
var size = codec.encodeTable(buf, t, 0);
|
||||
var decoded = codec.decodeFields(buf.subarray(4, size)); // ignore the length-prefix
|
||||
try {
|
||||
assert.deepEqual(removeExplicitTypes(t), decoded);
|
||||
}
|
||||
catch (e) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
function roundtrips(T) {
|
||||
return forAll(T).satisfy(function(v) { return roundtrip_table({value: v}); });
|
||||
}
|
||||
|
||||
suite("Roundtrip values", function() {
|
||||
[
|
||||
amqp.Octet,
|
||||
amqp.ShortStr,
|
||||
amqp.LongStr,
|
||||
amqp.UShort,
|
||||
amqp.ULong,
|
||||
amqp.ULongLong,
|
||||
amqp.UShort,
|
||||
amqp.Short,
|
||||
amqp.Long,
|
||||
amqp.Bit,
|
||||
amqp.Decimal,
|
||||
amqp.Timestamp,
|
||||
amqp.UnsignedByte,
|
||||
amqp.UnsignedShort,
|
||||
amqp.UnsignedInt,
|
||||
amqp.Double,
|
||||
amqp.Float,
|
||||
amqp.FieldArray,
|
||||
amqp.FieldTable
|
||||
].forEach(function(T) {
|
||||
test(T.toString() + ' roundtrip', roundtrips(T).asTest());
|
||||
});
|
||||
});
|
||||
|
||||
// When encoding, you can supply explicitly-typed fields like `{'!':
|
||||
// int32, 50}`. Most of these do not appear in the decoded values, so
|
||||
// to compare like-to-like we have to remove them from the input.
|
||||
function removeExplicitTypes(input) {
|
||||
switch (typeof input) {
|
||||
case 'object':
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
if (Array.isArray(input)) {
|
||||
var newArr = [];
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
newArr[i] = removeExplicitTypes(input[i]);
|
||||
}
|
||||
return newArr;
|
||||
}
|
||||
if (Buffer.isBuffer(input)) {
|
||||
return input;
|
||||
}
|
||||
switch (input['!']) {
|
||||
case 'timestamp':
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
return input;
|
||||
case undefined:
|
||||
var newObj = {}
|
||||
for (var k in input) {
|
||||
newObj[k] = removeExplicitTypes(input[k]);
|
||||
}
|
||||
return newObj;
|
||||
default:
|
||||
return input.value;
|
||||
}
|
||||
|
||||
default:
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
// Asserts that the decoded fields are equal to the original fields,
|
||||
// or equal to a default where absent in the original. The defaults
|
||||
// depend on the type of method or properties.
|
||||
//
|
||||
// This works slightly different for methods and properties: for
|
||||
// methods, each field must have a value, so the default is
|
||||
// substituted for undefined values when encoding; for properties,
|
||||
// fields may be absent in the encoded value, so a default is
|
||||
// substituted for missing fields when decoding. The effect is the
|
||||
// same so far as these tests are concerned.
|
||||
function assertEqualModuloDefaults(original, decodedFields) {
|
||||
var args = defs.info(original.id).args;
|
||||
for (var i=0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
var originalValue = original.fields[arg.name];
|
||||
var decodedValue = decodedFields[arg.name];
|
||||
try {
|
||||
if (originalValue === undefined) {
|
||||
// longstr gets special treatment here, since the defaults are
|
||||
// given as strings rather than buffers, but the decoded values
|
||||
// will be buffers.
|
||||
assert.deepEqual((arg.type === 'longstr') ?
|
||||
Buffer.from(arg.default) : arg.default,
|
||||
decodedValue);
|
||||
}
|
||||
else {
|
||||
assert.deepEqual(removeExplicitTypes(originalValue), decodedValue);
|
||||
}
|
||||
}
|
||||
catch (assertionErr) {
|
||||
var methodOrProps = defs.info(original.id).name;
|
||||
assertionErr.message += ' (frame ' + methodOrProps +
|
||||
' field ' + arg.name + ')';
|
||||
throw assertionErr;
|
||||
}
|
||||
}
|
||||
// %%% TODO make sure there's no surplus fields
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is handy for elsewhere
|
||||
module.exports.assertEqualModuloDefaults = assertEqualModuloDefaults;
|
||||
|
||||
function roundtripMethod(Method) {
|
||||
return forAll(Method).satisfy(function(method) {
|
||||
var buf = defs.encodeMethod(method.id, 0, method.fields);
|
||||
// FIXME depends on framing, ugh
|
||||
var fs1 = defs.decode(method.id, buf.subarray(11, buf.length));
|
||||
assertEqualModuloDefaults(method, fs1);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function roundtripProperties(Properties) {
|
||||
return forAll(Properties).satisfy(function(properties) {
|
||||
var buf = defs.encodeProperties(properties.id, 0, properties.size,
|
||||
properties.fields);
|
||||
// FIXME depends on framing, ugh
|
||||
var fs1 = defs.decode(properties.id, buf.subarray(19, buf.length));
|
||||
assert.equal(properties.size, ints.readUInt64BE(buf, 11));
|
||||
assertEqualModuloDefaults(properties, fs1);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
suite("Roundtrip methods", function() {
|
||||
amqp.methods.forEach(function(Method) {
|
||||
test(Method.toString() + ' roundtrip',
|
||||
roundtripMethod(Method).asTest());
|
||||
});
|
||||
});
|
||||
|
||||
suite("Roundtrip properties", function() {
|
||||
amqp.properties.forEach(function(Properties) {
|
||||
test(Properties.toString() + ' roundtrip',
|
||||
roundtripProperties(Properties).asTest());
|
||||
});
|
||||
});
|
||||
197
node_modules/amqplib/test/connect.js
generated
vendored
Normal file
197
node_modules/amqplib/test/connect.js
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
'use strict';
|
||||
|
||||
var connect = require('../lib/connect').connect;
|
||||
var credentialsFromUrl = require('../lib/connect').credentialsFromUrl;
|
||||
var defs = require('../lib/defs');
|
||||
var assert = require('assert');
|
||||
var util = require('./util');
|
||||
var net = require('net');
|
||||
var fail = util.fail, succeed = util.succeed, latch = util.latch,
|
||||
kCallback = util.kCallback,
|
||||
succeedIfAttributeEquals = util.succeedIfAttributeEquals;
|
||||
var format = require('util').format;
|
||||
|
||||
var URL = process.env.URL || 'amqp://localhost';
|
||||
|
||||
var urlparse = require('url-parse');
|
||||
|
||||
suite("Credentials", function() {
|
||||
|
||||
function checkCreds(creds, user, pass, done) {
|
||||
if (creds.mechanism != 'PLAIN') {
|
||||
return done('expected mechanism PLAIN');
|
||||
}
|
||||
if (creds.username != user || creds.password != pass) {
|
||||
return done(format("expected '%s', '%s'; got '%s', '%s'",
|
||||
user, pass, creds.username, creds.password));
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
||||
test("no creds", function(done) {
|
||||
var parts = urlparse('amqp://localhost');
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, 'guest', 'guest', done);
|
||||
});
|
||||
test("usual user:pass", function(done) {
|
||||
var parts = urlparse('amqp://user:pass@localhost')
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, 'user', 'pass', done);
|
||||
});
|
||||
test("missing user", function(done) {
|
||||
var parts = urlparse('amqps://:password@localhost');
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, '', 'password', done);
|
||||
});
|
||||
test("missing password", function(done) {
|
||||
var parts = urlparse('amqps://username:@localhost');
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, 'username', '', done);
|
||||
});
|
||||
test("escaped colons", function(done) {
|
||||
var parts = urlparse('amqp://user%3Aname:pass%3Aword@localhost')
|
||||
var creds = credentialsFromUrl(parts);
|
||||
checkCreds(creds, 'user:name', 'pass:word', done);
|
||||
});
|
||||
});
|
||||
|
||||
suite("Connect API", function() {
|
||||
|
||||
test("Connection refused", function(done) {
|
||||
connect('amqp://localhost:23450', {},
|
||||
kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
// %% this ought to fail the promise, rather than throwing an error
|
||||
test("bad URL", function() {
|
||||
assert.throws(function() {
|
||||
connect('blurble');
|
||||
});
|
||||
});
|
||||
|
||||
test("wrongly typed open option", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var q = parts.query || {};
|
||||
q.frameMax = 'NOT A NUMBER';
|
||||
parts.query = q;
|
||||
var u = url.format(parts);
|
||||
connect(u, {}, kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
test("serverProperties", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var config = parts.query || {};
|
||||
connect(config, {}, function(err, connection) {
|
||||
if (err) { return done(err); }
|
||||
assert.equal(connection.serverProperties.product, 'RabbitMQ');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test("using custom heartbeat option", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var config = parts.query || {};
|
||||
config.heartbeat = 20;
|
||||
connect(config, {}, kCallback(succeedIfAttributeEquals('heartbeat', 20, done), fail(done)));
|
||||
});
|
||||
|
||||
test("wrongly typed heartbeat option", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var config = parts.query || {};
|
||||
config.heartbeat = 'NOT A NUMBER';
|
||||
connect(config, {}, kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
test("using plain credentials", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var u = 'guest', p = 'guest';
|
||||
if (parts.auth) {
|
||||
var auth = parts.auth.split(":");
|
||||
u = auth[0], p = auth[1];
|
||||
}
|
||||
connect(URL, {credentials: require('../lib/credentials').plain(u, p)},
|
||||
kCallback(succeed(done), fail(done)));
|
||||
});
|
||||
|
||||
test("using amqplain credentials", function(done) {
|
||||
var url = require('url');
|
||||
var parts = url.parse(URL, true);
|
||||
var u = 'guest', p = 'guest';
|
||||
if (parts.auth) {
|
||||
var auth = parts.auth.split(":");
|
||||
u = auth[0], p = auth[1];
|
||||
}
|
||||
connect(URL, {credentials: require('../lib/credentials').amqplain(u, p)},
|
||||
kCallback(succeed(done), fail(done)));
|
||||
});
|
||||
|
||||
test("using unsupported mechanism", function(done) {
|
||||
var creds = {
|
||||
mechanism: 'UNSUPPORTED',
|
||||
response: function() { return Buffer.from(''); }
|
||||
};
|
||||
connect(URL, {credentials: creds},
|
||||
kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
test("with a given connection timeout", function(done) {
|
||||
var timeoutServer = net.createServer(function() {}).listen(31991);
|
||||
|
||||
connect('amqp://localhost:31991', {timeout: 50}, function(err, val) {
|
||||
timeoutServer.close();
|
||||
if (val) done(new Error('Expected connection timeout, did not'));
|
||||
else done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Errors on connect', function() {
|
||||
var server
|
||||
teardown(function() {
|
||||
if (server) {
|
||||
server.close();
|
||||
}
|
||||
})
|
||||
|
||||
test("closes underlying connection on authentication error", function(done) {
|
||||
var bothDone = latch(2, done);
|
||||
server = net.createServer(function(socket) {
|
||||
socket.once('data', function(protocolHeader) {
|
||||
assert.deepStrictEqual(
|
||||
protocolHeader,
|
||||
Buffer.from("AMQP" + String.fromCharCode(0,0,9,1))
|
||||
);
|
||||
util.runServer(socket, function(send, wait) {
|
||||
send(defs.ConnectionStart,
|
||||
{versionMajor: 0,
|
||||
versionMinor: 9,
|
||||
serverProperties: {},
|
||||
mechanisms: Buffer.from('PLAIN'),
|
||||
locales: Buffer.from('en_US')});
|
||||
wait(defs.ConnectionStartOk)().then(function() {
|
||||
send(defs.ConnectionClose,
|
||||
{replyCode: 403,
|
||||
replyText: 'ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN',
|
||||
classId: 0,
|
||||
methodId: 0});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for the connection to be closed after the authentication error
|
||||
socket.once('end', function() {
|
||||
bothDone();
|
||||
});
|
||||
}).listen(0);
|
||||
|
||||
connect('amqp://localhost:' + server.address().port, {}, function(err) {
|
||||
if (!err) bothDone(new Error('Expected authentication error'));
|
||||
bothDone();
|
||||
});
|
||||
});
|
||||
});
|
||||
390
node_modules/amqplib/test/connection.js
generated
vendored
Normal file
390
node_modules/amqplib/test/connection.js
generated
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var defs = require('../lib/defs');
|
||||
var Connection = require('../lib/connection').Connection;
|
||||
var HEARTBEAT = require('../lib/frame').HEARTBEAT;
|
||||
var HB_BUF = require('../lib/frame').HEARTBEAT_BUF;
|
||||
var util = require('./util');
|
||||
var succeed = util.succeed, fail = util.fail, latch = util.latch;
|
||||
var completes = util.completes;
|
||||
var kCallback = util.kCallback;
|
||||
|
||||
var LOG_ERRORS = process.env.LOG_ERRORS;
|
||||
|
||||
var OPEN_OPTS = {
|
||||
// start-ok
|
||||
'clientProperties': {},
|
||||
'mechanism': 'PLAIN',
|
||||
'response': Buffer.from(['', 'guest', 'guest'].join(String.fromCharCode(0))),
|
||||
'locale': 'en_US',
|
||||
|
||||
// tune-ok
|
||||
'channelMax': 0,
|
||||
'frameMax': 0,
|
||||
'heartbeat': 0,
|
||||
|
||||
// open
|
||||
'virtualHost': '/',
|
||||
'capabilities': '',
|
||||
'insist': 0
|
||||
};
|
||||
module.exports.OPEN_OPTS = OPEN_OPTS;
|
||||
|
||||
function happy_open(send, wait) {
|
||||
// kick it off
|
||||
send(defs.ConnectionStart,
|
||||
{versionMajor: 0,
|
||||
versionMinor: 9,
|
||||
serverProperties: {},
|
||||
mechanisms: Buffer.from('PLAIN'),
|
||||
locales: Buffer.from('en_US')});
|
||||
return wait(defs.ConnectionStartOk)()
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionTune,
|
||||
{channelMax: 0,
|
||||
heartbeat: 0,
|
||||
frameMax: 0});
|
||||
})
|
||||
.then(wait(defs.ConnectionTuneOk))
|
||||
.then(wait(defs.ConnectionOpen))
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionOpenOk,
|
||||
{knownHosts: ''});
|
||||
});
|
||||
}
|
||||
module.exports.connection_handshake = happy_open;
|
||||
|
||||
function connectionTest(client, server) {
|
||||
return function(done) {
|
||||
var bothDone = latch(2, done);
|
||||
var pair = util.socketPair();
|
||||
var c = new Connection(pair.client);
|
||||
if (LOG_ERRORS) c.on('error', console.warn);
|
||||
client(c, bothDone);
|
||||
|
||||
// NB only not a race here because the writes are synchronous
|
||||
var protocolHeader = pair.server.read(8);
|
||||
assert.deepEqual(Buffer.from("AMQP" + String.fromCharCode(0,0,9,1)),
|
||||
protocolHeader);
|
||||
|
||||
var s = util.runServer(pair.server, function(send, wait) {
|
||||
server(send, wait, bothDone, pair.server);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
suite("Connection errors", function() {
|
||||
|
||||
test("socket close during open", function(done) {
|
||||
// RabbitMQ itself will take at least 3 seconds to close the socket
|
||||
// in the event of a handshake problem. Instead of using a live
|
||||
// connection, I'm just going to pretend.
|
||||
var pair = util.socketPair();
|
||||
var conn = new Connection(pair.client);
|
||||
pair.server.on('readable', function() {
|
||||
pair.server.end();
|
||||
});
|
||||
conn.open({}, kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
test("bad frame during open", function(done) {
|
||||
var ss = util.socketPair();
|
||||
var conn = new (require('../lib/connection').Connection)(ss.client);
|
||||
ss.server.on('readable', function() {
|
||||
ss.server.write(Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));
|
||||
});
|
||||
conn.open({}, kCallback(fail(done), succeed(done)));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite("Connection open", function() {
|
||||
|
||||
test("happy", connectionTest(
|
||||
function(c, done) {
|
||||
c.open(OPEN_OPTS, kCallback(succeed(done), fail(done)));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("wrong first frame", connectionTest(
|
||||
function(c, done) {
|
||||
c.open(OPEN_OPTS, kCallback(fail(done), succeed(done)));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
// bad server! bad! whatever were you thinking?
|
||||
completes(function() {
|
||||
send(defs.ConnectionTune,
|
||||
{channelMax: 0,
|
||||
heartbeat: 0,
|
||||
frameMax: 0});
|
||||
}, done);
|
||||
}));
|
||||
|
||||
test("unexpected socket close", connectionTest(
|
||||
function(c, done) {
|
||||
c.open(OPEN_OPTS, kCallback(fail(done), succeed(done)));
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
send(defs.ConnectionStart,
|
||||
{versionMajor: 0,
|
||||
versionMinor: 9,
|
||||
serverProperties: {},
|
||||
mechanisms: Buffer.from('PLAIN'),
|
||||
locales: Buffer.from('en_US')});
|
||||
return wait(defs.ConnectionStartOk)()
|
||||
.then(function() {
|
||||
socket.end();
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
suite("Connection running", function() {
|
||||
|
||||
test("wrong frame on channel 0", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('error', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
// there's actually nothing that would plausibly be sent to a
|
||||
// just opened connection, so this is violating more than one
|
||||
// rule. Nonetheless.
|
||||
send(defs.ChannelOpenOk, {channelId: Buffer.from('')}, 0);
|
||||
})
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function(close) {
|
||||
send(defs.ConnectionCloseOk, {}, 0);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("unopened channel", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('error', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
// there's actually nothing that would plausibly be sent to a
|
||||
// just opened connection, so this is violating more than one
|
||||
// rule. Nonetheless.
|
||||
send(defs.ChannelOpenOk, {channelId: Buffer.from('')}, 3);
|
||||
})
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function(close) {
|
||||
send(defs.ConnectionCloseOk, {}, 0);
|
||||
}).then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("unexpected socket close", connectionTest(
|
||||
function(c, done) {
|
||||
var errorAndClosed = latch(2, done);
|
||||
c.on('error', succeed(errorAndClosed));
|
||||
c.on('close', succeed(errorAndClosed));
|
||||
c.open(OPEN_OPTS, kCallback(function() {
|
||||
c.sendHeartbeat();
|
||||
}, fail(errorAndClosed)));
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
happy_open(send, wait)
|
||||
.then(wait())
|
||||
.then(function() {
|
||||
socket.end();
|
||||
}).then(succeed(done));
|
||||
}));
|
||||
|
||||
test("connection.blocked", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('blocked', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
send(defs.ConnectionBlocked, {reason: 'felt like it'}, 0);
|
||||
})
|
||||
.then(succeed(done));
|
||||
}));
|
||||
|
||||
test("connection.unblocked", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('unblocked', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
send(defs.ConnectionUnblocked, {}, 0);
|
||||
})
|
||||
.then(succeed(done));
|
||||
}));
|
||||
|
||||
|
||||
});
|
||||
|
||||
suite("Connection close", function() {
|
||||
|
||||
test("happy", connectionTest(
|
||||
function(c, done0) {
|
||||
var done = latch(2, done0);
|
||||
c.on('close', done);
|
||||
c.open(OPEN_OPTS, kCallback(function(_ok) {
|
||||
c.close(kCallback(succeed(done), fail(done)));
|
||||
}, function() {}));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function(close) {
|
||||
send(defs.ConnectionCloseOk, {});
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("interleaved close frames", connectionTest(
|
||||
function(c, done0) {
|
||||
var done = latch(2, done0);
|
||||
c.on('close', done);
|
||||
c.open(OPEN_OPTS, kCallback(function(_ok) {
|
||||
c.close(kCallback(succeed(done), fail(done)));
|
||||
}, done));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionClose, {
|
||||
replyText: "Ha!",
|
||||
replyCode: defs.constants.REPLY_SUCCESS,
|
||||
methodId: 0, classId: 0
|
||||
});
|
||||
})
|
||||
.then(wait(defs.ConnectionCloseOk))
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionCloseOk, {});
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("server error close", connectionTest(
|
||||
function(c, done0) {
|
||||
var done = latch(2, done0);
|
||||
c.on('close', succeed(done));
|
||||
c.on('error', succeed(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionClose, {
|
||||
replyText: "Begone",
|
||||
replyCode: defs.constants.INTERNAL_ERROR,
|
||||
methodId: 0, classId: 0
|
||||
});
|
||||
})
|
||||
.then(wait(defs.ConnectionCloseOk))
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
test("operator-intiated close", connectionTest(
|
||||
function(c, done) {
|
||||
c.on('close', succeed(done));
|
||||
c.on('error', fail(done));
|
||||
c.open(OPEN_OPTS);
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(function(f) {
|
||||
send(defs.ConnectionClose, {
|
||||
replyText: "Begone",
|
||||
replyCode: defs.constants.CONNECTION_FORCED,
|
||||
methodId: 0, classId: 0
|
||||
});
|
||||
})
|
||||
.then(wait(defs.ConnectionCloseOk))
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
|
||||
test("double close", connectionTest(
|
||||
function(c, done) {
|
||||
c.open(OPEN_OPTS, kCallback(function() {
|
||||
c.close();
|
||||
// NB no synchronisation, we do this straight away
|
||||
assert.throws(function() {
|
||||
c.close();
|
||||
});
|
||||
done();
|
||||
}, done));
|
||||
},
|
||||
function(send, wait, done) {
|
||||
happy_open(send, wait)
|
||||
.then(wait(defs.ConnectionClose))
|
||||
.then(function() {
|
||||
send(defs.ConnectionCloseOk, {});
|
||||
})
|
||||
.then(succeed(done), fail(done));
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
suite("heartbeats", function() {
|
||||
|
||||
var heartbeat = require('../lib/heartbeat');
|
||||
|
||||
setup(function() {
|
||||
heartbeat.UNITS_TO_MS = 20;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
heartbeat.UNITS_TO_MS = 1000;
|
||||
});
|
||||
|
||||
test("send heartbeat after open", connectionTest(
|
||||
function(c, done) {
|
||||
completes(function() {
|
||||
var opts = Object.create(OPEN_OPTS);
|
||||
opts.heartbeat = 1;
|
||||
// Don't leave the error waiting to happen for the next test, this
|
||||
// confuses mocha awfully
|
||||
c.on('error', function() {});
|
||||
c.open(opts);
|
||||
}, done);
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
var timer;
|
||||
happy_open(send, wait)
|
||||
.then(function() {
|
||||
timer = setInterval(function() {
|
||||
socket.write(HB_BUF);
|
||||
}, heartbeat.UNITS_TO_MS);
|
||||
})
|
||||
.then(wait())
|
||||
.then(function(hb) {
|
||||
if (hb === HEARTBEAT) done();
|
||||
else done("Next frame after silence not a heartbeat");
|
||||
clearInterval(timer);
|
||||
});
|
||||
}));
|
||||
|
||||
test("detect lack of heartbeats", connectionTest(
|
||||
function(c, done) {
|
||||
var opts = Object.create(OPEN_OPTS);
|
||||
opts.heartbeat = 1;
|
||||
c.on('error', succeed(done));
|
||||
c.open(opts);
|
||||
},
|
||||
function(send, wait, done, socket) {
|
||||
happy_open(send, wait)
|
||||
.then(succeed(done), fail(done));
|
||||
// conspicuously not sending anything ...
|
||||
}));
|
||||
|
||||
});
|
||||
269
node_modules/amqplib/test/data.js
generated
vendored
Normal file
269
node_modules/amqplib/test/data.js
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
// Property-based testing representations of various things in AMQP
|
||||
|
||||
'use strict';
|
||||
|
||||
var C = require('claire');
|
||||
var forAll = C.forAll;
|
||||
var arb = C.data;
|
||||
var transform = C.transform;
|
||||
var repeat = C.repeat;
|
||||
var label = C.label;
|
||||
var sequence = C.sequence;
|
||||
var asGenerator = C.asGenerator;
|
||||
var sized = C.sized;
|
||||
var recursive = C.recursive;
|
||||
var choice = C.choice;
|
||||
var Undefined = C.Undefined;
|
||||
|
||||
// Stub these out so we can use outside tests
|
||||
// if (!suite) var suite = function() {}
|
||||
// if (!test) var test = function() {}
|
||||
|
||||
// These aren't exported in claire/index. so I could have to reproduce
|
||||
// them I guess.
|
||||
function choose(a, b) {
|
||||
return Math.random() * (b - a) + a;
|
||||
}
|
||||
|
||||
function chooseInt(a, b) {
|
||||
return Math.floor(choose(a, b));
|
||||
}
|
||||
|
||||
function rangeInt(name, a, b) {
|
||||
return label(name,
|
||||
asGenerator(function(_) { return chooseInt(a, b); }));
|
||||
}
|
||||
|
||||
function toFloat32(i) {
|
||||
var b = Buffer.alloc(4);
|
||||
b.writeFloatBE(i, 0);
|
||||
return b.readFloatBE(0);
|
||||
}
|
||||
|
||||
function floatChooser(maxExp) {
|
||||
return function() {
|
||||
var n = Number.NaN;
|
||||
while (isNaN(n)) {
|
||||
var mantissa = Math.random() * 2 - 1;
|
||||
var exponent = chooseInt(0, maxExp);
|
||||
n = Math.pow(mantissa, exponent);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
function explicitType(t, underlying) {
|
||||
return label(t, transform(function(n) {
|
||||
return {'!': t, value: n};
|
||||
}, underlying));
|
||||
}
|
||||
|
||||
// FIXME null, byte array, others?
|
||||
|
||||
var Octet = rangeInt('octet', 0, 255);
|
||||
var ShortStr = label('shortstr',
|
||||
transform(function(s) {
|
||||
return s.substr(0, 255);
|
||||
}, arb.Str));
|
||||
|
||||
var LongStr = label('longstr',
|
||||
transform(
|
||||
function(bytes) { return Buffer.from(bytes); },
|
||||
repeat(Octet)));
|
||||
|
||||
var UShort = rangeInt('short-uint', 0, 0xffff);
|
||||
var ULong = rangeInt('long-uint', 0, 0xffffffff);
|
||||
var ULongLong = rangeInt('longlong-uint', 0, 0xffffffffffffffff);
|
||||
var Short = rangeInt('short-int', -0x8000, 0x7fff);
|
||||
var Long = rangeInt('long-int', -0x80000000, 0x7fffffff);
|
||||
var LongLong = rangeInt('longlong-int', -0x8000000000000000,
|
||||
0x7fffffffffffffff);
|
||||
var Bit = label('bit', arb.Bool);
|
||||
var Double = label('double', asGenerator(floatChooser(308)));
|
||||
var Float = label('float', transform(toFloat32, floatChooser(38)));
|
||||
var Timestamp = label('timestamp', transform(
|
||||
function(n) {
|
||||
return {'!': 'timestamp', value: n};
|
||||
}, ULongLong));
|
||||
var Decimal = label('decimal', transform(
|
||||
function(args) {
|
||||
return {'!': 'decimal', value: {places: args[1], digits: args[0]}};
|
||||
}, sequence(arb.UInt, Octet)));
|
||||
var UnsignedByte = label('unsignedbyte', transform(
|
||||
function(n) {
|
||||
return {'!': 'unsignedbyte', value: n};
|
||||
}, Octet));
|
||||
var UnsignedShort = label('unsignedshort', transform(
|
||||
function(n) {
|
||||
return {'!': 'unsignedshort', value: n};
|
||||
}, UShort));
|
||||
var UnsignedInt = label('unsignedint', transform(
|
||||
function(n) {
|
||||
return {'!': 'unsignedint', value: n};
|
||||
}, ULong));
|
||||
|
||||
// Signed 8 bit int
|
||||
var Byte = rangeInt('byte', -128, 127);
|
||||
|
||||
// Explicitly typed values
|
||||
var ExByte = explicitType('byte', Byte);
|
||||
var ExInt8 = explicitType('int8', Byte);
|
||||
var ExShort = explicitType('short', Short);
|
||||
var ExInt16 = explicitType('int16', Short);
|
||||
var ExInt = explicitType('int', Long);
|
||||
var ExInt32 = explicitType('int32', Long);
|
||||
var ExLong = explicitType('long', LongLong);
|
||||
var ExInt64 = explicitType('int64', LongLong);
|
||||
|
||||
var FieldArray = label('field-array', recursive(function() {
|
||||
return arb.Array(
|
||||
arb.Null,
|
||||
LongStr, ShortStr,
|
||||
Octet, UShort, ULong, ULongLong,
|
||||
Byte, Short, Long, LongLong,
|
||||
ExByte, ExInt8, ExShort, ExInt16,
|
||||
ExInt, ExInt32, ExLong, ExInt64,
|
||||
Bit, Float, Double, FieldTable, FieldArray)
|
||||
}));
|
||||
|
||||
var FieldTable = label('table', recursive(function() {
|
||||
return sized(function() { return 5; },
|
||||
arb.Object(
|
||||
arb.Null,
|
||||
LongStr, ShortStr, Octet,
|
||||
UShort, ULong, ULongLong,
|
||||
Byte, Short, Long, LongLong,
|
||||
ExByte, ExInt8, ExShort, ExInt16,
|
||||
ExInt, ExInt32, ExLong, ExInt64,
|
||||
Bit, Float, Double, FieldArray, FieldTable))
|
||||
}));
|
||||
|
||||
// Internal tests of our properties
|
||||
var domainProps = [
|
||||
[Octet, function(n) { return n >= 0 && n < 256; }],
|
||||
[ShortStr, function(s) { return typeof s === 'string' && s.length < 256; }],
|
||||
[LongStr, function(s) { return Buffer.isBuffer(s); }],
|
||||
[UShort, function(n) { return n >= 0 && n <= 0xffff; }],
|
||||
[ULong, function(n) { return n >= 0 && n <= 0xffffffff; }],
|
||||
[ULongLong, function(n) {
|
||||
return n >= 0 && n <= 0xffffffffffffffff; }],
|
||||
[Short, function(n) { return n >= -0x8000 && n <= 0x8000; }],
|
||||
[Long, function(n) { return n >= -0x80000000 && n < 0x80000000; }],
|
||||
[LongLong, function(n) { return n >= -0x8000000000000000 && n < 0x8000000000000000; }],
|
||||
[Bit, function(b) { return typeof b === 'boolean'; }],
|
||||
[Double, function(f) { return !isNaN(f) && isFinite(f); }],
|
||||
[Float, function(f) { return !isNaN(f) && isFinite(f) && (Math.log(Math.abs(f)) * Math.LOG10E) < 309; }],
|
||||
[Decimal, function(d) {
|
||||
return d['!'] === 'decimal' &&
|
||||
d.value['places'] <= 255 &&
|
||||
d.value['digits'] <= 0xffffffff;
|
||||
}],
|
||||
[Timestamp, function(t) { return t['!'] === 'timestamp'; }],
|
||||
[FieldTable, function(t) { return typeof t === 'object'; }],
|
||||
[FieldArray, function(a) { return Array.isArray(a); }]
|
||||
];
|
||||
|
||||
suite("Domains", function() {
|
||||
domainProps.forEach(function(p) {
|
||||
test(p[0] + ' domain',
|
||||
forAll(p[0]).satisfy(p[1]).asTest({times: 500}));
|
||||
});
|
||||
});
|
||||
|
||||
// For methods and properties (as opposed to field table values) it's
|
||||
// easier just to accept and produce numbers for timestamps.
|
||||
var ArgTimestamp = label('timestamp', ULongLong);
|
||||
|
||||
// These are the domains used in method arguments
|
||||
var ARG_TYPES = {
|
||||
'octet': Octet,
|
||||
'shortstr': ShortStr,
|
||||
'longstr': LongStr,
|
||||
'short': UShort,
|
||||
'long': ULong,
|
||||
'longlong': ULongLong,
|
||||
'bit': Bit,
|
||||
'table': FieldTable,
|
||||
'timestamp': ArgTimestamp
|
||||
};
|
||||
|
||||
function argtype(thing) {
|
||||
if (thing.default === undefined) {
|
||||
return ARG_TYPES[thing.type];
|
||||
}
|
||||
else {
|
||||
return choice(ARG_TYPES[thing.type], Undefined);
|
||||
}
|
||||
}
|
||||
|
||||
function zipObject(vals, names) {
|
||||
var obj = {};
|
||||
vals.forEach(function(v, i) { obj[names[i]] = v; });
|
||||
return obj;
|
||||
}
|
||||
|
||||
function name(arg) { return arg.name; }
|
||||
|
||||
var defs = require('../lib/defs');
|
||||
|
||||
function method(info) {
|
||||
var domain = sequence.apply(null, info.args.map(argtype));
|
||||
var names = info.args.map(name);
|
||||
return label(info.name, transform(function(fieldVals) {
|
||||
return {id: info.id,
|
||||
fields: zipObject(fieldVals, names)};
|
||||
}, domain));
|
||||
}
|
||||
|
||||
function properties(info) {
|
||||
var types = info.args.map(argtype);
|
||||
types.unshift(ULongLong); // size
|
||||
var domain = sequence.apply(null, types);
|
||||
var names = info.args.map(name);
|
||||
return label(info.name, transform(function(fieldVals) {
|
||||
return {id: info.id,
|
||||
size: fieldVals[0],
|
||||
fields: zipObject(fieldVals.slice(1), names)};
|
||||
}, domain));
|
||||
}
|
||||
|
||||
var methods = [];
|
||||
var propertieses = [];
|
||||
|
||||
for (var k in defs) {
|
||||
if (k.substr(0, 10) === 'methodInfo') {
|
||||
methods.push(method(defs[k]));
|
||||
methods[defs[k].name] = method(defs[k]);
|
||||
}
|
||||
else if (k.substr(0, 14) === 'propertiesInfo') {
|
||||
propertieses.push(properties(defs[k]));
|
||||
propertieses[defs[k].name] = properties(defs[k]);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Octet: Octet,
|
||||
ShortStr: ShortStr,
|
||||
LongStr: LongStr,
|
||||
UShort: UShort,
|
||||
ULong: ULong,
|
||||
ULongLong: ULongLong,
|
||||
Short: Short,
|
||||
Long: Long,
|
||||
LongLong: LongLong,
|
||||
Bit: Bit,
|
||||
Double: Double,
|
||||
Float: Float,
|
||||
Timestamp: Timestamp,
|
||||
Decimal: Decimal,
|
||||
UnsignedByte: UnsignedByte,
|
||||
UnsignedShort: UnsignedShort,
|
||||
UnsignedInt: UnsignedInt,
|
||||
FieldArray: FieldArray,
|
||||
FieldTable: FieldTable,
|
||||
|
||||
methods: methods,
|
||||
properties: propertieses
|
||||
};
|
||||
|
||||
module.exports.rangeInt = rangeInt;
|
||||
191
node_modules/amqplib/test/frame.js
generated
vendored
Normal file
191
node_modules/amqplib/test/frame.js
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var succeed = require('./util').succeed;
|
||||
var fail = require('./util').fail;
|
||||
var connection = require('../lib/connection');
|
||||
var Frames = connection.Connection;
|
||||
var HEARTBEAT = require('../lib/frame').HEARTBEAT;
|
||||
var Stream = require('stream');
|
||||
var PassThrough = Stream.PassThrough;
|
||||
|
||||
var defs = require('../lib/defs');
|
||||
|
||||
// We'll need to supply a stream which we manipulate ourselves
|
||||
|
||||
function inputs() {
|
||||
// don't coalesce buffers, since that could mess up properties
|
||||
// (e.g., encoded frame size)
|
||||
return new PassThrough({objectMode: true});
|
||||
}
|
||||
|
||||
var HB = Buffer.from([defs.constants.FRAME_HEARTBEAT,
|
||||
0, 0, // channel 0
|
||||
0, 0, 0, 0, // zero size
|
||||
defs.constants.FRAME_END]);
|
||||
|
||||
suite("Explicit parsing", function() {
|
||||
|
||||
test('Parse heartbeat', function() {
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
input.write(HB);
|
||||
assert(frames.recvFrame() === HEARTBEAT);
|
||||
assert(!frames.recvFrame());
|
||||
});
|
||||
|
||||
test('Parse partitioned', function() {
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
input.write(HB.subarray(0, 3));
|
||||
assert(!frames.recvFrame());
|
||||
input.write(HB.subarray(3));
|
||||
assert(frames.recvFrame() === HEARTBEAT);
|
||||
assert(!frames.recvFrame());
|
||||
});
|
||||
|
||||
function testBogusFrame(name, bytes) {
|
||||
test(name, function(done) {
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
frames.frameMax = 5; //for the max frame test
|
||||
input.write(Buffer.from(bytes));
|
||||
frames.step(function(err, frame) {
|
||||
if (err != null) done();
|
||||
else done(new Error('Was a bogus frame!'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
testBogusFrame('Wrong sized frame',
|
||||
[defs.constants.FRAME_BODY,
|
||||
0,0, 0,0,0,0, // zero length
|
||||
65, // but a byte!
|
||||
defs.constants.FRAME_END]);
|
||||
|
||||
testBogusFrame('Unknown method frame',
|
||||
[defs.constants.FRAME_METHOD,
|
||||
0,0, 0,0,0,4,
|
||||
0,0,0,0, // garbage ID
|
||||
defs.constants.FRAME_END]);
|
||||
|
||||
});
|
||||
|
||||
// Now for a bit more fun.
|
||||
|
||||
var amqp = require('./data');
|
||||
var claire = require('claire');
|
||||
var choice = claire.choice;
|
||||
var forAll = claire.forAll;
|
||||
var repeat = claire.repeat;
|
||||
var label = claire.label;
|
||||
var sequence = claire.sequence;
|
||||
var transform = claire.transform;
|
||||
var sized = claire.sized;
|
||||
|
||||
var assertEqualModuloDefaults =
|
||||
require('./codec').assertEqualModuloDefaults;
|
||||
|
||||
var Trace = label('frame trace',
|
||||
repeat(choice.apply(choice, amqp.methods)));
|
||||
|
||||
suite("Parsing", function() {
|
||||
|
||||
function testPartitioning(partition) {
|
||||
return forAll(Trace).satisfy(function(t) {
|
||||
var bufs = [];
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
var i = 0, ex;
|
||||
frames.accept = function(f) {
|
||||
// A minor hack to make sure we get the assertion exception;
|
||||
// otherwise, it's just a test that we reached the line
|
||||
// incrementing `i` for each frame.
|
||||
try {
|
||||
assertEqualModuloDefaults(t[i], f.fields);
|
||||
}
|
||||
catch (e) {
|
||||
ex = e;
|
||||
}
|
||||
i++;
|
||||
};
|
||||
|
||||
t.forEach(function(f) {
|
||||
f.channel = 0;
|
||||
bufs.push(defs.encodeMethod(f.id, 0, f.fields));
|
||||
});
|
||||
|
||||
partition(bufs).forEach(function (chunk) { input.write(chunk); });
|
||||
frames.acceptLoop();
|
||||
if (ex) throw ex;
|
||||
return i === t.length;
|
||||
}).asTest({times: 20})
|
||||
};
|
||||
|
||||
test("Parse trace of methods",
|
||||
testPartitioning(function(bufs) { return bufs; }));
|
||||
|
||||
test("Parse concat'd methods",
|
||||
testPartitioning(function(bufs) {
|
||||
return [Buffer.concat(bufs)];
|
||||
}));
|
||||
|
||||
test("Parse partitioned methods",
|
||||
testPartitioning(function(bufs) {
|
||||
var full = Buffer.concat(bufs);
|
||||
var onethird = Math.floor(full.length / 3);
|
||||
var twothirds = 2 * onethird;
|
||||
return [
|
||||
full.subarray(0, onethird),
|
||||
full.subarray(onethird, twothirds),
|
||||
full.subarray(twothirds)
|
||||
];
|
||||
}));
|
||||
});
|
||||
|
||||
var FRAME_MAX_MAX = 4096 * 4;
|
||||
var FRAME_MAX_MIN = 4096;
|
||||
|
||||
var FrameMax = amqp.rangeInt('frame max',
|
||||
FRAME_MAX_MIN,
|
||||
FRAME_MAX_MAX);
|
||||
|
||||
var Body = sized(function(_n) {
|
||||
return Math.floor(Math.random() * FRAME_MAX_MAX);
|
||||
}, repeat(amqp.Octet));
|
||||
|
||||
var Content = transform(function(args) {
|
||||
return {
|
||||
method: args[0].fields,
|
||||
header: args[1].fields,
|
||||
body: Buffer.from(args[2])
|
||||
}
|
||||
}, sequence(amqp.methods['BasicDeliver'],
|
||||
amqp.properties['BasicProperties'], Body));
|
||||
|
||||
suite("Content framing", function() {
|
||||
test("Adhere to frame max",
|
||||
forAll(Content, FrameMax).satisfy(function(content, max) {
|
||||
var input = inputs();
|
||||
var frames = new Frames(input);
|
||||
frames.frameMax = max;
|
||||
frames.sendMessage(
|
||||
0,
|
||||
defs.BasicDeliver, content.method,
|
||||
defs.BasicProperties, content.header,
|
||||
content.body);
|
||||
var f, i = 0, largest = 0;
|
||||
while (f = input.read()) {
|
||||
i++;
|
||||
if (f.length > largest) largest = f.length;
|
||||
if (f.length > max) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// The ratio of frames to 'contents' should always be >= 2
|
||||
// (one properties frame and at least one content frame); > 2
|
||||
// indicates fragmentation. The largest is always, of course <= frame max
|
||||
//console.log('Frames: %d; frames per message: %d; largest frame %d', i, i / t.length, largest);
|
||||
return true;
|
||||
}).asTest());
|
||||
});
|
||||
203
node_modules/amqplib/test/mux.js
generated
vendored
Normal file
203
node_modules/amqplib/test/mux.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var Mux = require('../lib/mux').Mux;
|
||||
var PassThrough = require('stream').PassThrough;
|
||||
|
||||
var latch = require('./util').latch;
|
||||
var schedule = require('./util').schedule;
|
||||
|
||||
function stream() {
|
||||
return new PassThrough({objectMode: true});
|
||||
}
|
||||
|
||||
function readAllObjects(s, cb) {
|
||||
var objs = [];
|
||||
|
||||
function read() {
|
||||
var v = s.read();
|
||||
while (v !== null) {
|
||||
objs.push(v);
|
||||
v = s.read();
|
||||
}
|
||||
}
|
||||
|
||||
s.on('end', function() { cb(objs); });
|
||||
s.on('readable', read);
|
||||
|
||||
read();
|
||||
}
|
||||
|
||||
test("single input", function(done) {
|
||||
var input = stream();
|
||||
var output = stream();
|
||||
input.on('end', function() { output.end() });
|
||||
|
||||
var mux = new Mux(output);
|
||||
mux.pipeFrom(input);
|
||||
|
||||
var data = [1,2,3,4,5,6,7,8,9];
|
||||
// not 0, it's treated specially by PassThrough for some reason. By
|
||||
// 'specially' I mean it breaks the stream. See e.g.,
|
||||
// https://github.com/isaacs/readable-stream/pull/55
|
||||
data.forEach(function (chunk) { input.write(chunk); });
|
||||
|
||||
readAllObjects(output, function(vals) {
|
||||
assert.deepEqual(data, vals);
|
||||
done();
|
||||
});
|
||||
|
||||
input.end();
|
||||
});
|
||||
|
||||
test("single input, resuming stream", function(done) {
|
||||
var input = stream();
|
||||
var output = stream();
|
||||
input.on('end', function() { output.end() });
|
||||
|
||||
var mux = new Mux(output);
|
||||
mux.pipeFrom(input);
|
||||
|
||||
// Streams might be blocked and become readable again, simulate this
|
||||
// using a special read function and a marker
|
||||
var data = [1,2,3,4,'skip',6,7,8,9];
|
||||
|
||||
var oldRead = input.read;
|
||||
input.read = function(size) {
|
||||
var val = oldRead.call(input, size)
|
||||
|
||||
if (val === 'skip') {
|
||||
input.emit('readable');
|
||||
return null
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
data.forEach(function (chunk) { input.write(chunk); });
|
||||
|
||||
readAllObjects(output, function(vals) {
|
||||
assert.deepEqual([1,2,3,4,6,7,8,9], vals);
|
||||
done();
|
||||
});
|
||||
|
||||
input.end();
|
||||
});
|
||||
|
||||
test("two sequential inputs", function(done) {
|
||||
var input1 = stream();
|
||||
var input2 = stream();
|
||||
var output = stream();
|
||||
var mux = new Mux(output);
|
||||
mux.pipeFrom(input1);
|
||||
mux.pipeFrom(input2);
|
||||
|
||||
var data = [1,2,3,4,5,6,7,8,9];
|
||||
data.forEach(function(v) { input1.write(v); });
|
||||
|
||||
input1.on('end', function() {
|
||||
data.forEach(function (v) { input2.write(v); });
|
||||
input2.end();
|
||||
});
|
||||
input2.on('end', function() { output.end(); });
|
||||
|
||||
input1.end();
|
||||
readAllObjects(output, function(vs) {
|
||||
assert.equal(2 * data.length, vs.length);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test("two interleaved inputs", function(done) {
|
||||
var input1 = stream();
|
||||
var input2 = stream();
|
||||
var output = stream();
|
||||
var mux = new Mux(output);
|
||||
mux.pipeFrom(input1);
|
||||
mux.pipeFrom(input2);
|
||||
|
||||
var endLatch = latch(2, function() { output.end(); });
|
||||
input1.on('end', endLatch);
|
||||
input2.on('end', endLatch);
|
||||
|
||||
var data = [1,2,3,4,5,6,7,8,9];
|
||||
data.forEach(function(v) { input1.write(v); });
|
||||
input1.end();
|
||||
|
||||
data.forEach(function(v) { input2.write(v); });
|
||||
input2.end();
|
||||
|
||||
readAllObjects(output, function(vs) {
|
||||
assert.equal(2 * data.length, vs.length);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test("unpipe", function(done) {
|
||||
var input = stream();
|
||||
var output = stream();
|
||||
var mux = new Mux(output);
|
||||
|
||||
var pipedData = [1,2,3,4,5];
|
||||
var unpipedData = [6,7,8,9];
|
||||
|
||||
mux.pipeFrom(input);
|
||||
|
||||
schedule(function() {
|
||||
pipedData.forEach(function (chunk) { input.write(chunk); });
|
||||
|
||||
schedule(function() {
|
||||
mux.unpipeFrom(input);
|
||||
|
||||
schedule(function() {
|
||||
unpipedData.forEach(function(chunk) { input.write(chunk); });
|
||||
input.end();
|
||||
schedule(function() {
|
||||
// exhaust so that 'end' fires
|
||||
var v; while (v = input.read());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
input.on('end', function() {
|
||||
output.end();
|
||||
});
|
||||
|
||||
readAllObjects(output, function(vals) {
|
||||
try {
|
||||
assert.deepEqual(pipedData, vals);
|
||||
done();
|
||||
}
|
||||
catch (e) { done(e); }
|
||||
});
|
||||
});
|
||||
|
||||
test("roundrobin", function(done) {
|
||||
var input1 = stream();
|
||||
var input2 = stream();
|
||||
var output = stream();
|
||||
var mux = new Mux(output);
|
||||
|
||||
mux.pipeFrom(input1);
|
||||
mux.pipeFrom(input2);
|
||||
|
||||
var endLatch = latch(2, function() { output.end(); });
|
||||
input1.on('end', endLatch);
|
||||
input2.on('end', endLatch);
|
||||
|
||||
var ones = [1,1,1,1,1];
|
||||
ones.forEach(function(v) { input1.write(v); });
|
||||
input1.end();
|
||||
|
||||
var twos = [2,2,2,2,2];
|
||||
twos.forEach(function(v) { input2.write(v); });
|
||||
input2.end();
|
||||
|
||||
readAllObjects(output, function(vs) {
|
||||
assert.deepEqual([1,2,1,2,1,2,1,2,1,2], vs);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
217
node_modules/amqplib/test/util.js
generated
vendored
Normal file
217
node_modules/amqplib/test/util.js
generated
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
'use strict';
|
||||
|
||||
var crypto = require('crypto');
|
||||
var Connection = require('../lib/connection').Connection;
|
||||
var PassThrough = require('stream').PassThrough;
|
||||
var defs = require('../lib/defs');
|
||||
var assert = require('assert');
|
||||
|
||||
var schedule = (typeof setImmediate === 'function') ?
|
||||
setImmediate : process.nextTick;
|
||||
|
||||
function randomString() {
|
||||
var hash = crypto.createHash('sha1');
|
||||
hash.update(crypto.randomBytes(64));
|
||||
return hash.digest('base64');
|
||||
}
|
||||
|
||||
|
||||
// Set up a socket pair {client, server}, such that writes to the
|
||||
// client are readable from the server, and writes to the server are
|
||||
// readable at the client.
|
||||
//
|
||||
// +---+ +---+
|
||||
// | C | | S |
|
||||
// --write->| l |----->| e |--read-->
|
||||
// | i | | r |
|
||||
// <--read--| e |<-----| v |<-write--
|
||||
// | n | | e |
|
||||
// | t | | r |
|
||||
// +---+ +---+
|
||||
//
|
||||
// I also need to make sure that end called on either socket affects
|
||||
// the other.
|
||||
|
||||
function socketPair() {
|
||||
var server = new PassThrough();
|
||||
var client = new PassThrough();
|
||||
server.write = client.push.bind(client);
|
||||
client.write = server.push.bind(server);
|
||||
function end(chunk, encoding) {
|
||||
if (chunk) this.push(chunk, encoding);
|
||||
this.push(null);
|
||||
}
|
||||
server.end = end.bind(client);
|
||||
client.end = end.bind(server);
|
||||
|
||||
return {client: client, server: server};
|
||||
}
|
||||
|
||||
function runServer(socket, run) {
|
||||
var frames = new Connection(socket);
|
||||
// We will be closing the socket without doing a closing handshake,
|
||||
// so cheat
|
||||
frames.expectSocketClose = true;
|
||||
// We also need to create some channel buffers, again a cheat
|
||||
frames.freshChannel(null);
|
||||
frames.freshChannel(null);
|
||||
frames.freshChannel(null);
|
||||
|
||||
function send(id, fields, channel, content) {
|
||||
channel = channel || 0;
|
||||
if (content) {
|
||||
schedule(function() {
|
||||
frames.sendMessage(channel, id, fields,
|
||||
defs.BasicProperties, fields,
|
||||
content);
|
||||
});
|
||||
}
|
||||
else {
|
||||
schedule(function() {
|
||||
frames.sendMethod(channel, id, fields);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function wait(method) {
|
||||
return function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (method) {
|
||||
frames.step(function(e, f) {
|
||||
if (e !== null) return reject(e);
|
||||
if (f.id === method)
|
||||
resolve(f);
|
||||
else
|
||||
reject(new Error("Expected method: " + method +
|
||||
", got " + f.id));
|
||||
});
|
||||
}
|
||||
else {
|
||||
frames.step(function(e, f) {
|
||||
if (e !== null) return reject(e);
|
||||
else resolve(f);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
run(send, wait);
|
||||
return frames;
|
||||
}
|
||||
|
||||
// Produce a callback that will complete the test successfully
|
||||
function succeed(done) {
|
||||
return function() { done(); }
|
||||
}
|
||||
|
||||
// Produce a callback that will complete the test successfully
|
||||
// only if the value is an object, it has the specified
|
||||
// attribute, and its value is equals to the expected value
|
||||
function succeedIfAttributeEquals(attribute, value, done) {
|
||||
return function(object) {
|
||||
if (object && !(object instanceof Error) && value === object[attribute]) {
|
||||
return done();
|
||||
}
|
||||
|
||||
done(new Error(attribute + " is not equal to " + value));
|
||||
};
|
||||
}
|
||||
|
||||
// Produce a callback that will fail the test, given either an error
|
||||
// (to be used as a failure continuation) or any other value (to be
|
||||
// used as a success continuation when failure is expected)
|
||||
function fail(done) {
|
||||
return function(err) {
|
||||
if (err instanceof Error) done(err);
|
||||
else done(new Error("Expected to fail, instead got " + err.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
// Create a function that will call done once it's been called itself
|
||||
// `count` times. If it's called with an error value, it will
|
||||
// immediately call done with that error value.
|
||||
function latch(count, done) {
|
||||
var awaiting = count;
|
||||
var alive = true;
|
||||
return function(err) {
|
||||
if (err instanceof Error && alive) {
|
||||
alive = false;
|
||||
done(err);
|
||||
}
|
||||
else {
|
||||
awaiting--;
|
||||
if (awaiting === 0 && alive) {
|
||||
alive = false;
|
||||
done();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Call a thunk with a continuation that will be called with an error
|
||||
// if the thunk throws one, or nothing if it runs to completion.
|
||||
function completes(thunk, done) {
|
||||
try {
|
||||
thunk();
|
||||
done();
|
||||
}
|
||||
catch (e) { done(e); }
|
||||
}
|
||||
|
||||
// Construct a Node.JS-style callback from a success continuation and
|
||||
// an error continuation
|
||||
function kCallback(k, ek) {
|
||||
return function(err, val) {
|
||||
if (err === null) k && k(val);
|
||||
else ek && ek(err);
|
||||
};
|
||||
}
|
||||
|
||||
// A noddy way to make tests depend on the node version.
|
||||
function versionGreaterThan(actual, spec) {
|
||||
|
||||
function int(e) { return parseInt(e); }
|
||||
|
||||
var version = actual.split('.').map(int);
|
||||
var desired = spec.split('.').map(int);
|
||||
for (var i=0; i < desired.length; i++) {
|
||||
var a = version[i], b = desired[i];
|
||||
if (a != b) return a > b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
suite('versionGreaterThan', function() {
|
||||
|
||||
test('full spec', function() {
|
||||
assert(versionGreaterThan('0.8.26', '0.6.12'));
|
||||
assert(versionGreaterThan('0.8.26', '0.8.21'));
|
||||
});
|
||||
|
||||
test('partial spec', function() {
|
||||
assert(versionGreaterThan('0.9.12', '0.8'));
|
||||
});
|
||||
|
||||
test('not greater', function() {
|
||||
assert(!versionGreaterThan('0.8.12', '0.8.26'));
|
||||
assert(!versionGreaterThan('0.6.2', '0.6.12'));
|
||||
assert(!versionGreaterThan('0.8.29', '0.8'));
|
||||
});
|
||||
|
||||
test
|
||||
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
socketPair: socketPair,
|
||||
runServer: runServer,
|
||||
succeed: succeed,
|
||||
succeedIfAttributeEquals: succeedIfAttributeEquals,
|
||||
fail: fail,
|
||||
latch: latch,
|
||||
completes: completes,
|
||||
kCallback: kCallback,
|
||||
schedule: schedule,
|
||||
randomString: randomString,
|
||||
versionGreaterThan: versionGreaterThan
|
||||
};
|
||||
Reference in New Issue
Block a user