test('apply snapshot to new', function (t) {
    var self = this;
    var oldRaft = self.oldRaft;
    var snapshotter = oldRaft.snapshotter;
    var newRaft = self.newRaft;
    var snapshot;
    vasync.pipeline({
        funcs: [
            function (_, subcb) {
                vasync.forEachPipeline({
                    'func': oldRaft.clientRequest.bind(oldRaft),
                    'inputs': [
                        { 'command': 'foo' },
                        { 'command': 'bar' },
                        { 'command': 'baz' },
                        { 'command': 'bang' }
                    ]
                }, subcb);
            },
            function (_, subcb) {
                snapshotter.getLatest(function (err, snap) {
                    if (err) {
                        return (subcb(err));
                    }
                    snapshot = snap;
                    subcb();
                });
            },
            function (_, subcb) {
                newRaft.installSnapshot({
                    'snapshot': snapshot
                }, function (err, result) {
                    t.ok(result.success);
                    //The configuration + the 4 client requests above.
                    t.equal(4, result.index);
                    subcb(err);
                });
            },
            function (_, subcb) {
                //Check everything we can with the newraft instance.
                t.equal('raft-1', newRaft.id);
                t.equal(undefined, newRaft.leaderId);
                t.equal(0, newRaft.currentTerm());
                t.equal(undefined, newRaft.votedFor());
                //This is a little wonky.  In "real life" the entry for newRaft
                // would have been a part of r0's peer list.  But here we're
                // just checking that it copies over the peer list.
                t.deepEqual([ 'raft-0' ], newRaft.cluster.allPeerIds);
                t.equal(4, newRaft.stateMachine.commitIndex);
                t.equal('bang', newRaft.stateMachine.data);
                t.ok(Object.keys(newRaft.outstandingMessages).length === 0);
                helper.readClog(newRaft.clog, function (err, entries) {
                    if (err) {
                        return (subcb(err));
                    }
                    t.equals(5, entries.length);
                    //This is a bit wonky... but it is what it should be.
                    var firstEntryCommand = helper.e(createClusterConfig(
                        'raft-0'))(0, 0).command;
                    t.deepEqual([ firstEntryCommand,
                                  'foo', 'bar', 'baz', 'bang' ],
                                entries.map(function (x) {
                                    return (x.command);
                                }));
                    subcb();
                });
            }
        ]
    }, function (err) {
        if (err) {
            t.fail(err);
        }
        t.done();
    });
});
test('single raft, not bootstrapped on init', function (t) {
    vasync.pipeline({
        arg: {},
        funcs: [
            function newRaft(_, subcb) {
                memraft.raft({
                    'log': LOG,
                    'id': 'raft-0'
                }, function (err, r) {
                    if (err) {
                        return (subcb(err));
                    }
                    _.raft = r;
                    subcb();
                });
            },
            function tryTick(_, subcb) {
                t.equal('follower', _.raft.state);
                _.raft.tick();
                var newTimeout = _.raft.leaderTimeout;
                _.raft.tick();
                //Ticks should only reset a raft instance that has no
                // configuration.
                t.equal(newTimeout, _.raft.leaderTimeout);
                subcb();
            },
            function tryAppend(_, subcb) {
                _.raft.appendEntries({
                    'term': 0,
                    'commitIndex': 0,
                    'leaderId': 'raft-1',
                    'entries': entryStream([ 0, 0 ])
                }, function (err) {
                    if (!err) {
                        t.fail();
                        return (subcb());
                    }
                    t.equal('NotBootstrappedError', err.name);
                    subcb();
                });
            },
            function tryRequestVote(_, subcb) {
                _.raft.requestVote({
                    'term': 0,
                    'candidateId': 'raft-1',
                    'lastLogIndex': 0,
                    'lastLogTerm': 0
                }, function (err) {
                    if (!err) {
                        t.fail();
                        return (subcb());
                    }
                    t.equal('InvalidPeerError', err.name);
                    subcb();
                });
            }
        ]
    }, function (err) {
        if (err) {
            t.fail(err.toString());
        }
        t.done();
    });
});
Example #3
0
test('consistency check on 0, success', function (t) {
    var self = this;
    var funcs = [
        function (_, subcb) {
            self.clog.append({
                'commitIndex': 0,
                'term': 0,
                'entries': entryStream([ 0, 0 ])
            }, subcb);
        },
        function (_, subcb) {
            t.equal(1, self.clog.nextIndex);
            t.deepEqual(e(0, 0), self.clog.last());
            readClog(self.clog, function (err, clog) {
                t.ok(clog.length === 1);
                t.deepEqual(e(0, 0), clog[0]);
                subcb();
            });
        }
    ];
    vasync.pipeline({
        funcs: funcs,
        arg: {}
    }, function (err) {
        if (err) {
            t.fail(err);
        }
        t.done();
    });
});
Example #4
0
test('crud', function (t) {
    var props;
    vasync.pipeline({
        args: {},
        funcs: [
            function mkTmpDir(_, subcb) {
                fs.mkdir(TMP_DIR, function (err) {
                    if (err && err.code !== 'EEXIST') {
                        return (subcb(err));
                    }
                    return (subcb());
                });
            },
            function removeOldLevelDb(_, subcb) {
                helper.rmrf(DB_FILE, subcb);
            },
            function init(_, subcb) {
                props = new lib.Properties({
                    'log': LOG,
                    'location': DB_FILE
                });
                props.on('ready', subcb);
            },
            function write(_, subcb) {
                t.equal(0, props.get('currentTerm'));
                t.ok(props.get('foo') === undefined);
                t.ok(props.get('bar') === undefined);
                var p = { 'foo': 'fval', 'bar': 'bval'};
                props.write(p, subcb);
            },
            function read(_, subcb) {
                t.equal('fval', props.get('foo'));
                t.equal('bval', props.get('bar'));
                subcb();
            },
            function update(_, subcb) {
                var p = { 'bar': 'bval2'};
                props.write(p, subcb);
            },
            function checkUpdate(_, subcb) {
                t.equal('fval', props.get('foo'));
                t.equal('bval2', props.get('bar'));
                subcb();
            },
            function del(_, subcb) {
                props.delete('bar', subcb);
            },
            function checkDel(_, subcb) {
                t.equal('fval', props.get('foo'));
                t.ok(props.get('bar') === undefined);
                subcb();
            },
            function closeLeveDb(_, subcb) {
                props.db.close(subcb);
            },
            //Reopen and get again.
            function openNew(_, subcb) {
                props = new lib.Properties({
                    'log': LOG,
                    'location': DB_FILE
                });
                props.on('ready', subcb);
            },
            function readAgain(_, subcb) {
                t.equal('fval', props.get('foo'));
                t.ok(props.get('bar') === undefined);
                subcb();
            },
            function finalCloseLeveDb(_, subcb) {
                props.db.close(subcb);
            }
        ]
    }, function (err) {
        if (err) {
            t.fail(err);
        }
        t.done();
    });
});
Example #5
0
test('test assumptions', function (t) {
    var db;
    vasync.pipeline({
        'funcs': [
            function mkTmpDir(_, cb) {
                fs.mkdir(TMP_DIR, function (err) {
                    if (err && err.code !== 'EEXIST') {
                        return (cb(err));
                    }
                    return (cb());
                });
            },
            function initDb(_, cb) {
                var levelOpts = {
                    'keyEncoding': 'binary',
                    'valueEncoding': 'json'
                };
                levelup(DB_FILE, levelOpts, function (err, d) {
                    if (err) {
                        return (cb(err));
                    }
                    db = d;
                    cb();
                });
            },
            function writeStuff(_, cb) {
                db.batch([
                    { 'type': 'put', 'key': le(0), 'value': e(0, 0) },
                    { 'type': 'put', 'key': le(1), 'value': e(1, 0) },
                    { 'type': 'put', 'key': le(2), 'value': e(2, 1) },
                    { 'type': 'put', 'key': le(3), 'value': e(3, 1) },
                    { 'type': 'put', 'key': le(4), 'value': e(4, 2) }
                ], cb);
            },
            function iterMiddle(_, cb) {
                var rs = db.createReadStream({
                    'start': le(1),
                    'end': le(3)
                });
                var res = [];

                //Note: non-flowing!
                rs.on('readable', function () {
                    var d;
                    while (null !== (d = rs.read())) {
                        res.push(d.value.index);
                    }
                });

                rs.on('close', function () {
                    t.deepEqual([1, 2, 3], res);
                    cb();
                });
            },
            function iterMidToEnd(_, cb) {
                var rs = db.createReadStream({
                    'start': le(1),
                    'end': new Buffer('ffffffff', 'hex')
                });
                var res = [];

                //Note: non-flowing!
                rs.on('readable', function () {
                    var d;
                    while (null !== (d = rs.read())) {
                        res.push(d.value.index);
                    }
                });

                rs.on('close', function () {
                    cb();
                });
            },
            function iterAfterEnd(_, cb) {
                var rs = db.createReadStream({
                    'start': le(10),
                    'end': new Buffer('ffffffff', 'hex')
                });
                var res = [];

                rs.on('readable', function () {
                    var d;
                    while (null !== (d = rs.read())) {
                        res.push(d.value.index);
                    }
                });

                rs.on('close', function () {
                    t.deepEqual([], res);
                    cb();
                });
            },
            function iterStartEndEqual(_, cb) {
                var rs = db.createReadStream({
                    'start': le(2),
                    'end': le(2)
                });
                var res = [];

                rs.on('readable', function () {
                    var d;
                    while (null !== (d = rs.read())) {
                        res.push(d.value.index);
                    }
                });

                rs.on('close', function () {
                    t.deepEqual([ 2 ], res);
                    cb();
                });
            },
            function oneThenFlowingThenDestroy(_, cb) {
                var rs = db.createReadStream({
                    'start': le(1),
                    'end': le(5)
                });
                var res = [];

                rs.once('readable', function () {
                    var d = rs.read();
                    t.equal(1, d.value.index);
                    var read = 0;
                    rs.on('data', function (data) {
                        res.push(data.value.index);
                        if (++read === 2) {
                            //Note the destroy function here!
                            rs.destroy();
                        }
                    });
                });

                rs.on('close', function () {
                    t.deepEqual([2, 3], res);
                    cb();
                });
            },
            function close(_, cb) {
                db.close(cb);
            },
            function deleteDb(_, cb) {
                rmrf(DB_FILE, cb);
            }
        ]
    }, function (err) {
        if (err) {
            t.fail(err);
        }
        t.done();
    });
});
Example #6
0
///--- Helpers

function runLogKeyTest(t, i) {
    var enc = lib.key.log(i);
    var dec = lib.key.logDecode(enc);
    t.equal(i, dec);
}



///--- Tests

test('property key', function (t) {
    var s = 'foobarbaz';
    var enc = lib.key.property(s);
    var dec = lib.key.propertyDecode(enc);
    t.equal(s, dec);
    t.done();
});


test('internal property key', function (t) {
    var s = 'foobarbaz';
    var enc = lib.key.internalProperty(s);
    var dec = lib.key.propertyDecode(enc);
    t.equal(s, dec);
    t.done();
});


test('log key', function (t) {
Example #7
0
    runFlowingTest(opts, function () {
        opts.flowing = true;
        runFlowingTest(opts, function () {
            opts.t.done();
        });
    });
}



///--- Tests for the PairsStream class

test('no elements in streams', function (t) {
    runTest({
        't': t,
        'left': [], 'right': [],
        'result': makeResult()
    });
});


test('one element in streams', function (t) {
    runTest({
        't': t,
        'left': [1], 'right': [2],
        'result': makeResult([1, 2])
    });
});


test('end at same time', function (t) {
Example #8
0
test('request/reply', function (t) {
    var mb;
    var peers = getTopology(2);
    var funcs = [
        function init(_, subcb) {
            mb = new lib.MessageBus({ 'log': LOG, 'peers': peers });
            mb.on('ready', subcb);
        },
        function reqrep(_, subcb) {
            var messageId;
            var responseCalled = false;
            function onResponse(err, gMessageId, from, res) {
                t.equal(1, peers['0'].appendEntriesCalled);
                t.equal(messageId, gMessageId);
                t.equal('0', from);
                responseCalled = true;
            }

            t.equal(0, peers['0'].appendEntriesCalled);
            messageId = mb.send(
                'me', '0', { 'operation': 'appendEntries' }, onResponse);

            setImmediate(function () {
                t.equal(1, Object.keys(mb.messages).length);
                mb.tick(function () {
                    t.ok(responseCalled);
                    subcb();
                });
            });
        }
    ];
    vasync.pipeline({
        funcs: funcs
    }, function (err) {
        if (err) {
            t.fail(err);
        }
        t.done();
    });
});