<commit_before>/*
 * vim: ts=4:sw=4:expandtab
 */
;(function() {
    'use strict';

    function isStringable(thing) {
        return (thing === Object(thing) &&
                    (thing.__proto__ == StaticArrayBufferProto ||
                    thing.__proto__ == StaticUint8ArrayProto ||
                    thing.__proto__ == StaticByteBufferProto));
    }
    function convertToArrayBuffer(thing) {
        if (thing === undefined) {
            return undefined;
        }
        if (thing === Object(thing)) {
            if (thing.__proto__ == StaticArrayBufferProto) {
                return thing;
            }
            //TODO: Several more cases here...
        }

        if (thing instanceof Array) {
            // Assuming Uint16Array from curve25519
            var res = new ArrayBuffer(thing.length * 2);
            var uint = new Uint16Array(res);
            for (var i = 0; i < thing.length; i++) {
                uint[i] = thing[i];
            }
            return res;
        }

        var str;
        if (isStringable(thing)) {
            str = stringObject(thing);
        } else if (typeof thing == "string") {
            str = thing;
        } else {
            throw new Error("Tried to convert a non-stringable thing of type " + typeof thing + " to an array buffer");
        }
        var res = new ArrayBuffer(str.length);
        var uint = new Uint8Array(res);
        for (var i = 0; i < str.length; i++) {
            uint[i] = str.charCodeAt(i);
        }
        return res;
    }

    function equalArrayBuffers(ab1, ab2) {
        if (!(ab1 instanceof ArrayBuffer && ab2 instanceof ArrayBuffer)) {
            return false;
        }
        if (ab1.byteLength !== ab2.byteLength) {
            return false;
        }
        var result = true;
        var ta1 = new Uint8Array(ab1);
        var ta2 = new Uint8Array(ab2);
        for (var i = 0; i < ab1.byteLength; ++i) {
            if (ta1[i] !== ta2[i]) { result = false; }
        }
        return result;
    }

    var Model = Backbone.Model.extend({ database: Whisper.Database });
    var PreKey = Model.extend({ storeName: 'preKeys' });
    var SignedPreKey = Model.extend({ storeName: 'signedPreKeys' });
    var Session = Model.extend({ storeName: 'sessions' });
    var SessionCollection = Backbone.Collection.extend({
        storeName: 'sessions',
        database: Whisper.Database,
        model: Session,
        fetchSessionsForNumber: function(number) {
            return this.fetch({range: [number + '.1', number + '.' + ':']});
        }
    });
    var IdentityKey = Model.extend({ storeName: 'identityKeys' });
    var Group = Model.extend({ storeName: 'groups' });
    var Item = Model.extend({ storeName: 'items' });

    function AxolotlStore() {}

    AxolotlStore.prototype = {
        constructor: AxolotlStore,
        getMyIdentityKey: function() {
            var item = new Item({id: 'identityKey'});
            return new Promise(function(resolve) {
                item.fetch().then(function() {
                    resolve(item.get('value'));
                });
            });
        },
        getMyRegistrationId: function() {
            var item = new Item({id: 'registrationId'});
            return new Promise(function(resolve) {
                item.fetch().then(function() {
                    resolve(item.get('value'));
                });
            });
        },

        /* Returns a prekeypair object or undefined */
        getPreKey: function(keyId) {
            var prekey = new PreKey({id: keyId});
            return new Promise(function(resolve) {
                prekey.fetch().then(function() {
                    resolve({
                        pubKey: prekey.attributes.publicKey,
                        privKey: prekey.attributes.privateKey
                    });
                }).fail(resolve);
            });
        },
        putPreKey: function(keyId, keyPair) {
            var prekey = new PreKey({
                id         : keyId,
                publicKey  : keyPair.pubKey,
                privateKey : keyPair.privKey
            });
            return new Promise(function(resolve) {
                prekey.save().always(function() {
                    resolve();
                });
            });
        },
        removePreKey: function(keyId) {
            var prekey = new PreKey({id: keyId});

            new Promise(function(resolve) {
                getAccountManager().refreshPreKeys().then(resolve);
            });

            return new Promise(function(resolve) {
                prekey.destroy().then(function() {
                    resolve();
                });
            });
        },

        /* Returns a signed keypair object or undefined */
        getSignedPreKey: function(keyId) {
            var prekey = new SignedPreKey({id: keyId});
            return new Promise(function(resolve) {
                prekey.fetch().then(function() {
                    resolve({
                        pubKey: prekey.attributes.publicKey,
                        privKey: prekey.attributes.privateKey
                    });
                }).fail(resolve);
            });
        },
        putSignedPreKey: function(keyId, keyPair) {
            var prekey = new SignedPreKey({
                id         : keyId,
                publicKey  : keyPair.pubKey,
                privateKey : keyPair.privKey
            });
            return new Promise(function(resolve) {
                prekey.save().always(function() {
                    resolve();
                });
            });
        },
        removeSignedPreKey: function(keyId) {
            var prekey = new SignedPreKey({id: keyId});
            return new Promise(function(resolve) {
                prekey.destroy().then(function() {
                    resolve();
                });
            });
        },

        getSession: function(encodedNumber) {
            console.log('getSession', encodedNumber);
            if (encodedNumber === null || encodedNumber === undefined) {
                throw new Error("Tried to get session for undefined/null number");
            }
            return new Promise(function(resolve) {
                var session = new Session({id: encodedNumber});
                console.log('fetching session for', encodedNumber);
                session.fetch().always(function() {
                    console.log('returned session for', encodedNumber);
                    resolve(session.get('record'));
                });

            });
        },
        putSession: function(encodedNumber, record) {
            console.log('putSession', encodedNumber);
            if (encodedNumber === null || encodedNumber === undefined) {
                throw new Error("Tried to put session for undefined/null number");
            }
            return new Promise(function(resolve) {
                var number = textsecure.utils.unencodeNumber(encodedNumber)[0];
                var deviceId = parseInt(textsecure.utils.unencodeNumber(encodedNumber)[1]);

                var session = new Session({id: encodedNumber});
                console.log('fetching session for', encodedNumber);
                session.fetch().always(function() {
                    console.log('saving session for', encodedNumber);
                    session.save({
                        record: record,
                        deviceId: deviceId,
                        number: number
                    }).always(function() {
                        console.log('saved session for', encodedNumber);
                        resolve();
                    });
                });
            });
        },
        getDeviceIds: function(number) {
            if (number === null || number === undefined) {
                throw new Error("Tried to get device ids for undefined/null number");
            }
            return new Promise(function(resolve) {
                var sessions = new SessionCollection();
                sessions.fetchSessionsForNumber(number).always(function() {
                    resolve(sessions.pluck('deviceId'));
                });
            });
        },
        removeSession: function(encodedNumber) {
            return new Promise(function(resolve) {
                var session = new Session({id: encodedNumber});
                session.fetch().then(function() {
                    session.destroy().then(resolve);
                });
            });
        },
        removeAllSessions: function(number) {
            if (number === null || number === undefined) {
                throw new Error("Tried to remove sessions for undefined/null number");
            }
            return new Promise(function(resolve) {
                var sessions = new SessionCollection();
                sessions.fetchSessionsForNumber(number).always(function() {
                    var promises = [];
                    while (sessions.length > 0) {
                        promises.push(new Promise(function(res) {
                            sessions.pop().destroy().then(res);
                        }));
                    }
                    Promise.all(promises).then(resolve);
                });
            });
        },
        clearSessionStore: function() {
            return new Promise(function(resolve) {
                var sessions = new SessionCollection();
                sessions.sync('delete', sessions, {}).always(resolve);
            });

        },
        getIdentityKey: function(identifier) {
            if (identifier === null || identifier === undefined) {
                throw new Error("Tried to get identity key for undefined/null key");
            }
            var number = textsecure.utils.unencodeNumber(identifier)[0];
            return new Promise(function(resolve) {
                var identityKey = new IdentityKey({id: number});
                identityKey.fetch().always(function() {
                    resolve(identityKey.get('publicKey'));
                });
            });
        },
        putIdentityKey: function(identifier, publicKey) {
            if (identifier === null || identifier === undefined) {
                throw new Error("Tried to put identity key for undefined/null key");
            }
            if (!(publicKey instanceof ArrayBuffer)) {
                publicKey = convertToArrayBuffer(publicKey);
            }
            var number = textsecure.utils.unencodeNumber(identifier)[0];
            return new Promise(function(resolve, reject) {
                var identityKey = new IdentityKey({id: number});
                identityKey.fetch().always(function() {
                    var oldpublicKey = identityKey.get('publicKey');
                    if (!oldpublicKey) {
                        // Lookup failed, or the current key was removed, so save this one.
                        identityKey.save({publicKey: publicKey}).then(resolve);
                    } else {
                        // Key exists, if it matches do nothing, else throw
                        if (equalArrayBuffers(oldpublicKey, publicKey)) {
                            resolve();
                        } else {
                            reject(new Error("Attempted to overwrite a different identity key"));
                        }
                    }
                });
            });
        },
        removeIdentityKey: function(number) {
            return new Promise(function(resolve, reject) {
                var identityKey = new IdentityKey({id: number});
                identityKey.fetch().then(function() {
                    identityKey.save({publicKey: undefined});
                }).fail(function() {
                    reject(new Error("Tried to remove identity for unknown number"));
                });
                resolve(textsecure.storage.axolotl.removeAllSessions(number));
            });
        },
        getGroup: function(groupId) {
            if (groupId === null || groupId === undefined) {
                throw new Error("Tried to get group for undefined/null id");
            }
            return new Promise(function(resolve) {
                var group = new Group({id: groupId});
                group.fetch().always(function() {
                    resolve(group.get('data'));
                });
            });
        },
        putGroup: function(groupId, group) {
            if (groupId === null || groupId === undefined) {
                throw new Error("Tried to put group key for undefined/null id");
            }
            if (group === null || group === undefined) {
                throw new Error("Tried to put undefined/null group object");
            }
            var group = new Group({id: groupId, data: group});
            return new Promise(function(resolve) {
                group.save().always(resolve);
            });
        },
        removeGroup: function(groupId) {
            if (groupId === null || groupId === undefined) {
                throw new Error("Tried to remove group key for undefined/null id");
            }
            return new Promise(function(resolve) {
                var group = new Group({id: groupId});
                group.destroy().always(resolve);
            });
        },

    };

    window.AxolotlStore = AxolotlStore;
})();
<commit_msg>Turn off session logging<commit_after>/*
 * vim: ts=4:sw=4:expandtab
 */
;(function() {
    'use strict';

    function isStringable(thing) {
        return (thing === Object(thing) &&
                    (thing.__proto__ == StaticArrayBufferProto ||
                    thing.__proto__ == StaticUint8ArrayProto ||
                    thing.__proto__ == StaticByteBufferProto));
    }
    function convertToArrayBuffer(thing) {
        if (thing === undefined) {
            return undefined;
        }
        if (thing === Object(thing)) {
            if (thing.__proto__ == StaticArrayBufferProto) {
                return thing;
            }
            //TODO: Several more cases here...
        }

        if (thing instanceof Array) {
            // Assuming Uint16Array from curve25519
            var res = new ArrayBuffer(thing.length * 2);
            var uint = new Uint16Array(res);
            for (var i = 0; i < thing.length; i++) {
                uint[i] = thing[i];
            }
            return res;
        }

        var str;
        if (isStringable(thing)) {
            str = stringObject(thing);
        } else if (typeof thing == "string") {
            str = thing;
        } else {
            throw new Error("Tried to convert a non-stringable thing of type " + typeof thing + " to an array buffer");
        }
        var res = new ArrayBuffer(str.length);
        var uint = new Uint8Array(res);
        for (var i = 0; i < str.length; i++) {
            uint[i] = str.charCodeAt(i);
        }
        return res;
    }

    function equalArrayBuffers(ab1, ab2) {
        if (!(ab1 instanceof ArrayBuffer && ab2 instanceof ArrayBuffer)) {
            return false;
        }
        if (ab1.byteLength !== ab2.byteLength) {
            return false;
        }
        var result = true;
        var ta1 = new Uint8Array(ab1);
        var ta2 = new Uint8Array(ab2);
        for (var i = 0; i < ab1.byteLength; ++i) {
            if (ta1[i] !== ta2[i]) { result = false; }
        }
        return result;
    }

    var Model = Backbone.Model.extend({ database: Whisper.Database });
    var PreKey = Model.extend({ storeName: 'preKeys' });
    var SignedPreKey = Model.extend({ storeName: 'signedPreKeys' });
    var Session = Model.extend({ storeName: 'sessions' });
    var SessionCollection = Backbone.Collection.extend({
        storeName: 'sessions',
        database: Whisper.Database,
        model: Session,
        fetchSessionsForNumber: function(number) {
            return this.fetch({range: [number + '.1', number + '.' + ':']});
        }
    });
    var IdentityKey = Model.extend({ storeName: 'identityKeys' });
    var Group = Model.extend({ storeName: 'groups' });
    var Item = Model.extend({ storeName: 'items' });

    function AxolotlStore() {}

    AxolotlStore.prototype = {
        constructor: AxolotlStore,
        getMyIdentityKey: function() {
            var item = new Item({id: 'identityKey'});
            return new Promise(function(resolve) {
                item.fetch().then(function() {
                    resolve(item.get('value'));
                });
            });
        },
        getMyRegistrationId: function() {
            var item = new Item({id: 'registrationId'});
            return new Promise(function(resolve) {
                item.fetch().then(function() {
                    resolve(item.get('value'));
                });
            });
        },

        /* Returns a prekeypair object or undefined */
        getPreKey: function(keyId) {
            var prekey = new PreKey({id: keyId});
            return new Promise(function(resolve) {
                prekey.fetch().then(function() {
                    resolve({
                        pubKey: prekey.attributes.publicKey,
                        privKey: prekey.attributes.privateKey
                    });
                }).fail(resolve);
            });
        },
        putPreKey: function(keyId, keyPair) {
            var prekey = new PreKey({
                id         : keyId,
                publicKey  : keyPair.pubKey,
                privateKey : keyPair.privKey
            });
            return new Promise(function(resolve) {
                prekey.save().always(function() {
                    resolve();
                });
            });
        },
        removePreKey: function(keyId) {
            var prekey = new PreKey({id: keyId});

            new Promise(function(resolve) {
                getAccountManager().refreshPreKeys().then(resolve);
            });

            return new Promise(function(resolve) {
                prekey.destroy().then(function() {
                    resolve();
                });
            });
        },

        /* Returns a signed keypair object or undefined */
        getSignedPreKey: function(keyId) {
            var prekey = new SignedPreKey({id: keyId});
            return new Promise(function(resolve) {
                prekey.fetch().then(function() {
                    resolve({
                        pubKey: prekey.attributes.publicKey,
                        privKey: prekey.attributes.privateKey
                    });
                }).fail(resolve);
            });
        },
        putSignedPreKey: function(keyId, keyPair) {
            var prekey = new SignedPreKey({
                id         : keyId,
                publicKey  : keyPair.pubKey,
                privateKey : keyPair.privKey
            });
            return new Promise(function(resolve) {
                prekey.save().always(function() {
                    resolve();
                });
            });
        },
        removeSignedPreKey: function(keyId) {
            var prekey = new SignedPreKey({id: keyId});
            return new Promise(function(resolve) {
                prekey.destroy().then(function() {
                    resolve();
                });
            });
        },

        getSession: function(encodedNumber) {
            if (encodedNumber === null || encodedNumber === undefined) {
                throw new Error("Tried to get session for undefined/null number");
            }
            return new Promise(function(resolve) {
                var session = new Session({id: encodedNumber});
                session.fetch().always(function() {
                    resolve(session.get('record'));
                });

            });
        },
        putSession: function(encodedNumber, record) {
            if (encodedNumber === null || encodedNumber === undefined) {
                throw new Error("Tried to put session for undefined/null number");
            }
            return new Promise(function(resolve) {
                var number = textsecure.utils.unencodeNumber(encodedNumber)[0];
                var deviceId = parseInt(textsecure.utils.unencodeNumber(encodedNumber)[1]);

                var session = new Session({id: encodedNumber});
                session.fetch().always(function() {
                    session.save({
                        record: record,
                        deviceId: deviceId,
                        number: number
                    }).always(function() {
                        resolve();
                    });
                });
            });
        },
        getDeviceIds: function(number) {
            if (number === null || number === undefined) {
                throw new Error("Tried to get device ids for undefined/null number");
            }
            return new Promise(function(resolve) {
                var sessions = new SessionCollection();
                sessions.fetchSessionsForNumber(number).always(function() {
                    resolve(sessions.pluck('deviceId'));
                });
            });
        },
        removeSession: function(encodedNumber) {
            return new Promise(function(resolve) {
                var session = new Session({id: encodedNumber});
                session.fetch().then(function() {
                    session.destroy().then(resolve);
                });
            });
        },
        removeAllSessions: function(number) {
            if (number === null || number === undefined) {
                throw new Error("Tried to remove sessions for undefined/null number");
            }
            return new Promise(function(resolve) {
                var sessions = new SessionCollection();
                sessions.fetchSessionsForNumber(number).always(function() {
                    var promises = [];
                    while (sessions.length > 0) {
                        promises.push(new Promise(function(res) {
                            sessions.pop().destroy().then(res);
                        }));
                    }
                    Promise.all(promises).then(resolve);
                });
            });
        },
        clearSessionStore: function() {
            return new Promise(function(resolve) {
                var sessions = new SessionCollection();
                sessions.sync('delete', sessions, {}).always(resolve);
            });

        },
        getIdentityKey: function(identifier) {
            if (identifier === null || identifier === undefined) {
                throw new Error("Tried to get identity key for undefined/null key");
            }
            var number = textsecure.utils.unencodeNumber(identifier)[0];
            return new Promise(function(resolve) {
                var identityKey = new IdentityKey({id: number});
                identityKey.fetch().always(function() {
                    resolve(identityKey.get('publicKey'));
                });
            });
        },
        putIdentityKey: function(identifier, publicKey) {
            if (identifier === null || identifier === undefined) {
                throw new Error("Tried to put identity key for undefined/null key");
            }
            if (!(publicKey instanceof ArrayBuffer)) {
                publicKey = convertToArrayBuffer(publicKey);
            }
            var number = textsecure.utils.unencodeNumber(identifier)[0];
            return new Promise(function(resolve, reject) {
                var identityKey = new IdentityKey({id: number});
                identityKey.fetch().always(function() {
                    var oldpublicKey = identityKey.get('publicKey');
                    if (!oldpublicKey) {
                        // Lookup failed, or the current key was removed, so save this one.
                        identityKey.save({publicKey: publicKey}).then(resolve);
                    } else {
                        // Key exists, if it matches do nothing, else throw
                        if (equalArrayBuffers(oldpublicKey, publicKey)) {
                            resolve();
                        } else {
                            reject(new Error("Attempted to overwrite a different identity key"));
                        }
                    }
                });
            });
        },
        removeIdentityKey: function(number) {
            return new Promise(function(resolve, reject) {
                var identityKey = new IdentityKey({id: number});
                identityKey.fetch().then(function() {
                    identityKey.save({publicKey: undefined});
                }).fail(function() {
                    reject(new Error("Tried to remove identity for unknown number"));
                });
                resolve(textsecure.storage.axolotl.removeAllSessions(number));
            });
        },
        getGroup: function(groupId) {
            if (groupId === null || groupId === undefined) {
                throw new Error("Tried to get group for undefined/null id");
            }
            return new Promise(function(resolve) {
                var group = new Group({id: groupId});
                group.fetch().always(function() {
                    resolve(group.get('data'));
                });
            });
        },
        putGroup: function(groupId, group) {
            if (groupId === null || groupId === undefined) {
                throw new Error("Tried to put group key for undefined/null id");
            }
            if (group === null || group === undefined) {
                throw new Error("Tried to put undefined/null group object");
            }
            var group = new Group({id: groupId, data: group});
            return new Promise(function(resolve) {
                group.save().always(resolve);
            });
        },
        removeGroup: function(groupId) {
            if (groupId === null || groupId === undefined) {
                throw new Error("Tried to remove group key for undefined/null id");
            }
            return new Promise(function(resolve) {
                var group = new Group({id: groupId});
                group.destroy().always(resolve);
            });
        },

    };

    window.AxolotlStore = AxolotlStore;
})();
<|endoftext|>


<commit_before>var tape         = require('tape'),
    levelup      = require('levelup'),
    down         = require('memdown'),
    transactions = require('../');

tape('Async Lock',function(t){
  t.plan(2);

  var db = levelup('test', {
    db: down,
    valueEncoding: 'json'
  });
  transactions(db);

  var tx = db.transaction();
  var tx2 = db.transaction();

  tx2.get('k', function(err, value){
    t.equal(value, 167, 'tx2 get equals 167');
    tx2.put('k', value+1);
  });

  tx.put('k', 167);
  tx.commit();

  tx2.commit(function(err){
    db.get('k', function(err, value){
      t.equal(value, 168, 'db get equals 168');
    });
  });

});

<commit_msg>break stuff again<commit_after>var tape         = require('tape'),
    levelup      = require('levelup'),
    down         = require('memdown'),
    transactions = require('../');

tape('Async Lock',function(t){
  t.plan(2);

  var db = levelup('test', {
    db: down,
    valueEncoding: 'json'
  });
  transactions(db);

  var tx = db.transaction();
  var tx2 = db.transaction();

  tx.put('k', 167);

  tx2.get('k', function(err, value){
    t.equal(value, 167, 'tx2 get equals 167');
    tx2.put('k', value+1);
  });

  tx.commit();

  tx2.commit(function(err){
    db.get('k', function(err, value){
      t.equal(value, 168, 'db get equals 168');
    });
  });
});

tape('Async Lock',function(t){
  t.plan(2);

  var db = levelup('test', {
    db: down,
    valueEncoding: 'json'
  });
  transactions(db);

  var tx = db.transaction();
  var tx2 = db.transaction();

  tx.put('k', 167);
  tx.commit();

  tx2.get('k', function(err, value){
    t.equal(value, 167, 'tx2 get equals 167');

    tx2.put('k', value+1);

    tx2.commit(function(err){
      db.get('k', function(err, value){
        t.equal(value, 168, 'db get equals 168');
      });
    });
  });

});

<|endoftext|>


<commit_before>var http = require('http');
var url = require('url');
var assert = require('assert');
var localtunnel = require('localtunnel');

var localtunnel_server = require('../server')();

suite('basic');

var lt_server_port

before('set up localtunnel server', function(done) {
    var server = localtunnel_server.listen(function() {
        lt_server_port = server.address().port;
        done();
    });
});

test('landing page', function(done) {
    var opt = {
        host: 'localhost',
        port: lt_server_port,
        headers: {
            host: 'example.com'
        },
        path: '/'
    }

    var req = http.request(opt, function(res) {
        res.setEncoding('utf8');
        var body = '';

        res.on('data', function(chunk) {
            body += chunk;
        });

        res.on('end', function() {
            assert(body.indexOf('<title>Localtunnel ~ Expose yourself to the world</title>') > 0);
            done();
        });
    });

    req.end();
});

before('set up local http server', function(done) {
    var server = http.createServer();
    server.on('request', function(req, res) {
        res.write('foo');
        res.end();
    });
    server.listen(function() {
        var port = server.address().port;

        test._fake_port = port;
        done();
    });
});

before('set up localtunnel client', function(done) {
    var opt = {
        host: 'http://localhost:' + lt_server_port,
    };

    localtunnel(test._fake_port, opt, function(err, tunnel) {
        assert.ifError(err);
        var url = tunnel.url;
        assert.ok(new RegExp('^http:\/\/.*localhost:' + lt_server_port + '$').test(url));
        test._fake_url = url;
        done(err);
    });
});

test('query localtunnel server w/ ident', function(done) {
    var uri = test._fake_url;
    var hostname = url.parse(uri).hostname;

    var opt = {
        host: 'localhost',
        port: lt_server_port,
        headers: {
            host: hostname + '.tld'
        },
        path: '/'
    }

    var req = http.request(opt, function(res) {
        res.setEncoding('utf8');
        var body = '';

        res.on('data', function(chunk) {
            body += chunk;
        });

        res.on('end', function() {
            assert.equal('foo', body);

            // TODO(shtylman) shutdown client
            done();
        });
    });

    req.end();
});

test('request specific domain', function(done) {
    var opt = {
        host: 'http://localhost:' + lt_server_port,
        subdomain: 'abcd'
    };

    localtunnel(test._fake_port, opt, function(err, tunnel) {
        assert.ifError(err);
        var url = tunnel.url;
        assert.ok(new RegExp('^http:\/\/.*localhost:' + lt_server_port + '$').test(url));
        test._fake_url = url;
        done(err);
    });
});

test('request domain that is too long', function(done) {
    var opt = {
        host: 'http://localhost:' + lt_server_port,
        subdomain: 'thisdomainisoutsidethesizeofwhatweallowwhichissixtythreecharacters'
    };

    localtunnel(test._fake_port, opt, function(err, tunnel) {
        assert(err);
        assert.equal(err.message, 'Invalid subdomain. Subdomains must be lowercase and between 4 and 63 alphanumeric characters.');
        done();
    });
});

test('request uppercase domain', function(done) {
    var opt = {
        host: 'http://localhost:' + lt_server_port,
        subdomain: 'ABCD'
    };

    localtunnel(test._fake_port, opt, function(err, tunnel) {
        assert(err);
        assert.equal(err.message, 'Invalid subdomain. Subdomains must be lowercase and between 4 and 63 alphanumeric characters.');
        done();
    });
});

after('shutdown', function() {
    localtunnel_server.close();
});
<commit_msg>test: fix redirect (#53)<commit_after>var http = require('http');
var url = require('url');
var assert = require('assert');
var localtunnel = require('localtunnel');

var localtunnel_server = require('../server')();

suite('basic');

var lt_server_port

before('set up localtunnel server', function(done) {
    var server = localtunnel_server.listen(function() {
        lt_server_port = server.address().port;
        done();
    });
});

test('landing page', function(done) {
    var opt = {
        host: 'localhost',
        port: lt_server_port,
        headers: {
            host: 'example.com'
        },
        path: '/'
    }

    var req = http.request(opt, function(res) {
        res.setEncoding('utf8');
        var body = '';

        res.on('data', function(chunk) {
            body += chunk;
        });

        res.on('end', function() {
            assert(body.indexOf('Redirecting to https://localtunnel.github.io/www/') > 0);
            done();
        });
    });

    req.end();
});

before('set up local http server', function(done) {
    var server = http.createServer();
    server.on('request', function(req, res) {
        res.write('foo');
        res.end();
    });
    server.listen(function() {
        var port = server.address().port;

        test._fake_port = port;
        done();
    });
});

before('set up localtunnel client', function(done) {
    var opt = {
        host: 'http://localhost:' + lt_server_port,
    };

    localtunnel(test._fake_port, opt, function(err, tunnel) {
        assert.ifError(err);
        var url = tunnel.url;
        assert.ok(new RegExp('^http:\/\/.*localhost:' + lt_server_port + '$').test(url));
        test._fake_url = url;
        done(err);
    });
});

test('query localtunnel server w/ ident', function(done) {
    var uri = test._fake_url;
    var hostname = url.parse(uri).hostname;

    var opt = {
        host: 'localhost',
        port: lt_server_port,
        headers: {
            host: hostname + '.tld'
        },
        path: '/'
    }

    var req = http.request(opt, function(res) {
        res.setEncoding('utf8');
        var body = '';

        res.on('data', function(chunk) {
            body += chunk;
        });

        res.on('end', function() {
            assert.equal('foo', body);

            // TODO(shtylman) shutdown client
            done();
        });
    });

    req.end();
});

test('request specific domain', function(done) {
    var opt = {
        host: 'http://localhost:' + lt_server_port,
        subdomain: 'abcd'
    };

    localtunnel(test._fake_port, opt, function(err, tunnel) {
        assert.ifError(err);
        var url = tunnel.url;
        assert.ok(new RegExp('^http:\/\/.*localhost:' + lt_server_port + '$').test(url));
        test._fake_url = url;
        done(err);
    });
});

test('request domain that is too long', function(done) {
    var opt = {
        host: 'http://localhost:' + lt_server_port,
        subdomain: 'thisdomainisoutsidethesizeofwhatweallowwhichissixtythreecharacters'
    };

    localtunnel(test._fake_port, opt, function(err, tunnel) {
        assert(err);
        assert.equal(err.message, 'Invalid subdomain. Subdomains must be lowercase and between 4 and 63 alphanumeric characters.');
        done();
    });
});

test('request uppercase domain', function(done) {
    var opt = {
        host: 'http://localhost:' + lt_server_port,
        subdomain: 'ABCD'
    };

    localtunnel(test._fake_port, opt, function(err, tunnel) {
        assert(err);
        assert.equal(err.message, 'Invalid subdomain. Subdomains must be lowercase and between 4 and 63 alphanumeric characters.');
        done();
    });
});

after('shutdown', function() {
    localtunnel_server.close();
});
<|endoftext|>


<commit_before>/*!
 * Bootstrap v3.3.7 (http://getbootstrap.com)
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */

/*!
 * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=be96633dbab5e6228e035e05931c2c7c)
 * Config saved to config.json and https://gist.github.com/be96633dbab5e6228e035e05931c2c7c
 */
if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(t){"use strict";var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||e[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(t){"use strict";function e(e,i){return this.each(function(){var o=t(this),n=o.data("bs.modal"),r=t.extend({},s.DEFAULTS,o.data(),"object"==typeof e&&e);n||o.data("bs.modal",n=new s(this,r)),"string"==typeof e?n[e](i):r.show&&n.show(i)})}var s=function(e,s){this.options=s,this.$body=t(document.body),this.$element=t(e),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,t.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};s.VERSION="3.3.7",s.TRANSITION_DURATION=300,s.BACKDROP_TRANSITION_DURATION=150,s.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},s.prototype.toggle=function(t){return this.isShown?this.hide():this.show(t)},s.prototype.show=function(e){var i=this,o=t.Event("show.bs.modal",{relatedTarget:e});this.$element.trigger(o),this.isShown||o.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',t.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){i.$element.one("mouseup.dismiss.bs.modal",function(e){t(e.target).is(i.$element)&&(i.ignoreBackdropClick=!0)})}),this.backdrop(function(){var o=t.support.transition&&i.$element.hasClass("fade");i.$element.parent().length||i.$element.appendTo(i.$body),i.$element.show().scrollTop(0),i.adjustDialog(),o&&i.$element[0].offsetWidth,i.$element.addClass("in"),i.enforceFocus();var n=t.Event("shown.bs.modal",{relatedTarget:e});o?i.$dialog.one("bsTransitionEnd",function(){i.$element.trigger("focus").trigger(n)}).emulateTransitionEnd(s.TRANSITION_DURATION):i.$element.trigger("focus").trigger(n)}))},s.prototype.hide=function(e){e&&e.preventDefault(),e=t.Event("hide.bs.modal"),this.$element.trigger(e),this.isShown&&!e.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),t(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),t.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",t.proxy(this.hideModal,this)).emulateTransitionEnd(s.TRANSITION_DURATION):this.hideModal())},s.prototype.enforceFocus=function(){t(document).off("focusin.bs.modal").on("focusin.bs.modal",t.proxy(function(t){document===t.target||this.$element[0]===t.target||this.$element.has(t.target).length||this.$element.trigger("focus")},this))},s.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",t.proxy(function(t){27==t.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},s.prototype.resize=function(){this.isShown?t(window).on("resize.bs.modal",t.proxy(this.handleUpdate,this)):t(window).off("resize.bs.modal")},s.prototype.hideModal=function(){var t=this;this.$element.hide(),this.backdrop(function(){t.$body.removeClass("modal-open"),t.resetAdjustments(),t.resetScrollbar(),t.$element.trigger("hidden.bs.modal")})},s.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},s.prototype.backdrop=function(e){var i=this,o=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var n=t.support.transition&&o;if(this.$backdrop=t(document.createElement("div")).addClass("modal-backdrop "+o).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",t.proxy(function(t){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(t.target===t.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),n&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!e)return;n?this.$backdrop.one("bsTransitionEnd",e).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):e()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var r=function(){i.removeBackdrop(),e&&e()};t.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",r).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):r()}else e&&e()},s.prototype.handleUpdate=function(){this.adjustDialog()},s.prototype.adjustDialog=function(){var t=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},s.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},s.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth<t,this.scrollbarWidth=this.measureScrollbar()},s.prototype.setScrollbar=function(){var t=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",t+this.scrollbarWidth)},s.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},s.prototype.measureScrollbar=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",this.$body.append(t);var e=t.offsetWidth-t.clientWidth;return this.$body[0].removeChild(t),e};var i=t.fn.modal;t.fn.modal=e,t.fn.modal.Constructor=s,t.fn.modal.noConflict=function(){return t.fn.modal=i,this},t(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(s){var i=t(this),o=i.attr("href"),n=t(i.attr("data-target")||o&&o.replace(/.*(?=#[^\s]+$)/,"")),r=n.data("bs.modal")?"toggle":t.extend({remote:!/#/.test(o)&&o},n.data(),i.data());i.is("a")&&s.preventDefault(),n.one("show.bs.modal",function(t){t.isDefaultPrevented()||n.one("hidden.bs.modal",function(){i.is(":visible")&&i.trigger("focus")})}),e.call(n,r,this)})}(jQuery),+function(t){"use strict";function e(s,i){this.$body=t(document.body),this.$scrollElement=t(t(s).is(document.body)?window:s),this.options=t.extend({},e.DEFAULTS,i),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",t.proxy(this.process,this)),this.refresh(),this.process()}function s(s){return this.each(function(){var i=t(this),o=i.data("bs.scrollspy"),n="object"==typeof s&&s;o||i.data("bs.scrollspy",o=new e(this,n)),"string"==typeof s&&o[s]()})}e.VERSION="3.3.7",e.DEFAULTS={offset:10},e.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},e.prototype.refresh=function(){var e=this,s="offset",i=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),t.isWindow(this.$scrollElement[0])||(s="position",i=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var e=t(this),o=e.data("target")||e.attr("href"),n=/^#./.test(o)&&t(o);return n&&n.length&&n.is(":visible")&&[[n[s]().top+i,o]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){e.offsets.push(this[0]),e.targets.push(this[1])})},e.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,s=this.getScrollHeight(),i=this.options.offset+s-this.$scrollElement.height(),o=this.offsets,n=this.targets,r=this.activeTarget;if(this.scrollHeight!=s&&this.refresh(),e>=i)return r!=(t=n[n.length-1])&&this.activate(t);if(r&&e<o[0])return this.activeTarget=null,this.clear();for(t=o.length;t--;)r!=n[t]&&e>=o[t]&&(void 0===o[t+1]||e<o[t+1])&&this.activate(n[t])},e.prototype.activate=function(e){this.activeTarget=e,this.clear();var s=this.selector+'[data-target="'+e+'"],'+this.selector+'[href="'+e+'"]',i=t(s).parents("li").addClass("active");i.parent(".dropdown-menu").length&&(i=i.closest("li.dropdown").addClass("active")),i.trigger("activate.bs.scrollspy")},e.prototype.clear=function(){t(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var i=t.fn.scrollspy;t.fn.scrollspy=s,t.fn.scrollspy.Constructor=e,t.fn.scrollspy.noConflict=function(){return t.fn.scrollspy=i,this},t(window).on("load.bs.scrollspy.data-api",function(){t('[data-spy="scroll"]').each(function(){var e=t(this);s.call(e,e.data())})})}(jQuery),+function(t){"use strict";function e(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var s in e)if(void 0!==t.style[s])return{end:e[s]};return!1}t.fn.emulateTransitionEnd=function(e){var s=!1,i=this;t(this).one("bsTransitionEnd",function(){s=!0});var o=function(){s||t(i).trigger(t.support.transition.end)};return setTimeout(o,e),this},t(function(){t.support.transition=e(),t.support.transition&&(t.event.special.bsTransitionEnd={bindType:t.support.transition.end,delegateType:t.support.transition.end,handle:function(e){return t(e.target).is(this)?e.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery);<commit_msg>More customization<commit_after>/*!
 * Bootstrap v3.3.7 (http://getbootstrap.com)
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */

/*!
 * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=5b68a75c6545fada456663bfe497b60e)
 * Config saved to config.json and https://gist.github.com/5b68a75c6545fada456663bfe497b60e
 */
if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(t){"use strict";var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||e[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(t){"use strict";function e(e,i){return this.each(function(){var o=t(this),n=o.data("bs.modal"),r=t.extend({},s.DEFAULTS,o.data(),"object"==typeof e&&e);n||o.data("bs.modal",n=new s(this,r)),"string"==typeof e?n[e](i):r.show&&n.show(i)})}var s=function(e,s){this.options=s,this.$body=t(document.body),this.$element=t(e),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,t.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};s.VERSION="3.3.7",s.TRANSITION_DURATION=300,s.BACKDROP_TRANSITION_DURATION=150,s.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},s.prototype.toggle=function(t){return this.isShown?this.hide():this.show(t)},s.prototype.show=function(e){var i=this,o=t.Event("show.bs.modal",{relatedTarget:e});this.$element.trigger(o),this.isShown||o.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',t.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){i.$element.one("mouseup.dismiss.bs.modal",function(e){t(e.target).is(i.$element)&&(i.ignoreBackdropClick=!0)})}),this.backdrop(function(){var o=t.support.transition&&i.$element.hasClass("fade");i.$element.parent().length||i.$element.appendTo(i.$body),i.$element.show().scrollTop(0),i.adjustDialog(),o&&i.$element[0].offsetWidth,i.$element.addClass("in"),i.enforceFocus();var n=t.Event("shown.bs.modal",{relatedTarget:e});o?i.$dialog.one("bsTransitionEnd",function(){i.$element.trigger("focus").trigger(n)}).emulateTransitionEnd(s.TRANSITION_DURATION):i.$element.trigger("focus").trigger(n)}))},s.prototype.hide=function(e){e&&e.preventDefault(),e=t.Event("hide.bs.modal"),this.$element.trigger(e),this.isShown&&!e.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),t(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),t.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",t.proxy(this.hideModal,this)).emulateTransitionEnd(s.TRANSITION_DURATION):this.hideModal())},s.prototype.enforceFocus=function(){t(document).off("focusin.bs.modal").on("focusin.bs.modal",t.proxy(function(t){document===t.target||this.$element[0]===t.target||this.$element.has(t.target).length||this.$element.trigger("focus")},this))},s.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",t.proxy(function(t){27==t.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},s.prototype.resize=function(){this.isShown?t(window).on("resize.bs.modal",t.proxy(this.handleUpdate,this)):t(window).off("resize.bs.modal")},s.prototype.hideModal=function(){var t=this;this.$element.hide(),this.backdrop(function(){t.$body.removeClass("modal-open"),t.resetAdjustments(),t.resetScrollbar(),t.$element.trigger("hidden.bs.modal")})},s.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},s.prototype.backdrop=function(e){var i=this,o=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var n=t.support.transition&&o;if(this.$backdrop=t(document.createElement("div")).addClass("modal-backdrop "+o).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",t.proxy(function(t){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(t.target===t.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),n&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!e)return;n?this.$backdrop.one("bsTransitionEnd",e).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):e()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var r=function(){i.removeBackdrop(),e&&e()};t.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",r).emulateTransitionEnd(s.BACKDROP_TRANSITION_DURATION):r()}else e&&e()},s.prototype.handleUpdate=function(){this.adjustDialog()},s.prototype.adjustDialog=function(){var t=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},s.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},s.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth<t,this.scrollbarWidth=this.measureScrollbar()},s.prototype.setScrollbar=function(){var t=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",t+this.scrollbarWidth)},s.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},s.prototype.measureScrollbar=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",this.$body.append(t);var e=t.offsetWidth-t.clientWidth;return this.$body[0].removeChild(t),e};var i=t.fn.modal;t.fn.modal=e,t.fn.modal.Constructor=s,t.fn.modal.noConflict=function(){return t.fn.modal=i,this},t(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(s){var i=t(this),o=i.attr("href"),n=t(i.attr("data-target")||o&&o.replace(/.*(?=#[^\s]+$)/,"")),r=n.data("bs.modal")?"toggle":t.extend({remote:!/#/.test(o)&&o},n.data(),i.data());i.is("a")&&s.preventDefault(),n.one("show.bs.modal",function(t){t.isDefaultPrevented()||n.one("hidden.bs.modal",function(){i.is(":visible")&&i.trigger("focus")})}),e.call(n,r,this)})}(jQuery),+function(t){"use strict";function e(e){var s,i=e.attr("data-target")||(s=e.attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"");return t(i)}function s(e){return this.each(function(){var s=t(this),o=s.data("bs.collapse"),n=t.extend({},i.DEFAULTS,s.data(),"object"==typeof e&&e);!o&&n.toggle&&/show|hide/.test(e)&&(n.toggle=!1),o||s.data("bs.collapse",o=new i(this,n)),"string"==typeof e&&o[e]()})}var i=function(e,s){this.$element=t(e),this.options=t.extend({},i.DEFAULTS,s),this.$trigger=t('[data-toggle="collapse"][href="#'+e.id+'"],[data-toggle="collapse"][data-target="#'+e.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};i.VERSION="3.3.7",i.TRANSITION_DURATION=350,i.DEFAULTS={toggle:!0},i.prototype.dimension=function(){var t=this.$element.hasClass("width");return t?"width":"height"},i.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var e,o=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(o&&o.length&&(e=o.data("bs.collapse"),e&&e.transitioning))){var n=t.Event("show.bs.collapse");if(this.$element.trigger(n),!n.isDefaultPrevented()){o&&o.length&&(s.call(o,"hide"),e||o.data("bs.collapse",null));var r=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[r](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var a=function(){this.$element.removeClass("collapsing").addClass("collapse in")[r](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!t.support.transition)return a.call(this);var l=t.camelCase(["scroll",r].join("-"));this.$element.one("bsTransitionEnd",t.proxy(a,this)).emulateTransitionEnd(i.TRANSITION_DURATION)[r](this.$element[0][l])}}}},i.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var e=t.Event("hide.bs.collapse");if(this.$element.trigger(e),!e.isDefaultPrevented()){var s=this.dimension();this.$element[s](this.$element[s]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var o=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return t.support.transition?void this.$element[s](0).one("bsTransitionEnd",t.proxy(o,this)).emulateTransitionEnd(i.TRANSITION_DURATION):o.call(this)}}},i.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},i.prototype.getParent=function(){return t(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(t.proxy(function(s,i){var o=t(i);this.addAriaAndCollapsedClass(e(o),o)},this)).end()},i.prototype.addAriaAndCollapsedClass=function(t,e){var s=t.hasClass("in");t.attr("aria-expanded",s),e.toggleClass("collapsed",!s).attr("aria-expanded",s)};var o=t.fn.collapse;t.fn.collapse=s,t.fn.collapse.Constructor=i,t.fn.collapse.noConflict=function(){return t.fn.collapse=o,this},t(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(i){var o=t(this);o.attr("data-target")||i.preventDefault();var n=e(o),r=n.data("bs.collapse"),a=r?"toggle":o.data();s.call(n,a)})}(jQuery),+function(t){"use strict";function e(s,i){this.$body=t(document.body),this.$scrollElement=t(t(s).is(document.body)?window:s),this.options=t.extend({},e.DEFAULTS,i),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",t.proxy(this.process,this)),this.refresh(),this.process()}function s(s){return this.each(function(){var i=t(this),o=i.data("bs.scrollspy"),n="object"==typeof s&&s;o||i.data("bs.scrollspy",o=new e(this,n)),"string"==typeof s&&o[s]()})}e.VERSION="3.3.7",e.DEFAULTS={offset:10},e.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},e.prototype.refresh=function(){var e=this,s="offset",i=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),t.isWindow(this.$scrollElement[0])||(s="position",i=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var e=t(this),o=e.data("target")||e.attr("href"),n=/^#./.test(o)&&t(o);return n&&n.length&&n.is(":visible")&&[[n[s]().top+i,o]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){e.offsets.push(this[0]),e.targets.push(this[1])})},e.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,s=this.getScrollHeight(),i=this.options.offset+s-this.$scrollElement.height(),o=this.offsets,n=this.targets,r=this.activeTarget;if(this.scrollHeight!=s&&this.refresh(),e>=i)return r!=(t=n[n.length-1])&&this.activate(t);if(r&&e<o[0])return this.activeTarget=null,this.clear();for(t=o.length;t--;)r!=n[t]&&e>=o[t]&&(void 0===o[t+1]||e<o[t+1])&&this.activate(n[t])},e.prototype.activate=function(e){this.activeTarget=e,this.clear();var s=this.selector+'[data-target="'+e+'"],'+this.selector+'[href="'+e+'"]',i=t(s).parents("li").addClass("active");i.parent(".dropdown-menu").length&&(i=i.closest("li.dropdown").addClass("active")),i.trigger("activate.bs.scrollspy")},e.prototype.clear=function(){t(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var i=t.fn.scrollspy;t.fn.scrollspy=s,t.fn.scrollspy.Constructor=e,t.fn.scrollspy.noConflict=function(){return t.fn.scrollspy=i,this},t(window).on("load.bs.scrollspy.data-api",function(){t('[data-spy="scroll"]').each(function(){var e=t(this);s.call(e,e.data())})})}(jQuery),+function(t){"use strict";function e(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var s in e)if(void 0!==t.style[s])return{end:e[s]};return!1}t.fn.emulateTransitionEnd=function(e){var s=!1,i=this;t(this).one("bsTransitionEnd",function(){s=!0});var o=function(){s||t(i).trigger(t.support.transition.end)};return setTimeout(o,e),this},t(function(){t.support.transition=e(),t.support.transition&&(t.event.special.bsTransitionEnd={bindType:t.support.transition.end,delegateType:t.support.transition.end,handle:function(e){return t(e.target).is(this)?e.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery);<|endoftext|>


<commit_before>'use strict'

var Buffer = require('safe-buffer').Buffer
var test = require('tape').test
var helper = require('./helper')
var aedes = require('../')
var eos = require('end-of-stream')
var setup = helper.setup
var connect = helper.connect
var noError = helper.noError
var subscribe = helper.subscribe

test('connect and connack (minimal)', function (t) {
  var s = setup()

  s.inStream.write({
    cmd: 'connect',
    protocolId: 'MQTT',
    protocolVersion: 4,
    clean: true,
    clientId: 'my-client',
    keepalive: 0
  })

  s.outStream.on('data', function (packet) {
    t.deepEqual(packet, {
      cmd: 'connack',
      returnCode: 0,
      length: 2,
      qos: 0,
      retain: false,
      dup: false,
      topic: null,
      payload: null,
      sessionPresent: false
    }, 'successful connack')
    t.end()
  })
})

test('publish QoS 0', function (t) {
  var s = connect(setup())
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    qos: 0,
    retain: false,
    messageId: 0
  }

  s.broker.mq.on('hello', function (packet, cb) {
    expected.brokerId = s.broker.id
    expected.brokerCounter = s.broker.counter
    t.deepEqual(packet, expected, 'packet matches')
    cb()
    t.end()
  })

  s.inStream.write({
    cmd: 'publish',
    topic: 'hello',
    payload: 'world'
  })
})

test('subscribe QoS 0', function (t) {
  var s = connect(setup())
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    dup: false,
    length: 12,
    qos: 0,
    retain: false
  }

  subscribe(t, s, 'hello', 0, function () {
    s.outStream.once('data', function (packet) {
      t.deepEqual(packet, expected, 'packet matches')
      t.end()
    })

    s.broker.publish({
      cmd: 'publish',
      topic: 'hello',
      payload: 'world'
    })
  })
})

test('does not die badly on connection error', function (t) {
  t.plan(3)
  var s = connect(setup())

  s.inStream.write({
    cmd: 'subscribe',
    messageId: 42,
    subscriptions: [{
      topic: 'hello',
      qos: 0
    }]
  })

  s.broker.on('clientError', function (client, err) {
    t.ok(client, 'client is passed')
    t.ok(err, 'err is passed')
  })

  s.outStream.on('data', function (packet) {
    s.conn._writableState.ended = true
    s.broker.publish({
      cmd: 'publish',
      topic: 'hello',
      payload: Buffer.from('world')
    }, function () {
      t.pass('calls the callback')
    })
  })
})

test('unsubscribe', function (t) {
  t.plan(5)

  var s = noError(connect(setup()), t)

  subscribe(t, s, 'hello', 0, function () {
    s.inStream.write({
      cmd: 'unsubscribe',
      messageId: 43,
      unsubscriptions: ['hello']
    })

    s.outStream.once('data', function (packet) {
      t.deepEqual(packet, {
        cmd: 'unsuback',
        messageId: 43,
        dup: false,
        length: 2,
        qos: 0,
        retain: false
      }, 'packet matches')

      s.outStream.on('data', function (packet) {
        t.fail('packet received')
      })

      s.broker.publish({
        cmd: 'publish',
        topic: 'hello',
        payload: 'world'
      }, function () {
        t.pass('publish finished')
      })
    })
  })
})

test('unsubscribe without subscribe', function (t) {
  t.plan(1)

  var s = noError(connect(setup()), t)

  s.inStream.write({
    cmd: 'unsubscribe',
    messageId: 43,
    unsubscriptions: ['hello']
  })

  s.outStream.once('data', function (packet) {
    t.deepEqual(packet, {
      cmd: 'unsuback',
      messageId: 43,
      dup: false,
      length: 2,
      qos: 0,
      retain: false
    }, 'packet matches')
  })
})

test('unsubscribe on disconnect', function (t) {
  var s = noError(connect(setup()), t)

  subscribe(t, s, 'hello', 0, function () {
    s.conn.emit('close')
    s.outStream.on('data', function () {
      t.fail('should not receive any more messages')
    })
    s.broker.publish({
      cmd: 'publish',
      topic: 'hello',
      payload: Buffer.from('world')
    }, function () {
      t.pass('calls the callback')
      t.end()
    })
  })
})

test('disconnect', function (t) {
  var s = noError(connect(setup()), t)

  s.outStream.on('finish', function () {
    t.end()
  })

  s.inStream.write({
    cmd: 'disconnect'
  })
})

test('retain messages', function (t) {
  var broker = aedes()
  var publisher = connect(setup(broker))
  var subscriber = connect(setup(broker))
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    qos: 0,
    dup: false,
    length: 12,
    retain: true
  }

  broker.subscribe('hello', function (packet, cb) {
    cb()

    // defer this or it will receive the message which
    // is being published
    setImmediate(function () {
      subscribe(t, subscriber, 'hello', 0, function () {
        subscriber.outStream.once('data', function (packet) {
          t.deepEqual(packet, expected, 'packet must match')
          t.end()
        })
      })
    })
  })

  publisher.inStream.write(expected)
})

test('closes', function (t) {
  t.plan(2)

  var broker = aedes()
  var client = noError(connect(setup(broker)))
  eos(client.conn, t.pass.bind('client closes'))

  broker.close(function (err) {
    t.error(err, 'no error')
  })
})

test('connect without a clientId for MQTT 3.1.1', function (t) {
  var s = setup()

  s.inStream.write({
    cmd: 'connect',
    protocolId: 'MQTT',
    protocolVersion: 4,
    clean: true,
    keepalive: 0
  })

  s.outStream.on('data', function (packet) {
    t.deepEqual(packet, {
      cmd: 'connack',
      returnCode: 0,
      length: 2,
      qos: 0,
      retain: false,
      dup: false,
      topic: null,
      payload: null,
      sessionPresent: false
    }, 'successful connack')

    t.end()
  })
})

test('disconnect another client with the same clientId', function (t) {
  t.plan(2)

  var broker = aedes()
  var c1 = connect(setup(broker), {
    clientId: 'abcde'
  }, function () {
    eos(c1.conn, function () {
      t.pass('first client disconnected')
    })

    connect(setup(broker), {
      clientId: 'abcde'
    }, function () {
      t.pass('second client connected')

      // setImmediate needed because eos
      // will happen at the next tick
      setImmediate(t.end.bind(t))
    })
  })
})

test('disconnect if another broker connects the same client', function (t) {
  t.plan(2)

  var broker = aedes()
  var c1 = connect(setup(broker), {
    clientId: 'abcde'
  }, function () {
    eos(c1.conn, function () {
      t.pass('first client disconnected')
    })

    broker.publish({
      topic: '$SYS/anotherBroker/new/clients',
      payload: Buffer.from('abcde')
    }, function () {
      t.pass('published')

      // setImmediate needed because eos
      // will happen at the next tick
      setImmediate(t.end.bind(t))
    })
  })
})

test('publish to $SYS/broker/new/clients', function (t) {
  t.plan(1)

  var broker = aedes()

  broker.mq.on('$SYS/' + broker.id + '/new/clients', function (packet, done) {
    t.equal(packet.payload.toString(), 'abcde', 'clientId matches')
    done()
  })

  connect(setup(broker), {
    clientId: 'abcde'
  })
})

test('restore QoS 0 subscriptions not clean', function (t) {
  var broker = aedes()
  var publisher
  var subscriber = connect(setup(broker), { clean: false, clientId: 'abcde' })
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    qos: 0,
    dup: false,
    length: 12,
    retain: false
  }

  subscribe(t, subscriber, 'hello', 0, function () {
    subscriber.inStream.end()

    publisher = connect(setup(broker))

    subscriber = connect(setup(broker), { clean: false, clientId: 'abcde' }, function (connect) {
      t.equal(connect.sessionPresent, true, 'session present is set to true')
      publisher.inStream.write({
        cmd: 'publish',
        topic: 'hello',
        payload: 'world',
        qos: 0
      })
    })

    subscriber.outStream.once('data', function (packet) {
      t.deepEqual(packet, expected, 'packet must match')
      t.end()
    })
  })
})

test('double sub does not double deliver', function (t) {
  var s = connect(setup())
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    dup: false,
    length: 12,
    qos: 0,
    retain: false
  }

  subscribe(t, s, 'hello', 0, function () {
    subscribe(t, s, 'hello', 0, function () {
      s.outStream.once('data', function (packet) {
        t.deepEqual(packet, expected, 'packet matches')
        s.outStream.on('data', function () {
          t.fail('double deliver')
        })
        // wait for a tick, so it will double deliver
        setImmediate(t.end.bind(t))
      })

      s.broker.publish({
        cmd: 'publish',
        topic: 'hello',
        payload: 'world'
      })
    })
  })
})

test('overlapping sub does not double deliver', function (t) {
  var s = connect(setup())
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    dup: false,
    length: 12,
    qos: 0,
    retain: false
  }

  subscribe(t, s, 'hello', 0, function () {
    subscribe(t, s, 'hello/#', 0, function () {
      s.outStream.once('data', function (packet) {
        t.deepEqual(packet, expected, 'packet matches')
        s.outStream.on('data', function () {
          t.fail('double deliver')
        })
        // wait for a tick, so it will double deliver
        setImmediate(t.end.bind(t))
      })

      s.broker.publish({
        cmd: 'publish',
        topic: 'hello',
        payload: 'world'
      })
    })
  })
})

test('avoid wrong deduping of retain messages', function (t) {
  var broker = aedes()
  var publisher = connect(setup(broker))
  var subscriber = connect(setup(broker))
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    qos: 0,
    dup: false,
    length: 12,
    retain: true
  }

  broker.subscribe('hello', function (packet, cb) {
    cb()
    // subscribe and publish another topic
    subscribe(t, subscriber, 'hello2', 0, function () {
      cb()

      publisher.inStream.write({
        cmd: 'publish',
        topic: 'hello2',
        payload: Buffer.from('world'),
        qos: 0,
        dup: false
      })

      subscriber.outStream.once('data', function (packet) {
        subscribe(t, subscriber, 'hello', 0, function () {
          subscriber.outStream.once('data', function (packet) {
            t.deepEqual(packet, expected, 'packet must match')
            t.end()
          })
        })
      })
    })
  })

  publisher.inStream.write(expected)
})

test('publish invalid topic with #', function (t) {
  var s = connect(setup())

  subscribe(t, s, '#', 0, function () {
    s.outStream.once('data', function (packet) {
      t.fail('no packet')
      t.end()
    })

    s.inStream.write({
      cmd: 'publish',
      topic: 'hello/#',
      payload: 'world'
    })
  })

  eos(s.conn, function () {
    t.equal(s.broker.connectedClients, 0, 'no connected clients')
    t.end()
  })
})

test('publish invalid topic with +', function (t) {
  var s = connect(setup())

  subscribe(t, s, '#', 0, function () {
    s.outStream.once('data', function (packet) {
      t.fail('no packet')
    })

    s.inStream.write({
      cmd: 'publish',
      topic: 'hello/+/eee',
      payload: 'world'
    })
  })

  eos(s.conn, function () {
    t.equal(s.broker.connectedClients, 0, 'no connected clients')
    t.end()
  })
})

;['base/#/sub', 'base/#sub', 'base/+xyz/sub'].forEach(function (topic) {
  test('subscribe to invalid topic with ' + topic, function (t) {
    var s = connect(setup())

    s.inStream.write({
      cmd: 'subscribe',
      messageId: 24,
      subscriptions: [{
        topic: topic,
        qos: 0
      }]
    })

    eos(s.conn, function () {
      t.equal(s.broker.connectedClients, 0, 'no connected clients')
      t.end()
    })
  })
})
<commit_msg>Added test for empty topic<commit_after>'use strict'

var Buffer = require('safe-buffer').Buffer
var test = require('tape').test
var helper = require('./helper')
var aedes = require('../')
var eos = require('end-of-stream')
var setup = helper.setup
var connect = helper.connect
var noError = helper.noError
var subscribe = helper.subscribe

test('connect and connack (minimal)', function (t) {
  var s = setup()

  s.inStream.write({
    cmd: 'connect',
    protocolId: 'MQTT',
    protocolVersion: 4,
    clean: true,
    clientId: 'my-client',
    keepalive: 0
  })

  s.outStream.on('data', function (packet) {
    t.deepEqual(packet, {
      cmd: 'connack',
      returnCode: 0,
      length: 2,
      qos: 0,
      retain: false,
      dup: false,
      topic: null,
      payload: null,
      sessionPresent: false
    }, 'successful connack')
    t.end()
  })
})

test('publish QoS 0', function (t) {
  var s = connect(setup())
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    qos: 0,
    retain: false,
    messageId: 0
  }

  s.broker.mq.on('hello', function (packet, cb) {
    expected.brokerId = s.broker.id
    expected.brokerCounter = s.broker.counter
    t.deepEqual(packet, expected, 'packet matches')
    cb()
    t.end()
  })

  s.inStream.write({
    cmd: 'publish',
    topic: 'hello',
    payload: 'world'
  })
})

test('subscribe QoS 0', function (t) {
  var s = connect(setup())
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    dup: false,
    length: 12,
    qos: 0,
    retain: false
  }

  subscribe(t, s, 'hello', 0, function () {
    s.outStream.once('data', function (packet) {
      t.deepEqual(packet, expected, 'packet matches')
      t.end()
    })

    s.broker.publish({
      cmd: 'publish',
      topic: 'hello',
      payload: 'world'
    })
  })
})

test('does not die badly on connection error', function (t) {
  t.plan(3)
  var s = connect(setup())

  s.inStream.write({
    cmd: 'subscribe',
    messageId: 42,
    subscriptions: [{
      topic: 'hello',
      qos: 0
    }]
  })

  s.broker.on('clientError', function (client, err) {
    t.ok(client, 'client is passed')
    t.ok(err, 'err is passed')
  })

  s.outStream.on('data', function (packet) {
    s.conn._writableState.ended = true
    s.broker.publish({
      cmd: 'publish',
      topic: 'hello',
      payload: Buffer.from('world')
    }, function () {
      t.pass('calls the callback')
    })
  })
})

test('unsubscribe', function (t) {
  t.plan(5)

  var s = noError(connect(setup()), t)

  subscribe(t, s, 'hello', 0, function () {
    s.inStream.write({
      cmd: 'unsubscribe',
      messageId: 43,
      unsubscriptions: ['hello']
    })

    s.outStream.once('data', function (packet) {
      t.deepEqual(packet, {
        cmd: 'unsuback',
        messageId: 43,
        dup: false,
        length: 2,
        qos: 0,
        retain: false
      }, 'packet matches')

      s.outStream.on('data', function (packet) {
        t.fail('packet received')
      })

      s.broker.publish({
        cmd: 'publish',
        topic: 'hello',
        payload: 'world'
      }, function () {
        t.pass('publish finished')
      })
    })
  })
})

test('unsubscribe without subscribe', function (t) {
  t.plan(1)

  var s = noError(connect(setup()), t)

  s.inStream.write({
    cmd: 'unsubscribe',
    messageId: 43,
    unsubscriptions: ['hello']
  })

  s.outStream.once('data', function (packet) {
    t.deepEqual(packet, {
      cmd: 'unsuback',
      messageId: 43,
      dup: false,
      length: 2,
      qos: 0,
      retain: false
    }, 'packet matches')
  })
})

test('unsubscribe on disconnect', function (t) {
  var s = noError(connect(setup()), t)

  subscribe(t, s, 'hello', 0, function () {
    s.conn.emit('close')
    s.outStream.on('data', function () {
      t.fail('should not receive any more messages')
    })
    s.broker.publish({
      cmd: 'publish',
      topic: 'hello',
      payload: Buffer.from('world')
    }, function () {
      t.pass('calls the callback')
      t.end()
    })
  })
})

test('disconnect', function (t) {
  var s = noError(connect(setup()), t)

  s.outStream.on('finish', function () {
    t.end()
  })

  s.inStream.write({
    cmd: 'disconnect'
  })
})

test('retain messages', function (t) {
  var broker = aedes()
  var publisher = connect(setup(broker))
  var subscriber = connect(setup(broker))
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    qos: 0,
    dup: false,
    length: 12,
    retain: true
  }

  broker.subscribe('hello', function (packet, cb) {
    cb()

    // defer this or it will receive the message which
    // is being published
    setImmediate(function () {
      subscribe(t, subscriber, 'hello', 0, function () {
        subscriber.outStream.once('data', function (packet) {
          t.deepEqual(packet, expected, 'packet must match')
          t.end()
        })
      })
    })
  })

  publisher.inStream.write(expected)
})

test('closes', function (t) {
  t.plan(2)

  var broker = aedes()
  var client = noError(connect(setup(broker)))
  eos(client.conn, t.pass.bind('client closes'))

  broker.close(function (err) {
    t.error(err, 'no error')
  })
})

test('connect without a clientId for MQTT 3.1.1', function (t) {
  var s = setup()

  s.inStream.write({
    cmd: 'connect',
    protocolId: 'MQTT',
    protocolVersion: 4,
    clean: true,
    keepalive: 0
  })

  s.outStream.on('data', function (packet) {
    t.deepEqual(packet, {
      cmd: 'connack',
      returnCode: 0,
      length: 2,
      qos: 0,
      retain: false,
      dup: false,
      topic: null,
      payload: null,
      sessionPresent: false
    }, 'successful connack')

    t.end()
  })
})

test('disconnect another client with the same clientId', function (t) {
  t.plan(2)

  var broker = aedes()
  var c1 = connect(setup(broker), {
    clientId: 'abcde'
  }, function () {
    eos(c1.conn, function () {
      t.pass('first client disconnected')
    })

    connect(setup(broker), {
      clientId: 'abcde'
    }, function () {
      t.pass('second client connected')

      // setImmediate needed because eos
      // will happen at the next tick
      setImmediate(t.end.bind(t))
    })
  })
})

test('disconnect if another broker connects the same client', function (t) {
  t.plan(2)

  var broker = aedes()
  var c1 = connect(setup(broker), {
    clientId: 'abcde'
  }, function () {
    eos(c1.conn, function () {
      t.pass('first client disconnected')
    })

    broker.publish({
      topic: '$SYS/anotherBroker/new/clients',
      payload: Buffer.from('abcde')
    }, function () {
      t.pass('published')

      // setImmediate needed because eos
      // will happen at the next tick
      setImmediate(t.end.bind(t))
    })
  })
})

test('publish to $SYS/broker/new/clients', function (t) {
  t.plan(1)

  var broker = aedes()

  broker.mq.on('$SYS/' + broker.id + '/new/clients', function (packet, done) {
    t.equal(packet.payload.toString(), 'abcde', 'clientId matches')
    done()
  })

  connect(setup(broker), {
    clientId: 'abcde'
  })
})

test('restore QoS 0 subscriptions not clean', function (t) {
  var broker = aedes()
  var publisher
  var subscriber = connect(setup(broker), { clean: false, clientId: 'abcde' })
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    qos: 0,
    dup: false,
    length: 12,
    retain: false
  }

  subscribe(t, subscriber, 'hello', 0, function () {
    subscriber.inStream.end()

    publisher = connect(setup(broker))

    subscriber = connect(setup(broker), { clean: false, clientId: 'abcde' }, function (connect) {
      t.equal(connect.sessionPresent, true, 'session present is set to true')
      publisher.inStream.write({
        cmd: 'publish',
        topic: 'hello',
        payload: 'world',
        qos: 0
      })
    })

    subscriber.outStream.once('data', function (packet) {
      t.deepEqual(packet, expected, 'packet must match')
      t.end()
    })
  })
})

test('double sub does not double deliver', function (t) {
  var s = connect(setup())
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    dup: false,
    length: 12,
    qos: 0,
    retain: false
  }

  subscribe(t, s, 'hello', 0, function () {
    subscribe(t, s, 'hello', 0, function () {
      s.outStream.once('data', function (packet) {
        t.deepEqual(packet, expected, 'packet matches')
        s.outStream.on('data', function () {
          t.fail('double deliver')
        })
        // wait for a tick, so it will double deliver
        setImmediate(t.end.bind(t))
      })

      s.broker.publish({
        cmd: 'publish',
        topic: 'hello',
        payload: 'world'
      })
    })
  })
})

test('overlapping sub does not double deliver', function (t) {
  var s = connect(setup())
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    dup: false,
    length: 12,
    qos: 0,
    retain: false
  }

  subscribe(t, s, 'hello', 0, function () {
    subscribe(t, s, 'hello/#', 0, function () {
      s.outStream.once('data', function (packet) {
        t.deepEqual(packet, expected, 'packet matches')
        s.outStream.on('data', function () {
          t.fail('double deliver')
        })
        // wait for a tick, so it will double deliver
        setImmediate(t.end.bind(t))
      })

      s.broker.publish({
        cmd: 'publish',
        topic: 'hello',
        payload: 'world'
      })
    })
  })
})

test('avoid wrong deduping of retain messages', function (t) {
  var broker = aedes()
  var publisher = connect(setup(broker))
  var subscriber = connect(setup(broker))
  var expected = {
    cmd: 'publish',
    topic: 'hello',
    payload: Buffer.from('world'),
    qos: 0,
    dup: false,
    length: 12,
    retain: true
  }

  broker.subscribe('hello', function (packet, cb) {
    cb()
    // subscribe and publish another topic
    subscribe(t, subscriber, 'hello2', 0, function () {
      cb()

      publisher.inStream.write({
        cmd: 'publish',
        topic: 'hello2',
        payload: Buffer.from('world'),
        qos: 0,
        dup: false
      })

      subscriber.outStream.once('data', function (packet) {
        subscribe(t, subscriber, 'hello', 0, function () {
          subscriber.outStream.once('data', function (packet) {
            t.deepEqual(packet, expected, 'packet must match')
            t.end()
          })
        })
      })
    })
  })

  publisher.inStream.write(expected)
})

test('publish empty topic', function (t) {
  var s = connect(setup())

  subscribe(t, s, '#', 0, function () {
    s.outStream.once('data', function (packet) {
      t.fail('no packet')
      t.end()
    })

    s.inStream.write({
      cmd: 'publish',
      topic: '',
      payload: 'world'
    })
  })

  eos(s.conn, function () {
    t.equal(s.broker.connectedClients, 0, 'no connected clients')
    t.end()
  })
})

test('publish invalid topic with #', function (t) {
  var s = connect(setup())

  subscribe(t, s, '#', 0, function () {
    s.outStream.once('data', function (packet) {
      t.fail('no packet')
      t.end()
    })

    s.inStream.write({
      cmd: 'publish',
      topic: 'hello/#',
      payload: 'world'
    })
  })

  eos(s.conn, function () {
    t.equal(s.broker.connectedClients, 0, 'no connected clients')
    t.end()
  })
})

test('publish invalid topic with +', function (t) {
  var s = connect(setup())

  subscribe(t, s, '#', 0, function () {
    s.outStream.once('data', function (packet) {
      t.fail('no packet')
    })

    s.inStream.write({
      cmd: 'publish',
      topic: 'hello/+/eee',
      payload: 'world'
    })
  })

  eos(s.conn, function () {
    t.equal(s.broker.connectedClients, 0, 'no connected clients')
    t.end()
  })
})

;['base/#/sub', 'base/#sub', 'base/+xyz/sub'].forEach(function (topic) {
  test('subscribe to invalid topic with ' + topic, function (t) {
    var s = connect(setup())

    s.inStream.write({
      cmd: 'subscribe',
      messageId: 24,
      subscriptions: [{
        topic: topic,
        qos: 0
      }]
    })

    eos(s.conn, function () {
      t.equal(s.broker.connectedClients, 0, 'no connected clients')
      t.end()
    })
  })
})
<|endoftext|>


<commit_before>const ENUMS = require('../lib/enums.js');

function WindowCoveringAccessory(name, uuid, profile, node, platform) {
    this.name = name;
    this.uuid = uuid;
    this.platform = platform
    this.log = platform.log;
    this.nodeId = node.id;
    this.profile = profile;
    this.attributes = {};

    for (attribute in node.attributes) {
        switch (attribute.type) {
            case ENUMS.CAAttributeType.CAAttributeTypePosition:
                this.attributes.position = attribute;
                break;
            case ENUMS.CAAttributeType.CAAttributeTypeUpDown:
                this.attributes.upDown = attribute;
                break;
        }
    }
}

WindowCoveringAccessory.prototype.updateValue = function (attribute) {
    switch (attribute.type) {
        case ENUMS.CAAttributeType.CAAttributeTypePosition:
            this.attributes.position = attribute;
            this.service.getCharacteristic(Characteristic.CurrentPosition)
                .updateValue(attribute.current_value, null, 'ws');
            this.service.getCharactersitic(Charactersitic.TargetPosition)
                .updateValue(attribute.target_value, null, 'ws');
            break;
        case ENUMS.CAAttributeType.CAAttributeTypeUpDown:
            this.attributes.upDown = attribute;
            this.service.getCharacteristic(Characteristic.PositionState)
                .updateValue([2,2,2,1,0][this.attributes.upDown.current_value], null, 'ws');
            break;
    }

    if (that.platform.debug) that.log(that.name + ': ' + attributeType + ': ' + newValue);
}

WindowCoveringAccessory.prototype.setTargetPosition = function (value, callback, context) {
    if (context && context == 'ws') {
        callback(null, value);
        return;
    }

    if (this.platform.debug) this.log('Setting ' + this.name + ' to ' + value);
    this.platform.homee.send(
        'PUT:/nodes/' + this.nodeId + '/attributes/' + this.attributes.position.id + '?target_value=' + value
    );

    callback(null, value);
}

WindowCoveringAccessory.prototype.setPositionState = function (value, callback, context) {
    if (context && context == 'ws') {
        callback(null, value);
        return;
    }

    newValue = [1,0,2][value];

    if (this.platform.debug) this.log('Setting ' + this.name + ' to ' + newValue);
    this.platform.homee.send(
        'PUT:/nodes/' + this.nodeId + '/attributes/' + this.attributes.upDown.id + '?target_value=' + newValue
    );

    callback(null, value);
}

WindowCoveringAccessory.prototype.getServices = function () {
    let informationService = new Service.AccessoryInformation();

    informationService
        .setCharacteristic(Characteristic.Manufacturer, "Homee")
        .setCharacteristic(Characteristic.Model, "WindowCovering")
        .setCharacteristic(Characteristic.SerialNumber, "");

    this.service = new Service.WindowCovering(this.name);

    this.service.getCharacteristics(Characteristic.CurrentPosition)
        .updateValue(this.attributes.position.current_value);

    this.service.getCharacteristics(Characteristic.TargetPosition)
        .updateValue(this.attributes.position.target_value)
        .on('set', this.setTargetPosition.bind(this));

    this.service.getCharacteristics(Characteristic.PositionState)
        .updateValue([2,2,2,1,0][this.attributes.upDown.current_value])
        .on('set', this.setPositionState.bind(this));

    return [informationService, this.service];
};

module.exports = function(oService, oCharacteristic) {
    Service = oService;
    Characteristic = oCharacteristic;

    return WindowCoveringAccessory;
};
<commit_msg>fixed typo and attribute mapping<commit_after>const ENUMS = require('../lib/enums.js');

function WindowCoveringAccessory(name, uuid, profile, node, platform) {
    this.name = name;
    this.uuid = uuid;
    this.platform = platform
    this.log = platform.log;
    this.nodeId = node.id;
    this.profile = profile;
    this.attributes = {};

    for (let i=0; i<node.attributes.length; i++) {
        switch (node.attributes[i].type) {
            case ENUMS.CAAttributeType.CAAttributeTypePosition:
                this.attributes.position = node.attributes[i];
                break;
            case ENUMS.CAAttributeType.CAAttributeTypeUpDown:
                this.attributes.upDown = node.attributes[i];
                break;
        }
    }
}

WindowCoveringAccessory.prototype.updateValue = function (attribute) {
    switch (attribute.type) {
        case ENUMS.CAAttributeType.CAAttributeTypePosition:
            this.attributes.position = attribute;
            this.service.getCharacteristic(Characteristic.CurrentPosition)
                .updateValue(attribute.current_value, null, 'ws');
            this.service.getCharactersitic(Charactersitic.TargetPosition)
                .updateValue(attribute.target_value, null, 'ws');
            break;
        case ENUMS.CAAttributeType.CAAttributeTypeUpDown:
            this.attributes.upDown = attribute;
            this.service.getCharacteristic(Characteristic.PositionState)
                .updateValue([2,2,2,1,0][this.attributes.upDown.current_value], null, 'ws');
            break;
    }

    if (that.platform.debug) that.log(that.name + ': ' + attributeType + ': ' + newValue);
}

WindowCoveringAccessory.prototype.setTargetPosition = function (value, callback, context) {
    if (context && context == 'ws') {
        callback(null, value);
        return;
    }

    if (this.platform.debug) this.log('Setting ' + this.name + ' to ' + value);
    this.platform.homee.send(
        'PUT:/nodes/' + this.nodeId + '/attributes/' + this.attributes.position.id + '?target_value=' + value
    );

    callback(null, value);
}

WindowCoveringAccessory.prototype.setPositionState = function (value, callback, context) {
    if (context && context == 'ws') {
        callback(null, value);
        return;
    }

    newValue = [1,0,2][value];

    if (this.platform.debug) this.log('Setting ' + this.name + ' to ' + newValue);
    this.platform.homee.send(
        'PUT:/nodes/' + this.nodeId + '/attributes/' + this.attributes.upDown.id + '?target_value=' + newValue
    );

    callback(null, value);
}

WindowCoveringAccessory.prototype.getServices = function () {
    let informationService = new Service.AccessoryInformation();

    informationService
        .setCharacteristic(Characteristic.Manufacturer, "Homee")
        .setCharacteristic(Characteristic.Model, "WindowCovering")
        .setCharacteristic(Characteristic.SerialNumber, "");

    this.service = new Service.WindowCovering(this.name);

    this.service.getCharacteristic(Characteristic.CurrentPosition)
        .updateValue(this.attributes.position.current_value);

    this.service.getCharacteristic(Characteristic.TargetPosition)
        .updateValue(this.attributes.position.target_value)
        .on('set', this.setTargetPosition.bind(this));

    this.service.getCharacteristic(Characteristic.PositionState)
        .updateValue([2,2,2,1,0][this.attributes.upDown.current_value])
        .on('set', this.setPositionState.bind(this));

    return [informationService, this.service];
};

module.exports = function(oService, oCharacteristic) {
    Service = oService;
    Characteristic = oCharacteristic;

    return WindowCoveringAccessory;
};
<|endoftext|>


<commit_before>var pull = require('pull-stream')
var ex = require('../example')

//var model_a = ex.create()

var tape = require('tape')

tape('simple', function (t) {
  var alice = ex.createChatModel('alice', [])
  var bob = ex.createChatModel('bob')

  var as = ex.createStream(alice)
  var bs = ex.createStream(bob)

  pull(as, bs, as)

  var msg = {author: 'alice', sequence: 1, content: 'hello bob!', timestamp: Date.now()}

  //have bob get ready to receive something
  bob.onAppend(function (_msg) {
    console.log('msg at bob:', _msg)
    t.deepEqual(_msg, msg)
    t.end()
  })

  //have alice send a message to bob
  alice.append(msg)

})

function init(model, N, M) {
  for(var i = 0; i < N; i++)
    model.logs['peer'+i] = []

  for(var i = 0; i < M; i++) {
    var k = 'peer'+~~(Math.random()*N)
    model.logs[k].push({
      author: k, sequence: model.logs[k].length + 1, content: 'hello:'+Math.random()
    })
  }
}

tape('simple', function (t) {
  var alice = ex.createChatModel()
  var bob = ex.createChatModel()

  init(alice, 10, 100)

  var as = ex.createStream(alice)
  var bs = ex.createStream(bob)

  function log(name) {
    return pull.through(function (data) {
      console.log(name, data)
    })
  }

  pull(as, bs, as)

  //because everything is sync, it should be complete already.

  t.deepEqual(bob.logs, alice.logs)
  t.end()
//  console.log(alice.logs)
})

tape('simple', function (t) {
  var alice = ex.createChatModel()
  var bob = ex.createChatModel()

  init(alice, 1000, 10000)

  var as = ex.createStream(alice)
  var bs = ex.createStream(bob)

  function log(name) {
    return pull.through(function (data) {
      console.log(name, data)
    })
  }
  var c = 0, start = Date.now()
  bob.onAppend(function () {
    if(!(c++ % 1000))
      console.log(c, Date.now() - start)
  })

  pull(as, bs, as)

  //because everything is sync, it should be complete already.

  var seconds = ((Date.now() - start)/1000)
  console.log(c, seconds, c / seconds)

  t.deepEqual(bob.logs, alice.logs)
  t.end()
//  console.log(alice.logs)
})

<commit_msg>add a benchmark into the tests<commit_after>var pull = require('pull-stream')
var ex = require('../example')

//var model_a = ex.create()

var tape = require('tape')

tape('simple', function (t) {
  var alice = ex.createChatModel('alice', [])
  var bob = ex.createChatModel('bob')

  var as = ex.createStream(alice)
  var bs = ex.createStream(bob)

  pull(as, bs, as)

  var msg = {author: 'alice', sequence: 1, content: 'hello bob!', timestamp: Date.now()}

  //have bob get ready to receive something
  bob.onAppend(function (_msg) {
    console.log('msg at bob:', _msg)
    t.deepEqual(_msg, msg)
    t.end()
  })

  //have alice send a message to bob
  alice.append(msg)

})

function init(model, N, M) {
  for(var i = 0; i < N; i++)
    model.logs['peer'+i] = []

  for(var i = 0; i < M; i++) {
    var k = 'peer'+~~(Math.random()*N)
    model.logs[k].push({
      author: k, sequence: model.logs[k].length + 1, content: 'hello:'+Math.random()
    })
  }
}

tape('simple', function (t) {
  var alice = ex.createChatModel()
  var bob = ex.createChatModel()

  init(alice, 10, 100)

  var as = ex.createStream(alice)
  var bs = ex.createStream(bob)

  function log(name) {
    return pull.through(function (data) {
      console.log(name, data)
    })
  }

  pull(as, bs, as)

  //because everything is sync, it should be complete already.

  t.deepEqual(bob.logs, alice.logs)
  t.end()
//  console.log(alice.logs)
})

tape('simple', function (t) {
  var alice = ex.createChatModel()
  var bob = ex.createChatModel()

  init(alice, 1000, 100000)

  var as = ex.createStream(alice)
  var bs = ex.createStream(bob)

  function log(name) {
    return pull.through(function (data) {
      console.log(name, data)
    })
  }
  var c = 0, start = Date.now()
  bob.onAppend(function () {
    if(!(c++ % 1000))
      console.log(c, Date.now() - start)
  })

  pull(as, bs, as)

  //because everything is sync, it should be complete already.

  var seconds = ((Date.now() - start)/1000)
  console.log(c, seconds, c / seconds)

  t.deepEqual(bob.logs, alice.logs)
  t.end()
//  console.log(alice.logs)
})

<|endoftext|>


<commit_before>'use strict';

var FetchRequest = require('./FetchRequest');
var _ = require('lodash');

module.exports = function (context, payload, done) {
    var done = done || function () {};

    var review = _.cloneDeep(payload.businessReview);
    review.request = payload.businessReviewRequest;

    context
        .getHairfieApi()
        .saveBusinessReview(review, {token: null})
        .then(function () {
            // refresh the token
            context.executeAction(FetchRequest, {
                id: payload.businessReviewRequest.id
            });
        })
        .fail(done);
};
<commit_msg>Track business review events<commit_after>'use strict';

var FetchRequest = require('./FetchRequest');
var _ = require('lodash');
var ga = require('../services/analytics');

module.exports = function (context, payload, done) {
    var done = done || function () {};

    var review = _.cloneDeep(payload.businessReview);
    review.request = payload.businessReviewRequest;

    context
        .getHairfieApi()
        .saveBusinessReview(review, {token: null})
        .then(function () {
            // refresh the token
            context.executeAction(FetchRequest, {
                id: payload.businessReviewRequest.id
            });

            // track the event
            ga('send', 'event', 'Business Reviews', 'Submit');
        })
        .fail(done);
};
<|endoftext|>


<commit_before>define({

	version : "Insert Version Here",

	webServiceSettings : {
		"" : {
			"MongoPimometer" : "http://localhost/pi_mometer/pi_collection/"
		}
	},

	clientSettings : {
		"default" : {
			"RefreshInterval" : 60000,
			"RequestTimeout" : 10000,
			"TimezoneOffset" : "put an offset here"
		}
	},

	themesConfig : {
		"Classic" : {
			"css" : "bootstrap-classic.css",
			"post-css" : "none"
		},
		"Cerulean" : {
			"css" : "bootstrap-cerulean.css",
			"post-css" : "none"
		},
		"Cyborg" : {
			"css" : "bootstrap-cyborg.css",
			"post-css" : "none"
		},
		"Redy" : {
			"css" : "bootstrap-redy.css",
			"post-css" : "none"
		},
		"Journal" : {
			"css" : "bootstrap-journal.css",
			"post-css" : "none"
		},
		"Simplex" : {
			"css" : "bootstrap-simplex.css",
			"post-css" : "none"
		},
		"Slate" : {
			"css" : "bootstrap-slate.css",
			"post-css" : "none"
		},
		"Spacelab" : {
			"css" : "bootstrap-spacelab.css",
			"post-css" : "none"
		},
		"United" : {
			"css" : "bootstrap-united.css",
			"post-css" : "none"
		}

	},

	menuConfig : {
		"Thermometer" : [ {
			"name" : "Real Time",
			"link" : "thermometer.html",
			"icon" : "icon name"
		}, {
			"name" : "Historical",
			"link" : "hist-thermometer.html",
			"icon" : "icon name"
		} ]
	}
});<commit_msg>apply proper port number to mongo host configuration<commit_after>define({

	version : "Insert Version Here",

	webServiceSettings : {
		"" : {
			"MongoPimometer" : "http://localhost:28017/pi_mometer/pi_collection/"
		}
	},

	clientSettings : {
		"default" : {
			"RefreshInterval" : 60000,
			"RequestTimeout" : 10000,
			"TimezoneOffset" : "put an offset here"
		}
	},

	themesConfig : {
		"Classic" : {
			"css" : "bootstrap-classic.css",
			"post-css" : "none"
		},
		"Cerulean" : {
			"css" : "bootstrap-cerulean.css",
			"post-css" : "none"
		},
		"Cyborg" : {
			"css" : "bootstrap-cyborg.css",
			"post-css" : "none"
		},
		"Redy" : {
			"css" : "bootstrap-redy.css",
			"post-css" : "none"
		},
		"Journal" : {
			"css" : "bootstrap-journal.css",
			"post-css" : "none"
		},
		"Simplex" : {
			"css" : "bootstrap-simplex.css",
			"post-css" : "none"
		},
		"Slate" : {
			"css" : "bootstrap-slate.css",
			"post-css" : "none"
		},
		"Spacelab" : {
			"css" : "bootstrap-spacelab.css",
			"post-css" : "none"
		},
		"United" : {
			"css" : "bootstrap-united.css",
			"post-css" : "none"
		}

	},

	menuConfig : {
		"Thermometer" : [ {
			"name" : "Real Time",
			"link" : "thermometer.html",
			"icon" : "icon name"
		}, {
			"name" : "Historical",
			"link" : "hist-thermometer.html",
			"icon" : "icon name"
		} ]
	}
});
<|endoftext|>


<commit_before>/**
 * Handles writting the notes to the browser console in synchronization with the reveal.js
 */
var ConsoleNotes = (function() {

    function log(event) {
	    // event.previousSlide, event.currentSlide, event.indexh, event.indexv
	    var notes = event.currentSlide.querySelector(".notes");

		if (notes) {
	        console.info(notes.innerHTML.replace(/\n\s+/g, "\n"));
	    }
    }

    // Fires when slide is changed
    Reveal.addEventListener("slidechanged", log);
})();
<commit_msg>Add support for remote file loading using JSONP + allow notes to be defined as a multiline array<commit_after>/**
 * Handles writting the notes to the browser console in synchronization with the reveal.js
 */
var ConsoleNotes = (function() {

    function log(event) {
	    // event.previousSlide, event.currentSlide, event.indexh, event.indexv
	    var notes = event.currentSlide.querySelector(".notes");

		if (notes) {
	        console.info(notes.innerHTML.replace(/\n\s+/g, "\n"));
	    }
    }

    // Fires when slide is changed
    Reveal.addEventListener("slidechanged", log);
})();<|endoftext|>