<commit_before>'use strict';

/*globals describe, it*/
/*eslint no-unused-expressions:0 */

const chai = require('chai'),
    sinon = require('sinon');
chai.should();
const expect = chai.expect;

// The thing we're testing
const dice = require('../../plugins/dice'),
    util = require('../../util');

describe('dice', () => {
    describe('exports', () => {
        let fns = ['begin', 'stop', 'getDice', 'getMatcher', 'random', 'reply', 'rollDice'],
            objs = ['defaults', 'matchers', 'config'];
        describe('should export expected functions:', () => {
            fns.forEach((fn) => {
                it(fn + '()', () => expect(dice[fn]).to.be.a('function'));
            });
        });
        describe('should export expected objects', () => {
            objs.forEach((obj) => {
                it(obj, () => expect(dice[obj]).to.be.a('object'));
            });
        });
        it('should export only expected keys', () => {
            dice.should.have.all.keys(fns.concat(objs));
        });
    });

    describe('matchers (keys)', () => {
        ['Fate'].forEach((system) => {
            it('should have a matcher for ' + system + ' dice.', () => {
                expect(dice.matchers[system]).to.be.ok;
            });
            it('should have a regex matcher for ' + system + 'dice', () => {
                expect(dice.matchers[system].matcher).to.be.instanceof(RegExp);
            });
        });
    });
    describe('matchers (Fate)', () => {
        const matcher = dice.matchers.Fate.matcher;
        describe('Test Fate Dice inputs expecting negative match', () => {
            [
                ['arbitrary text', 'nomatch'],
                ['negative dice', '-4dF'],
                ['unrecognized suffix', '4df-nomatch'],
                ['embedded dice 1', 'embed4dF'],
                ['embedded dice 2', '4dFembed'],
                ['embedded dice 3', 'emb4dFed'],
                ['embedded dice 4', '4dembedFate']
            ].forEach((test) => {
                it(test[0] + ' (' + test[1] + ')', () => {
                    expect(matcher.exec(test[1])).to.equal(null);
                });
            });
        });
        describe('Test Fate Dice inputs expecting positive match', () => {
            [
                ['Uppercase', '4dF', ['4', undefined, undefined, undefined]],
                ['Uppercase 2', '4DF', ['4', undefined, undefined, undefined]],
                ['Lowercase', '4df', ['4', undefined, undefined, undefined]],
                ['Lowercase 2', '4Df', ['4', undefined, undefined, undefined]],
                ['Uppercase Long', '4dFate', ['4', 'ate', undefined, undefined]],
                ['Uppercase 2 Long', '4DFate', ['4', 'ate', undefined, undefined]],
                ['Uppercase All Long', '4dFATE', ['4', 'ATE', undefined, undefined]],
                ['Uppercase 2 All Long', '4DFATE', ['4', 'ATE', undefined, undefined]],
                ['Lowercase Long', '4dfate', ['4', 'ate', undefined, undefined]],
                ['Lowercase 2 Long', '4Dfate', ['4', 'ate', undefined, undefined]],
                ['Bonus Uppercase', '4dF+3', ['4', undefined, '+3', '+']],
                ['Bonus Uppercase 2', '4DF+3', ['4', undefined, '+3', '+']],
                ['Bonus Lowercase', '4df+3', ['4', undefined, '+3', '+']],
                ['Bonus Lowercase 2', '4Df+3', ['4', undefined, '+3', '+']],
                ['Bonus Uppercase Long', '4dFate+3', ['4', 'ate', '+3', '+']],
                ['Bonus Uppercase 2 Long', '4DFate+3', ['4', 'ate', '+3', '+']],
                ['Bonus Uppercase All Long', '4dFATE+3', ['4', 'ATE', '+3', '+']],
                ['Bonus Uppercase 2 All Long', '4DFATE+3', ['4', 'ATE', '+3', '+']],
                ['Bonus Lowercase Long', '4dfate+3', ['4', 'ate', '+3', '+']],
                ['Bonus Lowercase 2 Long', '4Dfate+3', ['4', 'ate', '+3', '+']],
                ['Penalty Uppercase', '4dF-3', ['4', undefined, '-3', '-']],
                ['Penalty Uppercase 2', '4DF-3', ['4', undefined, '-3', '-']],
                ['Penalty Lowercase', '4df-3', ['4', undefined, '-3', '-']],
                ['Penalty Lowercase 2', '4Df-3', ['4', undefined, '-3', '-']],
                ['Penalty Uppercase Long', '4dFate-3', ['4', 'ate', '-3', '-']],
                ['Penalty Uppercase 2 Long', '4DFate-3', ['4', 'ate', '-3', '-']],
                ['Penalty Uppercase All Long', '4dFATE-3', ['4', 'ATE', '-3', '-']],
                ['Penalty Uppercase 2 All Long', '4DFATE-3', ['4', 'ATE', '-3', '-']],
                ['Penalty Lowercase Long', '4dfate-3', ['4', 'ate', '-3', '-']],
                ['Penalty Lowercase 2 Long', '4Dfate-3', ['4', 'ate', '-3', '-']]
            ].forEach((test) => {
                it(test[0] + ' (' + test[1] + ')', () => {
                    test[2].unshift(test[1]);
                    (matcher.exec(test[1]) || {}).slice().should.deep.equal(test[2]);
                });
            });
        });
    });
    describe('parsers (Fate)', () => {
        const parser = dice.matchers.Fate.parser;
        describe('parser produces correct output from given inputs', () => {
            [
                [
                    ['4dF', '4', undefined, undefined, undefined], 4, 0
                ],
                [
                    ['7dF+5', '7', undefined, '+5', undefined], 7, 5
                ],
                [
                    ['9dF-6', '9', undefined, '-6', undefined], 9, -6
                ],
                [
                    ['0dF+2', '0', undefined, '+2', undefined], 0, 2
                ],
                [
                    ['-2dF+4', '-2', undefined, '+4', undefined], -2, 4
                ]
            ].forEach((test) => {
                it(JSON.stringify(test[0]).replace(/null/g, 'undefined'), () => {
                    parser(test[0]).should.deep.equal({
                        sides: 6,
                        count: test[1],
                        bonus: test[2]
                    });
                });
            });
        });
    });
    describe('formatters (Fate)', () => {
        const formatter = dice.matchers.Fate.formatter;
        describe('should produce expected output for given input:', () => {
            [
                [
                    [0, 0, 0, 0], 0, '|| [-] [-] [-] [-] || -4'
                ],
                [
                    [1, 1, 1, 1], 1, '|| [-] [-] [-] [-] || -3 (-4+1)'
                ],
                [
                    [0, 1, 0, 1], -1, '|| [-] [-] [-] [-] || -5 (-4-1)'
                ],
                [
                    [0, 0, 0, 0], 7, '|| [-] [-] [-] [-] || 3 (-4+7)'
                ],
                [
                    [3, 3, 3, 3], 0, '|| [0] [0] [0] [0] || 0'
                ],
                [
                    [4, 4, 4, 4], 1, '|| [0] [0] [0] [0] || 1 (0+1)'
                ],
                [
                    [3, 4, 3, 4], -1, '|| [0] [0] [0] [0] || -1 (0-1)'
                ],
                [
                    [4, 3, 3, 4], 7, '|| [0] [0] [0] [0] || 7 (0+7)'
                ],
                [
                    [5, 5, 5, 5], 0, '|| [+] [+] [+] [+] || 4'
                ],
                [
                    [6, 6, 6, 6], 1, '|| [+] [+] [+] [+] || 5 (4+1)'
                ],
                [
                    [5, 6, 5, 6], -1, '|| [+] [+] [+] [+] || 3 (4-1)'
                ],
                [
                    [6, 5, 5, 6], 7, '|| [+] [+] [+] [+] || 11 (4+7)'
                ],
                [
                    [1, 2, 3, 4], 0, '|| [-] [-] [0] [0] || -2'
                ],
                [
                    [2, 3, 5, 6], 0, '|| [-] [0] [+] [+] || 1'
                ]
            ].forEach((test) => {
                it(JSON.stringify(test[0]) + ' + ' + test[1], () => {
                    formatter({
                        bonus: test[1]
                    }, test[0]).should.equal(test[2]);
                });
            });
        });
    });
    describe('getDice()', () => {
        describe('Should produce correct number of dice for requested number of dice: ', () => {
            [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5].forEach((d) => {
                it(d + ' dice', () => {
                    expect(dice.getDice(4, d)).to.have.length(Math.abs(d));
                });
            });
        });
        describe('should convert from uniform distribution to dice faces:', () => {
            [
                [0, 1],
                [0.17, 2],
                [0.34, 3],
                [0.51, 4],
                [0.67, 5],
                [0.84, 6]
            ].forEach((test) => {
                dice.random = () => test[0];
                let result = dice.getDice(6, 1);
                dice.random = Math.random;
                it(test[0] + ' => ' + test[1], () => result.should.deep.equal([test[1]]));
            });
        });
    });
    describe('getMatcher()', () => {
        it('should accept null as argument', () => expect(dice.getMatcher(null)).to.be.undefined);
        it('should return undefined on no command', () => expect(dice.getMatcher({
            command: undefined
        })).to.be.undefined);
        it('should return undefined on wrong command', () => expect(dice.getMatcher({
            command: '!logstart'
        })).to.be.undefined);
        it('should return undefined Iroll command witn nonmatching arg', () => expect(dice.getMatcher({
            command: '!roLL',
            args: ['notDiceAtAll']
        })).to.be.undefined);
        const fate = dice.matchers.Fate;
        describe('should select FATE matcher for:', () => {
            ['4dF', '4DFate', '4DF+3', '4dfate+3', '4dF-3', '4DFATE-3'].forEach((test) => {
                it(test, () => dice.getMatcher({
                    command: '!RoLl',
                    args: [test]
                }).should.deep.equal(fate));
            });
        });
    });
    describe('reply()', () => {
        it('should reply privately to non action', () => {
            let client = {
                say: sinon.spy(),
                notice: sinon.spy()
            };
            let event = {
                who: 'foo',
                what: 'bar'
            };
            dice.reply(client, event, 'i am test');
            client.say.called.should.be.false;
            client.notice.called.should.be.true;
            client.notice.lastCall.args.should.deep.equal([event.who, 'i am test']);
        });
        it('should reply publically to action', () => {
            let client = {
                say: sinon.spy(),
                notice: sinon.spy()
            };
            let event = {
                type: 'action',
                who: 'foo',
                reply: 'bar'
            };
            dice.reply(client, event, 'i am test');
            client.say.called.should.be.true;
            client.notice.called.should.be.false;
            client.say.lastCall.args.should.deep.equal([event.reply, 'foo: i am test']);
        });
    });
    describe('rollDice()', () => {
        describe('Should not trigger for negative input conditions', () => {
            [
                ['no command', undefined, undefined],
                ['wrong command', '!foo', undefined],
                ['no dice', '!ROLL', ['nodice']]
            ].forEach((test) => {
                it('should pass on ' + test[0], () => {
                    let client = {
                        say: sinon.spy(),
                        notice: sinon.spy()
                    };
                    let event = {
                        command: test[1],
                        args: test[2]
                    };
                    dice.rollDice(client, event).should.be.false;
                    client.say.called.should.be.false;
                    client.notice.called.should.be.false;
                });
            });
        });
        describe('Should trigger for positive input conditions', () => {
            [
                ['one dice', ['4dF'], /^||( \[.\]){4} || \d+$/],
                ['one dice comment', ['4dF', 'comment'], /^||( \[.\]){4} || \d+ comment$/],
                ['one dice dash comment', ['4dF', '--', 'comment'], /^||( \[.\]){4} || \d+ comment$/],
                ['one fate dice two requested', ['4dF', '4df+3'], /^||( \[.\]){4} || \d+ 4dF\+3$/],
                ['one fate dice dash two requected', ['4dF', '--', '5dF'], /^||( \[.\]){4} || \d+ 5dF$/]
            ].forEach((test) => {
                it('should roll the correct number of dice: ' + test[0], () => {
                    let client = {
                        say: sinon.spy(),
                        notice: sinon.spy()
                    };
                    let event = {
                        command: '!ROLL',
                        args: test[1]
                    };
                    dice.rollDice(client, event).should.be.true;
                    client.notice.called.should.be.true;
                    client.notice.lastCall.args[1].should.match(test[2]);
                });
            });
        });
    });
    describe('begin()', () => {
        let handlers = {},
            client = {
                on: (evt, handler) => handlers[evt] = handler,
                notice: sinon.spy()
            };
        dice.begin(client);
        it('should only register for message_received', () => {
            Object.keys(handlers).should.deep.equal(['message_received']);
        });
        it('should respond to dice roll request', () => {
            handlers.message_received({
                command: '!roll',
                args: ['4dF']
            });
            client.notice.called.should.be.true;
        });
    });
    describe('stop', () => {
        it('should log message on stop', () => {
            const log = util.log,
                spy = sinon.spy();
            util.log = spy;
            dice.stop();
            util.log = log;
            spy.called.should.be.true;
            spy.lastCall.args.shoud.have.length(1);
            spy.lastCall.args[0].should.equal('Dice Stopping');
        });
    });
});
<commit_msg>fix stupid typo<commit_after>'use strict';

/*globals describe, it*/
/*eslint no-unused-expressions:0 */

const chai = require('chai'),
    sinon = require('sinon');
chai.should();
const expect = chai.expect;

// The thing we're testing
const dice = require('../../plugins/dice'),
    util = require('../../util');

describe('dice', () => {
    describe('exports', () => {
        let fns = ['begin', 'stop', 'getDice', 'getMatcher', 'random', 'reply', 'rollDice'],
            objs = ['defaults', 'matchers', 'config'];
        describe('should export expected functions:', () => {
            fns.forEach((fn) => {
                it(fn + '()', () => expect(dice[fn]).to.be.a('function'));
            });
        });
        describe('should export expected objects', () => {
            objs.forEach((obj) => {
                it(obj, () => expect(dice[obj]).to.be.a('object'));
            });
        });
        it('should export only expected keys', () => {
            dice.should.have.all.keys(fns.concat(objs));
        });
    });

    describe('matchers (keys)', () => {
        ['Fate'].forEach((system) => {
            it('should have a matcher for ' + system + ' dice.', () => {
                expect(dice.matchers[system]).to.be.ok;
            });
            it('should have a regex matcher for ' + system + 'dice', () => {
                expect(dice.matchers[system].matcher).to.be.instanceof(RegExp);
            });
        });
    });
    describe('matchers (Fate)', () => {
        const matcher = dice.matchers.Fate.matcher;
        describe('Test Fate Dice inputs expecting negative match', () => {
            [
                ['arbitrary text', 'nomatch'],
                ['negative dice', '-4dF'],
                ['unrecognized suffix', '4df-nomatch'],
                ['embedded dice 1', 'embed4dF'],
                ['embedded dice 2', '4dFembed'],
                ['embedded dice 3', 'emb4dFed'],
                ['embedded dice 4', '4dembedFate']
            ].forEach((test) => {
                it(test[0] + ' (' + test[1] + ')', () => {
                    expect(matcher.exec(test[1])).to.equal(null);
                });
            });
        });
        describe('Test Fate Dice inputs expecting positive match', () => {
            [
                ['Uppercase', '4dF', ['4', undefined, undefined, undefined]],
                ['Uppercase 2', '4DF', ['4', undefined, undefined, undefined]],
                ['Lowercase', '4df', ['4', undefined, undefined, undefined]],
                ['Lowercase 2', '4Df', ['4', undefined, undefined, undefined]],
                ['Uppercase Long', '4dFate', ['4', 'ate', undefined, undefined]],
                ['Uppercase 2 Long', '4DFate', ['4', 'ate', undefined, undefined]],
                ['Uppercase All Long', '4dFATE', ['4', 'ATE', undefined, undefined]],
                ['Uppercase 2 All Long', '4DFATE', ['4', 'ATE', undefined, undefined]],
                ['Lowercase Long', '4dfate', ['4', 'ate', undefined, undefined]],
                ['Lowercase 2 Long', '4Dfate', ['4', 'ate', undefined, undefined]],
                ['Bonus Uppercase', '4dF+3', ['4', undefined, '+3', '+']],
                ['Bonus Uppercase 2', '4DF+3', ['4', undefined, '+3', '+']],
                ['Bonus Lowercase', '4df+3', ['4', undefined, '+3', '+']],
                ['Bonus Lowercase 2', '4Df+3', ['4', undefined, '+3', '+']],
                ['Bonus Uppercase Long', '4dFate+3', ['4', 'ate', '+3', '+']],
                ['Bonus Uppercase 2 Long', '4DFate+3', ['4', 'ate', '+3', '+']],
                ['Bonus Uppercase All Long', '4dFATE+3', ['4', 'ATE', '+3', '+']],
                ['Bonus Uppercase 2 All Long', '4DFATE+3', ['4', 'ATE', '+3', '+']],
                ['Bonus Lowercase Long', '4dfate+3', ['4', 'ate', '+3', '+']],
                ['Bonus Lowercase 2 Long', '4Dfate+3', ['4', 'ate', '+3', '+']],
                ['Penalty Uppercase', '4dF-3', ['4', undefined, '-3', '-']],
                ['Penalty Uppercase 2', '4DF-3', ['4', undefined, '-3', '-']],
                ['Penalty Lowercase', '4df-3', ['4', undefined, '-3', '-']],
                ['Penalty Lowercase 2', '4Df-3', ['4', undefined, '-3', '-']],
                ['Penalty Uppercase Long', '4dFate-3', ['4', 'ate', '-3', '-']],
                ['Penalty Uppercase 2 Long', '4DFate-3', ['4', 'ate', '-3', '-']],
                ['Penalty Uppercase All Long', '4dFATE-3', ['4', 'ATE', '-3', '-']],
                ['Penalty Uppercase 2 All Long', '4DFATE-3', ['4', 'ATE', '-3', '-']],
                ['Penalty Lowercase Long', '4dfate-3', ['4', 'ate', '-3', '-']],
                ['Penalty Lowercase 2 Long', '4Dfate-3', ['4', 'ate', '-3', '-']]
            ].forEach((test) => {
                it(test[0] + ' (' + test[1] + ')', () => {
                    test[2].unshift(test[1]);
                    (matcher.exec(test[1]) || {}).slice().should.deep.equal(test[2]);
                });
            });
        });
    });
    describe('parsers (Fate)', () => {
        const parser = dice.matchers.Fate.parser;
        describe('parser produces correct output from given inputs', () => {
            [
                [
                    ['4dF', '4', undefined, undefined, undefined], 4, 0
                ],
                [
                    ['7dF+5', '7', undefined, '+5', undefined], 7, 5
                ],
                [
                    ['9dF-6', '9', undefined, '-6', undefined], 9, -6
                ],
                [
                    ['0dF+2', '0', undefined, '+2', undefined], 0, 2
                ],
                [
                    ['-2dF+4', '-2', undefined, '+4', undefined], -2, 4
                ]
            ].forEach((test) => {
                it(JSON.stringify(test[0]).replace(/null/g, 'undefined'), () => {
                    parser(test[0]).should.deep.equal({
                        sides: 6,
                        count: test[1],
                        bonus: test[2]
                    });
                });
            });
        });
    });
    describe('formatters (Fate)', () => {
        const formatter = dice.matchers.Fate.formatter;
        describe('should produce expected output for given input:', () => {
            [
                [
                    [0, 0, 0, 0], 0, '|| [-] [-] [-] [-] || -4'
                ],
                [
                    [1, 1, 1, 1], 1, '|| [-] [-] [-] [-] || -3 (-4+1)'
                ],
                [
                    [0, 1, 0, 1], -1, '|| [-] [-] [-] [-] || -5 (-4-1)'
                ],
                [
                    [0, 0, 0, 0], 7, '|| [-] [-] [-] [-] || 3 (-4+7)'
                ],
                [
                    [3, 3, 3, 3], 0, '|| [0] [0] [0] [0] || 0'
                ],
                [
                    [4, 4, 4, 4], 1, '|| [0] [0] [0] [0] || 1 (0+1)'
                ],
                [
                    [3, 4, 3, 4], -1, '|| [0] [0] [0] [0] || -1 (0-1)'
                ],
                [
                    [4, 3, 3, 4], 7, '|| [0] [0] [0] [0] || 7 (0+7)'
                ],
                [
                    [5, 5, 5, 5], 0, '|| [+] [+] [+] [+] || 4'
                ],
                [
                    [6, 6, 6, 6], 1, '|| [+] [+] [+] [+] || 5 (4+1)'
                ],
                [
                    [5, 6, 5, 6], -1, '|| [+] [+] [+] [+] || 3 (4-1)'
                ],
                [
                    [6, 5, 5, 6], 7, '|| [+] [+] [+] [+] || 11 (4+7)'
                ],
                [
                    [1, 2, 3, 4], 0, '|| [-] [-] [0] [0] || -2'
                ],
                [
                    [2, 3, 5, 6], 0, '|| [-] [0] [+] [+] || 1'
                ]
            ].forEach((test) => {
                it(JSON.stringify(test[0]) + ' + ' + test[1], () => {
                    formatter({
                        bonus: test[1]
                    }, test[0]).should.equal(test[2]);
                });
            });
        });
    });
    describe('getDice()', () => {
        describe('Should produce correct number of dice for requested number of dice: ', () => {
            [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5].forEach((d) => {
                it(d + ' dice', () => {
                    expect(dice.getDice(4, d)).to.have.length(Math.abs(d));
                });
            });
        });
        describe('should convert from uniform distribution to dice faces:', () => {
            [
                [0, 1],
                [0.17, 2],
                [0.34, 3],
                [0.51, 4],
                [0.67, 5],
                [0.84, 6]
            ].forEach((test) => {
                dice.random = () => test[0];
                let result = dice.getDice(6, 1);
                dice.random = Math.random;
                it(test[0] + ' => ' + test[1], () => result.should.deep.equal([test[1]]));
            });
        });
    });
    describe('getMatcher()', () => {
        it('should accept null as argument', () => expect(dice.getMatcher(null)).to.be.undefined);
        it('should return undefined on no command', () => expect(dice.getMatcher({
            command: undefined
        })).to.be.undefined);
        it('should return undefined on wrong command', () => expect(dice.getMatcher({
            command: '!logstart'
        })).to.be.undefined);
        it('should return undefined Iroll command witn nonmatching arg', () => expect(dice.getMatcher({
            command: '!roLL',
            args: ['notDiceAtAll']
        })).to.be.undefined);
        const fate = dice.matchers.Fate;
        describe('should select FATE matcher for:', () => {
            ['4dF', '4DFate', '4DF+3', '4dfate+3', '4dF-3', '4DFATE-3'].forEach((test) => {
                it(test, () => dice.getMatcher({
                    command: '!RoLl',
                    args: [test]
                }).should.deep.equal(fate));
            });
        });
    });
    describe('reply()', () => {
        it('should reply privately to non action', () => {
            let client = {
                say: sinon.spy(),
                notice: sinon.spy()
            };
            let event = {
                who: 'foo',
                what: 'bar'
            };
            dice.reply(client, event, 'i am test');
            client.say.called.should.be.false;
            client.notice.called.should.be.true;
            client.notice.lastCall.args.should.deep.equal([event.who, 'i am test']);
        });
        it('should reply publically to action', () => {
            let client = {
                say: sinon.spy(),
                notice: sinon.spy()
            };
            let event = {
                type: 'action',
                who: 'foo',
                reply: 'bar'
            };
            dice.reply(client, event, 'i am test');
            client.say.called.should.be.true;
            client.notice.called.should.be.false;
            client.say.lastCall.args.should.deep.equal([event.reply, 'foo: i am test']);
        });
    });
    describe('rollDice()', () => {
        describe('Should not trigger for negative input conditions', () => {
            [
                ['no command', undefined, undefined],
                ['wrong command', '!foo', undefined],
                ['no dice', '!ROLL', ['nodice']]
            ].forEach((test) => {
                it('should pass on ' + test[0], () => {
                    let client = {
                        say: sinon.spy(),
                        notice: sinon.spy()
                    };
                    let event = {
                        command: test[1],
                        args: test[2]
                    };
                    dice.rollDice(client, event).should.be.false;
                    client.say.called.should.be.false;
                    client.notice.called.should.be.false;
                });
            });
        });
        describe('Should trigger for positive input conditions', () => {
            [
                ['one dice', ['4dF'], /^||( \[.\]){4} || \d+$/],
                ['one dice comment', ['4dF', 'comment'], /^||( \[.\]){4} || \d+ comment$/],
                ['one dice dash comment', ['4dF', '--', 'comment'], /^||( \[.\]){4} || \d+ comment$/],
                ['one fate dice two requested', ['4dF', '4df+3'], /^||( \[.\]){4} || \d+ 4dF\+3$/],
                ['one fate dice dash two requected', ['4dF', '--', '5dF'], /^||( \[.\]){4} || \d+ 5dF$/]
            ].forEach((test) => {
                it('should roll the correct number of dice: ' + test[0], () => {
                    let client = {
                        say: sinon.spy(),
                        notice: sinon.spy()
                    };
                    let event = {
                        command: '!ROLL',
                        args: test[1]
                    };
                    dice.rollDice(client, event).should.be.true;
                    client.notice.called.should.be.true;
                    client.notice.lastCall.args[1].should.match(test[2]);
                });
            });
        });
    });
    describe('begin()', () => {
        let handlers = {},
            client = {
                on: (evt, handler) => handlers[evt] = handler,
                notice: sinon.spy()
            };
        dice.begin(client);
        it('should only register for message_received', () => {
            Object.keys(handlers).should.deep.equal(['message_received']);
        });
        it('should respond to dice roll request', () => {
            handlers.message_received({
                command: '!roll',
                args: ['4dF']
            });
            client.notice.called.should.be.true;
        });
    });
    describe('stop', () => {
        it('should log message on stop', () => {
            const log = util.log,
                spy = sinon.spy();
            util.log = spy;
            dice.stop();
            util.log = log;
            spy.called.should.be.true;
            spy.lastCall.args.should.have.length(1);
            spy.lastCall.args[0].should.equal('Dice Stopping');
        });
    });
});
<|endoftext|>


<commit_before>var util = require('./util');
var should = require('should');

// For all tests, the dummy driver spits back the passed queries as the result
// data, so testing that the result matches the query will confirm that the
// result data is from the correct query.
describe('Database', function() {
  describe('#queryTransaction()', function() {
    var db = util.get_driver();
    beforeEach(function() {
      db.driver.clear_query_stack();
    });
    describe('with one successful query (custom)', function() {
      it('should not return an error object', function(done) {
        var query = util.query_string;
        var cb = function(err, data) {
          should.not.exist(err);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it('should return query data to the callback', function(done) {
        var query = util.query_string;
        var query_data = query();
        var cb = function(err, data) {
          data.query.should.equal(query_data);
          done();
        }
        db.queryTransaction(query, cb);
      });
    });
    describe('with one successful query (object)', function() {
      it('should not return an error object', function(done) {
        var query = util.query_object;
        var cb = function(err, data) {
          should.not.exist(err);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it('should return query data to the callback', function(done) {
        var query = util.query_object;
        var query_data = "object";
        var cb = function(err, data) {
          data.query.should.equal(query_data);
          done();
        }
        db.queryTransaction(query, cb);
      });
    });
    describe('with one failed query', function() {
      it('should return an error object', function(done) {
        var query = util.query_error;
        var error_data = 'ERROR';
        var cb = function(err, data) {
          err.should.equal(error_data);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it('should not return a data object', function(done) {
        var query = util.query_error;
        var cb = function(err, data) {
          should.not.exist(data);
          done();
        }
        db.queryTransaction(query, cb);
      });
    });
    describe('with one query returning false', function() {
      it('should not return an error object', function(done) {
        var query = util.query_false;
        var cb = function(err, data) {
          should.not.exist(err);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it('should return data as false', function(done) {
        var query = util.query_false;
        var cb = function(err, data) {
          data.should.be.false;
          done();
        }
        db.queryTransaction(query, cb);
      });
    });
    describe('with two queries (both succeed)', function() {
      it('should not return an error object', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1, query) {
          util.query_object(query);
        }
        var cb = function(err, data2) {
          should.not.exist(err);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should return query1 data as the second argument to query2', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1) {
          data1.query.should.equal(query1_data);
          return util.query_array();
        }
        var query1_data = query1();
        var cb = function(err, data2) {
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should return query2 data as the second argument to user callback', function(done) {
        var query1 = util.query_array;
        var query2 = function(data1) {
          return util.query_string();
        }
        var query2_data = util.query_string();
        var cb = function(err, data2) {
          data2.query.should.equal(query2_data);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
    });
    describe('with two queries (first fails)', function() {
      it('should return an error object', function(done) {
        var query1 = util.query_error;
        var query2 = function(data1) {
          return util.query_string();
        }
        var error_data = 'ERROR';
        var cb = function(err, data2) {
          err.should.equal(error_data);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should not return a data object to query2', function(done) {
        var query1 = util.query_error;
        var query2 = function(data1) {
          should.not.exist(data1);
          return util.query_string();
        }
        var cb = function(err, data2) {
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should not return a data object to the user callback', function(done) {
        var query1 = util.query_error;
        var query2 = function(data1) {
          return util.query_string();
        }
        var cb = function(err, data2) {
          should.not.exist(data2);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
    });
    describe('with two queries (second fails)', function() {
      it('should return an error object', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1) {
          return util.query_error();
        }
        var error_data = 'ERROR';
        var cb = function(err, data2) {
          err.should.equal(error_data);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should return a data object to query2', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1) {
          data1.query.should.equal(query1_data);
          return util.query_error();
        }
        var query1_data = query1();
        var cb = function(err, data2) {
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should not return a data object to the user callback', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1) {
          return util.query_error();
        }
        var cb = function(err, data2) {
          should.not.exist(data2);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
    });
  });
});

<commit_msg>add query stack tests for single queries for database queryTransaction method.<commit_after>var _ = require('underscore');
var util = require('./util');
var should = require('should');

// For all tests, the dummy driver spits back the passed queries as the result
// data, so testing that the result matches the query will confirm that the
// result data is from the correct query.
describe('Database', function() {
  describe('#queryTransaction()', function() {
    var db = util.get_driver();
    var check_query_stack = function(good_stack) {
      var stack = db.driver.get_query_stack();
      _.isEqual(good_stack, stack).should.be.true;
    }
    beforeEach(function() {
      db.driver.clear_query_stack();
    });
    describe('with one successful query (custom)', function() {
      it('should not return an error object', function(done) {
        var query = util.query_string;
        var cb = function(err, data) {
          should.not.exist(err);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it('should return query data to the callback', function(done) {
        var query = util.query_string;
        var query_data = query();
        var cb = function(err, data) {
          data.query.should.equal(query_data);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it("should have a query stack of ['BEGIN', query, 'COMMIT']", function(done) {
        var query = util.query_string;
        var query_data = query();
        var good_stack = ['BEGIN', query_data, 'COMMIT'];
        var cb = function(err, data) {
          check_query_stack(good_stack);
          done();
        }
        db.queryTransaction(query, cb);
      });
    });
    describe('with one successful query (object)', function() {
      it('should not return an error object', function(done) {
        var query = util.query_object;
        var cb = function(err, data) {
          should.not.exist(err);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it('should return query data to the callback', function(done) {
        var query = util.query_object;
        var query_data = "object";
        var cb = function(err, data) {
          data.query.should.equal(query_data);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it("should have a query stack of ['BEGIN', query, 'COMMIT']", function(done) {
        var query = util.query_object;
        var query_data = "object";
        var good_stack = ['BEGIN', query_data, 'COMMIT'];
        var cb = function(err, data) {
          check_query_stack(good_stack);
          done();
        }
        db.queryTransaction(query, cb);
      });
    });
    describe('with one failed query', function() {
      it('should return an error object', function(done) {
        var query = util.query_error;
        var error_data = 'ERROR';
        var cb = function(err, data) {
          err.should.equal(error_data);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it('should not return a data object', function(done) {
        var query = util.query_error;
        var cb = function(err, data) {
          should.not.exist(data);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it("should have a query stack of ['BEGIN', 'ERROR', 'ROLLBACK']", function(done) {
        var query = util.query_error;
        var error_data = 'ERROR';
        var good_stack = ['BEGIN', error_data, 'ROLLBACK'];
        var cb = function(err, data) {
          check_query_stack(good_stack);
          done();
        }
        db.queryTransaction(query, cb);
      });
    });
    describe('with one query returning false', function() {
      it('should not return an error object', function(done) {
        var query = util.query_false;
        var cb = function(err, data) {
          should.not.exist(err);
          done();
        }
        db.queryTransaction(query, cb);
      });
      it('should return data as false', function(done) {
        var query = util.query_false;
        var cb = function(err, data) {
          data.should.be.false;
          done();
        }
        db.queryTransaction(query, cb);
      });
      it("should have a query stack of ['BEGIN', 'COMMIT']", function(done) {
        var query = util.query_false;
        var good_stack = ['BEGIN', 'COMMIT'];
        var cb = function(err, data) {
          check_query_stack(good_stack);
          done();
        }
        db.queryTransaction(query, cb);
      });
    });
    describe('with two queries (both succeed)', function() {
      it('should not return an error object', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1, query) {
          util.query_object(query);
        }
        var cb = function(err, data2) {
          should.not.exist(err);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should return query1 data as the second argument to query2', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1) {
          data1.query.should.equal(query1_data);
          return util.query_array();
        }
        var query1_data = query1();
        var cb = function(err, data2) {
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should return query2 data as the second argument to user callback', function(done) {
        var query1 = util.query_array;
        var query2 = function(data1) {
          return util.query_string();
        }
        var query2_data = util.query_string();
        var cb = function(err, data2) {
          data2.query.should.equal(query2_data);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
    });
    describe('with two queries (first fails)', function() {
      it('should return an error object', function(done) {
        var query1 = util.query_error;
        var query2 = function(data1) {
          return util.query_string();
        }
        var error_data = 'ERROR';
        var cb = function(err, data2) {
          err.should.equal(error_data);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should not return a data object to query2', function(done) {
        var query1 = util.query_error;
        var query2 = function(data1) {
          should.not.exist(data1);
          return util.query_string();
        }
        var cb = function(err, data2) {
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should not return a data object to the user callback', function(done) {
        var query1 = util.query_error;
        var query2 = function(data1) {
          return util.query_string();
        }
        var cb = function(err, data2) {
          should.not.exist(data2);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
    });
    describe('with two queries (second fails)', function() {
      it('should return an error object', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1) {
          return util.query_error();
        }
        var error_data = 'ERROR';
        var cb = function(err, data2) {
          err.should.equal(error_data);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should return a data object to query2', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1) {
          data1.query.should.equal(query1_data);
          return util.query_error();
        }
        var query1_data = query1();
        var cb = function(err, data2) {
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
      it('should not return a data object to the user callback', function(done) {
        var query1 = util.query_string;
        var query2 = function(data1) {
          return util.query_error();
        }
        var cb = function(err, data2) {
          should.not.exist(data2);
          done();
        }
        db.queryTransaction(query1, query2, cb);
      });
    });
  });
});

<|endoftext|>


<commit_before>var assert = require('assert');
var redis_config = require('./support/config').redis_pool;
var MetaData = require('../lib/carto_metadata')(redis_config);

var user = 'vizzuality';
var app = 'test';
var endpointGroup = 'test';

describe('rate limits: getLowerRateLimit', function() {
    it("1 limit: not limited", function () {
        var limits = [[0, 3, 1, -1, 1]];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limits[0], result);
    });

    it("1 limit: limited", function () {
        var limits = [[1, 3, 0, 0, 1]];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limits[0], result);
    });

    it("empty or invalid", function () {
        var limits = [];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = undefined;
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = null;
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = [[]];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = [[], []];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = {};
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = [{}];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = [[1, 2]];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);
    });

    it("multiple limits: valid and invalid", function () {
        var limit1 = [0, 3, 0];
        var limit2 = [0, 3, 1, 0, 1];

        var limits = [limit1, limit2];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit2, result);

        limits = [limit2, limit1];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit2, result);
    });

    it("multiple limits: not limited", function () {
        var limit1 = [0, 3, 2, 0, 1];
        var limit2 = [0, 3, 3, 0, 1];
        var limit3 = [0, 3, 1, 0, 1];
        var limit4 = [0, 3, 4, 0, 1];
        var limit5 = [0, 3, 5, 0, 1];

        var limits = [limit1, limit2, limit3, limit4, limit5];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit3, result);

        limits = [limit1, limit2];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit1, result);
    });

    it("multiple limits: limited", function () {
        var limit1 = [0, 3, 2, 0, 1];
        var limit2 = [0, 3, 3, 0, 1];
        var limit3 = [0, 3, 1, 0, 1];
        var limit4 = [0, 3, 4, 0, 1];
        var limit5 = [1, 3, 5, 0, 1];

        var limits = [limit1, limit2, limit3, limit4, limit5];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit5, result);

        limits = [limit1, limit2, limit5, limit3, limit4];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit5, result);
    });
});

function assertGetRateLimitRequest(isBlocked, limit, remaining, retry, reset, done) {
    MetaData.getRateLimit(user, app, endpointGroup, function (err, rateLimit) {
        assert.ifError(err);
        assert.deepEqual(rateLimit, [isBlocked, limit, remaining, retry, reset]);

        if (done) {
            setTimeout(done, reset * 1000 + 1000)
        }
    });
}

describe('rate limits: getRateLimit', function() {
    before(function(done) {
        var storeKey = MetaData.rate_limits_store_key({
            username: user,
            app: app,
            endpointGroup: endpointGroup
        });

        MetaData.redisCmd(
            MetaData.rate_limits_db,
            'RPUSH',
            [storeKey, 1, 1, 1],
            done
        )
    });

    it("1 req/sec: 2 req/seg should be limited", function (done) {
        this.timeout(5000);

        assertGetRateLimitRequest(0, 2, 1, -1, 1);
        setTimeout( function() { assertGetRateLimitRequest(0, 2, 0, -1, 1)}, 250);
        setTimeout( function() { assertGetRateLimitRequest(1, 2, 0, 0, 1) }, 500);
        setTimeout( function() { assertGetRateLimitRequest(1, 2, 0, 0, 1) }, 750);
        setTimeout( function() { assertGetRateLimitRequest(1, 2, 0, 0, 1) }, 950);
        setTimeout( function() { assertGetRateLimitRequest(0, 2, 0, -1, 1, done) }, 1050);
    });

    it('should work with loadRateLimitsScript', function (done) {
        this.timeout(5000);

        MetaData.loadRateLimitsScript(function(err) {
            assert.ifError(err);
            assertGetRateLimitRequest(0, 2, 1, -1, 1, done);
        });
    });

    it('should work after lose loadRateLimitsScript', function (done) {
        this.timeout(5000);

        MetaData.loadRateLimitsScript(function(err) {
            assert.ifError(err);

            MetaData.redisCmd(MetaData.rate_limits_db, 'SCRIPT', ['FLUSH'], function () {
                assertGetRateLimitRequest(0, 2, 1, -1, 1, done);
            });
        });
    });
});
<commit_msg>no limits values test<commit_after>var assert = require('assert');
var redis_config = require('./support/config').redis_pool;
var MetaData = require('../lib/carto_metadata')(redis_config);

var user = 'vizzuality';
var app = 'test';
var endpointGroup = 'test';

describe('rate limits: getLowerRateLimit', function() {
    it("1 limit: not limited", function () {
        var limits = [[0, 3, 1, -1, 1]];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limits[0], result);
    });

    it("1 limit: limited", function () {
        var limits = [[1, 3, 0, 0, 1]];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limits[0], result);
    });

    it("empty or invalid", function () {
        var limits = [];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = undefined;
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = null;
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = [[]];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = [[], []];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = {};
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = [{}];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);

        limits = [[1, 2]];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(undefined, result);
    });

    it("multiple limits: valid and invalid", function () {
        var limit1 = [0, 3, 0];
        var limit2 = [0, 3, 1, 0, 1];

        var limits = [limit1, limit2];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit2, result);

        limits = [limit2, limit1];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit2, result);
    });

    it("multiple limits: not limited", function () {
        var limit1 = [0, 3, 2, 0, 1];
        var limit2 = [0, 3, 3, 0, 1];
        var limit3 = [0, 3, 1, 0, 1];
        var limit4 = [0, 3, 4, 0, 1];
        var limit5 = [0, 3, 5, 0, 1];

        var limits = [limit1, limit2, limit3, limit4, limit5];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit3, result);

        limits = [limit1, limit2];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit1, result);
    });

    it("multiple limits: limited", function () {
        var limit1 = [0, 3, 2, 0, 1];
        var limit2 = [0, 3, 3, 0, 1];
        var limit3 = [0, 3, 1, 0, 1];
        var limit4 = [0, 3, 4, 0, 1];
        var limit5 = [1, 3, 5, 0, 1];

        var limits = [limit1, limit2, limit3, limit4, limit5];
        var result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit5, result);

        limits = [limit1, limit2, limit5, limit3, limit4];
        result = MetaData.getLowerRateLimit(limits);
        assert.deepEqual(limit5, result);
    });
});

function assertGetRateLimitRequest(isBlocked, limit, remaining, retry, reset, done) {
    MetaData.getRateLimit(user, app, endpointGroup, function (err, rateLimit) {
        assert.ifError(err);
        assert.deepEqual(rateLimit, [isBlocked, limit, remaining, retry, reset]);

        if (done) {
            setTimeout(done, reset * 1000 + 1000)
        }
    });
}

describe('rate limits: getRateLimit', function() {
    before(function(done) {
        var storeKey = MetaData.rate_limits_store_key({
            username: user,
            app: app,
            endpointGroup: endpointGroup
        });

        MetaData.redisCmd(
            MetaData.rate_limits_db,
            'RPUSH',
            [storeKey, 1, 1, 1],
            done
        )
    });

    it("1 req/sec: 2 req/seg should be limited", function (done) {
        this.timeout(5000);

        assertGetRateLimitRequest(0, 2, 1, -1, 1);
        setTimeout( function() { assertGetRateLimitRequest(0, 2, 0, -1, 1)}, 250);
        setTimeout( function() { assertGetRateLimitRequest(1, 2, 0, 0, 1) }, 500);
        setTimeout( function() { assertGetRateLimitRequest(1, 2, 0, 0, 1) }, 750);
        setTimeout( function() { assertGetRateLimitRequest(1, 2, 0, 0, 1) }, 950);
        setTimeout( function() { assertGetRateLimitRequest(0, 2, 0, -1, 1, done) }, 1050);
    });

    it('should work with loadRateLimitsScript', function (done) {
        this.timeout(5000);

        MetaData.loadRateLimitsScript(function(err) {
            assert.ifError(err);
            assertGetRateLimitRequest(0, 2, 1, -1, 1, done);
        });
    });

    it('should work after lose loadRateLimitsScript', function (done) {
        this.timeout(5000);

        MetaData.loadRateLimitsScript(function(err) {
            assert.ifError(err);

            MetaData.redisCmd(MetaData.rate_limits_db, 'SCRIPT', ['FLUSH'], function () {
                assertGetRateLimitRequest(0, 2, 1, -1, 1, done);
            });
        });
    });
});

describe('rate limits: invalid limits', function() {
    it("should no return a rate limit if limit values are empty", function (done) {
        var storeKey = MetaData.rate_limits_store_key({
            username: user,
            app: app,
            endpointGroup: endpointGroup
        });

        MetaData.redisCmd(
            MetaData.rate_limits_db,
            'DEL',
            [storeKey],
            function() {
                MetaData.getRateLimit(user, app, endpointGroup, function (err, rateLimit) {
                    assert.ifError(err);
                    assert.ifError(rateLimit);
                    done();
                });
            }
        )

    });

});
<|endoftext|>


<commit_before>(function(require) {
require.alias('/events', '/../../../events-browserify/events');
require.define('/../../../events-browserify/events', '/../../../events-browserify/', function(global, module, exports, require, process, __filename, __dirname) {
if (!process.EventEmitter) process.EventEmitter = function () {};

var EventEmitter = exports.EventEmitter = process.EventEmitter;
var isArray = typeof Array.isArray === 'function'
    ? Array.isArray
    : function (xs) {
        return Object.prototype.toString.call(xs) === '[object Array]'
    }
;

// By default EventEmitters will print a warning if more than
// 10 listeners are added to it. This is a useful default which
// helps finding memory leaks.
//
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
var defaultMaxListeners = 10;
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!this._events) this._events = {};
  this._events.maxListeners = n;
};

EventEmitter.prototype.emit = function(type) {
  // If there is no 'error' event listener then throw.
  if (type === 'error') {
    if (!this._events || !this._events.error ||
        (isArray(this._events.error) && !this._events.error.length))
    {
      if (arguments[1] instanceof Error) {
        throw arguments[1]; // Unhandled 'error' event
      } else {
        throw new Error("Uncaught, unspecified 'error' event.");
      }
      return false;
    }
  }

  if (!this._events) return false;
  var handler = this._events[type];
  if (!handler) return false;

  if (typeof handler == 'function') {
    switch (arguments.length) {
      // fast cases
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      // slower
      default:
        var args = Array.prototype.slice.call(arguments, 1);
        handler.apply(this, args);
    }
    return true;

  } else if (isArray(handler)) {
    var args = Array.prototype.slice.call(arguments, 1);

    var listeners = handler.slice();
    for (var i = 0, l = listeners.length; i < l; i++) {
      listeners[i].apply(this, args);
    }
    return true;

  } else {
    return false;
  }
};

// EventEmitter is defined in src/node_events.cc
// EventEmitter.prototype.emit() is also defined there.
EventEmitter.prototype.addListener = function(type, listener) {
  if ('function' !== typeof listener) {
    throw new Error('addListener only takes instances of Function');
  }

  if (!this._events) this._events = {};

  // To avoid recursion in the case that type == "newListeners"! Before
  // adding it to the listeners, first emit "newListeners".
  this.emit('newListener', type, listener);

  if (!this._events[type]) {
    // Optimize the case of one listener. Don't need the extra array object.
    this._events[type] = listener;
  } else if (isArray(this._events[type])) {

    // Check for listener leak
    if (!this._events[type].warned) {
      var m;
      if (this._events.maxListeners !== undefined) {
        m = this._events.maxListeners;
      } else {
        m = defaultMaxListeners;
      }

      if (m && m > 0 && this._events[type].length > m) {
        this._events[type].warned = true;
        console.error('(node) warning: possible EventEmitter memory ' +
                      'leak detected. %d listeners added. ' +
                      'Use emitter.setMaxListeners() to increase limit.',
                      this._events[type].length);
        console.trace();
      }
    }

    // If we've already got an array, just append.
    this._events[type].push(listener);
  } else {
    // Adding the second element, need to change to array.
    this._events[type] = [this._events[type], listener];
  }

  return this;
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.once = function(type, listener) {
  var self = this;
  self.on(type, function g() {
    self.removeListener(type, g);
    listener.apply(this, arguments);
  });

  return this;
};

EventEmitter.prototype.removeListener = function(type, listener) {
  if ('function' !== typeof listener) {
    throw new Error('removeListener only takes instances of Function');
  }

  // does not use listeners(), so no side effect of creating _events[type]
  if (!this._events || !this._events[type]) return this;

  var list = this._events[type];

  if (isArray(list)) {
    var i = list.indexOf(listener);
    if (i < 0) return this;
    list.splice(i, 1);
    if (list.length == 0)
      delete this._events[type];
  } else if (this._events[type] === listener) {
    delete this._events[type];
  }

  return this;
};

EventEmitter.prototype.removeAllListeners = function(type) {
  // does not use listeners(), so no side effect of creating _events[type]
  if (type && this._events && this._events[type]) this._events[type] = null;
  return this;
};

EventEmitter.prototype.listeners = function(type) {
  if (!this._events) this._events = {};
  if (!this._events[type]) this._events[type] = [];
  if (!isArray(this._events[type])) {
    this._events[type] = [this._events[type]];
  }
  return this._events[type];
};

});

require.define('/events', '/', function(global, module, exports, require, process, __filename, __dirname) {
// check that events is properly shimmed
require('events');

});

})(require);
<commit_msg>fix test reference file<commit_after>(function(require) {
require.alias('/events', '/../../node_modules/events-browserify/events');
require.define('/../../node_modules/events-browserify/events', '/../../node_modules/events-browserify/', function(global, module, exports, require, process, __filename, __dirname) {
if (!process.EventEmitter) process.EventEmitter = function () {};

var EventEmitter = exports.EventEmitter = process.EventEmitter;
var isArray = typeof Array.isArray === 'function'
    ? Array.isArray
    : function (xs) {
        return Object.prototype.toString.call(xs) === '[object Array]'
    }
;

// By default EventEmitters will print a warning if more than
// 10 listeners are added to it. This is a useful default which
// helps finding memory leaks.
//
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
var defaultMaxListeners = 10;
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!this._events) this._events = {};
  this._events.maxListeners = n;
};

EventEmitter.prototype.emit = function(type) {
  // If there is no 'error' event listener then throw.
  if (type === 'error') {
    if (!this._events || !this._events.error ||
        (isArray(this._events.error) && !this._events.error.length))
    {
      if (arguments[1] instanceof Error) {
        throw arguments[1]; // Unhandled 'error' event
      } else {
        throw new Error("Uncaught, unspecified 'error' event.");
      }
      return false;
    }
  }

  if (!this._events) return false;
  var handler = this._events[type];
  if (!handler) return false;

  if (typeof handler == 'function') {
    switch (arguments.length) {
      // fast cases
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      // slower
      default:
        var args = Array.prototype.slice.call(arguments, 1);
        handler.apply(this, args);
    }
    return true;

  } else if (isArray(handler)) {
    var args = Array.prototype.slice.call(arguments, 1);

    var listeners = handler.slice();
    for (var i = 0, l = listeners.length; i < l; i++) {
      listeners[i].apply(this, args);
    }
    return true;

  } else {
    return false;
  }
};

// EventEmitter is defined in src/node_events.cc
// EventEmitter.prototype.emit() is also defined there.
EventEmitter.prototype.addListener = function(type, listener) {
  if ('function' !== typeof listener) {
    throw new Error('addListener only takes instances of Function');
  }

  if (!this._events) this._events = {};

  // To avoid recursion in the case that type == "newListeners"! Before
  // adding it to the listeners, first emit "newListeners".
  this.emit('newListener', type, listener);

  if (!this._events[type]) {
    // Optimize the case of one listener. Don't need the extra array object.
    this._events[type] = listener;
  } else if (isArray(this._events[type])) {

    // Check for listener leak
    if (!this._events[type].warned) {
      var m;
      if (this._events.maxListeners !== undefined) {
        m = this._events.maxListeners;
      } else {
        m = defaultMaxListeners;
      }

      if (m && m > 0 && this._events[type].length > m) {
        this._events[type].warned = true;
        console.error('(node) warning: possible EventEmitter memory ' +
                      'leak detected. %d listeners added. ' +
                      'Use emitter.setMaxListeners() to increase limit.',
                      this._events[type].length);
        console.trace();
      }
    }

    // If we've already got an array, just append.
    this._events[type].push(listener);
  } else {
    // Adding the second element, need to change to array.
    this._events[type] = [this._events[type], listener];
  }

  return this;
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.once = function(type, listener) {
  var self = this;
  self.on(type, function g() {
    self.removeListener(type, g);
    listener.apply(this, arguments);
  });

  return this;
};

EventEmitter.prototype.removeListener = function(type, listener) {
  if ('function' !== typeof listener) {
    throw new Error('removeListener only takes instances of Function');
  }

  // does not use listeners(), so no side effect of creating _events[type]
  if (!this._events || !this._events[type]) return this;

  var list = this._events[type];

  if (isArray(list)) {
    var i = list.indexOf(listener);
    if (i < 0) return this;
    list.splice(i, 1);
    if (list.length == 0)
      delete this._events[type];
  } else if (this._events[type] === listener) {
    delete this._events[type];
  }

  return this;
};

EventEmitter.prototype.removeAllListeners = function(type) {
  // does not use listeners(), so no side effect of creating _events[type]
  if (type && this._events && this._events[type]) this._events[type] = null;
  return this;
};

EventEmitter.prototype.listeners = function(type) {
  if (!this._events) this._events = {};
  if (!this._events[type]) this._events[type] = [];
  if (!isArray(this._events[type])) {
    this._events[type] = [this._events[type]];
  }
  return this._events[type];
};

});

require.define('/events', '/', function(global, module, exports, require, process, __filename, __dirname) {
// check that events is properly shimmed
require('events');

});

})(require);
<|endoftext|>


<commit_before>import test from 'ava'
import path from 'path'
import fs from 'fs'
import Jimp from 'jimp'

function existsSync (uri) {
  try {
    fs.statSync(uri)
    return true
  }catch (error) {
    return false
  }
}

function rmSync (uri) {
  try {
    fs.unlinkSync(uri)
  }catch (error) {
  }
}

test.afterEach.always(t => {
    // this runs after each test and other test hooks, even if they failed
    //remove created file
    try{
      fs.unlinkSync(t.context.outputPath)
    }catch(err){}
})

test.beforeEach(t => {
  let jamPath = '../dist/rendererCLI.js'
  t.context = {
    jamPath : path.resolve(__dirname, jamPath)
  }
})

test('server side renderer: can take a path to a file as input, generate a render of that 3d file as ouput(stl)', t => {
  // FIXME: can only be run locally (webgl support needed), commented out for now for travisCI
  // see https://github.com/stackgl/headless-gl/blob/master/.travis.yml
  const jamPath = t.context.jamPath
  const inputPath = path.resolve(__dirname, './data/cube.stl')
  const outputPath = path.resolve(__dirname, './test.png')
  const expImagePath = path.resolve(__dirname, './data/exp.cube.stl.png')
  const resolution = '160x120'

  const cmd = `node ${jamPath} ${inputPath} ${resolution} ${outputPath} `
  require('child_process').execSync(cmd, {stdio: [0, 1, 2]})
  // t.deepEqual(meshSource[0], 'fakeModel.stl')
  t.deepEqual(true, existsSync(outputPath))

  return Promise.all([Jimp.read(expImagePath), Jimp.read(outputPath)])
    .then(function (values) {
      const [exp, obs] = values
      const diff = Jimp.diff(exp, obs)
      const dist = Jimp.distance(exp, obs)
      const identical = (dist < 0.15 && diff.percent < 0.15)
      t.deepEqual(true, identical)
      rmSync(outputPath)
    }).catch(function () {
      rmSync(outputPath)
      t.fail('Files are not identical', expImagePath, outputPath)
    })
})

/*
test('server side renderer: can take a path to a file as input, generate a render of that 3d file as ouput(3mf)', t => {
  let jamPath = './rendererCLI.js'
  let inputPath = './data/cube_gears.3mf'// cube_gears.3mf'// dodeca_chain_loop_color.3mf'// pyramid_vertexcolor.3mf'
  let outputPath = './test.png'
  let resolution = '160x120'

  let expImagePath = './data/exp.cube_gears.3mf.png'

  jamPath = path.resolve(__dirname, jamPath)
  inputPath = path.resolve(__dirname, inputPath)
  outputPath = path.resolve(__dirname, outputPath)
  outputPath = path.resolve(outputPath)
  expImagePath = path.resolve(__dirname, expImagePath)

  const cmd = `babel-node ${jamPath} ${inputPath} ${resolution} ${outputPath} `
  require('child_process').execSync(cmd, {stdio: [0, 1, 2]})
  t.deepEqual(true, existsSync(outputPath))

  return Promise.all([Jimp.read(expImagePath), Jimp.read(outputPath)])
    .then(function (values) {
      let [exp, obs] = values
      let diff = Jimp.diff(exp, obs)
      let dist = Jimp.distance(exp, obs)
      const identical = (dist < 0.15 && diff.percent < 0.15)
      t.deepEqual(true, identical)
      rmSync(outputPath)
    }).catch(function () {
      rmSync(outputPath)
      t.fail('Files are not identical', expImagePath, outputPath)
    })
})

test('server side renderer: can take a path to a file as input, generate a render of that 3d file as ouput(obj)', function () {
  let jamPath = './rendererCLI.js'
  let inputPath = './data/cube.obj'
  let outputPath = './test.png'
  let resolution = '160x120'

  let expImagePath = './data/exp.cube.obj.png'

  jamPath = path.resolve(__dirname, jamPath)
  inputPath = path.resolve(__dirname, inputPath)
  outputPath = path.resolve(__dirname, outputPath)
  outputPath = path.resolve(outputPath)
  expImagePath = path.resolve(__dirname, expImagePath)

  const cmd = `babel-node ${jamPath} ${inputPath} ${resolution} ${outputPath} `
  require('child_process').execSync(cmd, {stdio: [0, 1, 2]})

  t.deepEqual(true, existsSync(outputPath))

  return Promise.all([Jimp.read(expImagePath), Jimp.read(outputPath)])
    .then(function (values) {
      let [exp, obs] = values
      let diff = Jimp.diff(exp, obs)
      let dist = Jimp.distance(exp, obs)
      const identical = (dist < 0.15 && diff.percent < 0.15)
      t.deepEqual(true, identical)
      rmSync(outputPath)
    }).catch(function () {
      rmSync(outputPath)
      t.fail('Files are not identical', expImagePath, outputPath)
    })
})

test('server side renderer: can take a path to a file as input, generate a render of that 3d file as ouput(ctm)', function () {
  let jamPath = './rendererCLI.js'
  let inputPath = './data/LeePerry.ctm'
  let outputPath = './test.png'
  let resolution = '160x120'

  let expImagePath = './data/exp.LeePerry.ctm.png'

  jamPath = path.resolve(__dirname, jamPath)
  inputPath = path.resolve(__dirname, inputPath)
  outputPath = path.resolve(__dirname, outputPath)
  outputPath = path.resolve(outputPath)
  expImagePath = path.resolve(__dirname, expImagePath)

  const cmd = `babel-node ${jamPath} ${inputPath} ${resolution} ${outputPath} `
  require('child_process').execSync(cmd, {stdio: [0, 1, 2]})

  t.deepEqual(true, existsSync(outputPath))

  return Promise.all([Jimp.read(expImagePath), Jimp.read(outputPath)])
    .then(function (values) {
      let [exp, obs] = values
      let diff = Jimp.diff(exp, obs)
      let dist = Jimp.distance(exp, obs)
      const identical = (dist < 0.15 && diff.percent < 0.15)
      t.deepEqual(true, identical)
      rmSync(outputPath)
    }).catch(function () {
      rmSync(outputPath)
      t.fail('Files are not identical', expImagePath, outputPath)
    })
})*/
<commit_msg>refactor(tests): test improvements: - better (re) use of context variables - make sure each test can be run in parallel (different output file names) - various minor tweaks<commit_after>import test from 'ava'
import path from 'path'
import fs from 'fs'
import Jimp from 'jimp'

function existsSync (uri) {
  try {
    fs.statSync(uri)
    return true
  } catch (error) {
    return false
  }
}

test.afterEach.always(t => {
  // this runs after each test and other test hooks, even if they failed
  // remove created file
  try {
    console.log('removing file', t.context.outputPath)
    fs.unlinkSync(t.context.outputPath)
  // rmSync(outputPath)
  } catch(err) {}
})

test.beforeEach(t => {
  let jamPath = '../dist/index.js'
  t.context = {
    jamPath: path.resolve(__dirname, jamPath),
    resolution: '160x120'
  }
})

test('server side renderer: can take a path to a file as input, generate a render of that 3d file as ouput(stl)', t => {
  // FIXME: can only be run locally (webgl support needed), commented out for now for travisCI
  // see https://github.com/stackgl/headless-gl/blob/master/.travis.yml
  const jamPath = t.context.jamPath
  const inputPath = path.resolve(__dirname, './data/cube.stl')
  const outputPath = path.resolve(__dirname, './test.stl.png')
  const expImagePath = path.resolve(__dirname, './data/exp.cube.stl.png')
  t.context = Object.assign({}, t.context, {outputPath})

  const cmd = `babel-node ${jamPath} ${inputPath} ${t.context.resolution} ${outputPath} `
  require('child_process').execSync(cmd, {stdio: [0, 1, 2]})
  // t.deepEqual(meshSource[0], 'fakeModel.stl')
  t.deepEqual(true, existsSync(outputPath))

  return Promise.all([Jimp.read(expImagePath), Jimp.read(outputPath)])
    .then(function (values) {
      const [exp, obs] = values
      const diff = Jimp.diff(exp, obs)
      const dist = Jimp.distance(exp, obs)
      const identical = (dist < 0.15 && diff.percent < 0.15)
      t.deepEqual(true, identical)
    }).catch(function () {
    t.fail('Files are not identical', expImagePath, outputPath)
  })
})

test('server side renderer: can take a path to a file as input, generate a render of that 3d file as ouput(3mf)', t => {
  const jamPath = t.context.jamPath
  const inputPath = path.resolve(__dirname, './data/cube_gears.3mf')// cube_gears.3mf'// dodeca_chain_loop_color.3mf'// pyramid_vertexcolor.3mf'
  const outputPath = path.resolve(__dirname, './test.3mf.png')
  const expImagePath = path.resolve(__dirname, './data/exp.cube_gears.3mf.png')
  t.context = Object.assign({}, t.context, {outputPath})

  const cmd = `babel-node ${jamPath} ${inputPath} ${t.context.resolution} ${outputPath} `
  require('child_process').execSync(cmd, {stdio: [0, 1, 2]})
  t.deepEqual(true, existsSync(outputPath))

  return Promise.all([Jimp.read(expImagePath), Jimp.read(outputPath)])
    .then(function (values) {
      let [exp, obs] = values
      let diff = Jimp.diff(exp, obs)
      let dist = Jimp.distance(exp, obs)
      const identical = (dist < 0.15 && diff.percent < 0.15)
      t.deepEqual(true, identical)
      rmSync(outputPath)
    }).catch(function () {
      rmSync(outputPath)
      t.fail('Files are not identical', expImagePath, outputPath)
    })
})

test('server side renderer: can take a path to a file as input, generate a render of that 3d file as ouput(obj)', t => {
  const jamPath = t.context.jamPath
  const inputPath = path.resolve(__dirname, './data/cube.obj')
  const outputPath = path.resolve(__dirname, './test.obj.png')
  const expImagePath = path.resolve(__dirname, './data/exp.cube.obj.png')
  t.context = Object.assign({}, t.context, {outputPath})

  const cmd = `babel-node ${jamPath} ${inputPath} ${t.context.resolution} ${outputPath} `
  require('child_process').execSync(cmd, {stdio: [0, 1, 2]})

  t.deepEqual(true, existsSync(outputPath))

  return Promise.all([Jimp.read(expImagePath), Jimp.read(outputPath)])
    .then(function (values) {
      let [exp, obs] = values
      let diff = Jimp.diff(exp, obs)
      let dist = Jimp.distance(exp, obs)
      const identical = (dist < 0.15 && diff.percent < 0.15)
      t.deepEqual(true, identical)
      rmSync(outputPath)
    }).catch(function () {
      rmSync(outputPath)
      t.fail('Files are not identical', expImagePath, outputPath)
    })
})

test('server side renderer: can take a path to a file as input, generate a render of that 3d file as ouput(ctm)', t => {
  const jamPath = t.context.jamPath
  const inputPath = path.resolve(__dirname, './data/LeePerry.ctm')
  const outputPath = path.resolve(__dirname, './test.ctm.png')
  const expImagePath = path.resolve(__dirname, './data/exp.LeePerry.ctm.png')
  t.context = Object.assign({}, t.context, {outputPath})

  const cmd = `babel-node ${jamPath} ${inputPath} ${t.context.resolution} ${outputPath} `
  require('child_process').execSync(cmd, {stdio: [0, 1, 2]})

  t.deepEqual(true, existsSync(outputPath))

  return Promise.all([Jimp.read(expImagePath), Jimp.read(outputPath)])
    .then(function (values) {
      let [exp, obs] = values
      let diff = Jimp.diff(exp, obs)
      let dist = Jimp.distance(exp, obs)
      const identical = (dist < 0.15 && diff.percent < 0.15)
      t.deepEqual(true, identical)
      rmSync(outputPath)
    }).catch(function () {
      rmSync(outputPath)
      t.fail('Files are not identical', expImagePath, outputPath)
    })
})
<|endoftext|>


<commit_before>var vumigo = require('vumigo_v02');
var fixtures = require('./fixtures_sms_inbound');
var AppTester = vumigo.AppTester;

describe("FamilyConnect SMS app", function() {
    describe("SMS inbound reply test", function() {
        var app;
        var tester;

        beforeEach(function() {
            app = new go.app.GoApp();
            tester = new AppTester(app);

            tester
                .setup.char_limit(160)
                .setup.config.app({
                    name: 'sms_inbound',
                    country_code: '256',  // nigeria
                    channel: '2561234',
                    transport_name: 'aggregator_sms',
                    transport_type: 'sms',
                    testing_today: '2015-04-03',  // testing only
                    testing_message_id: '0170b7bb-978e-4b8a-35d2-662af5b6daee',  // testing only
                    services: {
                        identities: {
                            api_token: 'test_token_identities',
                            url: "http://localhost:8001/api/v1/"
                        },
                        registrations: {
                            api_token: 'test_token_registrations',
                            url: "http://localhost:8002/api/v1/"
                        },
                        voice_content: {
                            api_token: "test_token_voice_content",
                            url: "http://localhost:8004/api/v1/"
                        },
                        subscriptions: {
                            api_token: 'test_token_subscriptions',
                            url: "http://localhost:8005/api/v1/"
                        },
                        message_sender: {
                            api_token: 'test_token_message_sender',
                            url: "http://localhost:8006/api/v1/"
                        }
                    },
                })
                .setup(function(api) {
                    fixtures().forEach(api.http.fixtures.add);
                })
                ;
        });

        describe("when the user sends a STOP message", function() {
            it("should opt them out if contact found", function() {
                return tester
                    .setup.user.addr('0720000222')
                    .inputs('stop and wait for green')
                    .check.interaction({
                        state: 'state_end_opt_out',
                        reply: 'You will no longer receive messages from Hello Mama. Should you ever want to re-subscribe, contact your local community health extension worker'
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [0,1]);
                    })
                    .run();
            });
            it("should report problem if contact not found", function() {
                return tester
                    .setup.user.addr('0720000111')
                    .inputs('stop')
                    .check.interaction({
                        state: 'state_end_unrecognised_opt_out',
                        reply: "We do not recognise your number and can therefore not opt you out."
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [2]);
                    })
                    .run();
            });
        });

        describe("when the user sends a BABY message", function() {
            it("should switch them to baby messages if on prebirth messages", function() {
                return tester
                    .setup.user.addr('0720000222')
                    .inputs('baby')
                    .check.interaction({
                        state: 'state_end_postbirth_subscription',
                        reply: "Congratulations! You will now receive messages relating to your baby."
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [0,4,5,6]);
                    })
                    .run();
            });
            it("should NOT switch them to baby messages if already on postbirth messages", function() {
                return tester
                    .setup.user.addr('0720000333')
                    .inputs('baby is born')
                    .check.interaction({
                        state: 'state_end_already_postbirth',
                        reply: "You are already subscribed to baby messages."
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [7,8,9]);
                    })
                    .run();
            });
            it("should report problem if contact not found", function() {
                return tester
                    .setup.user.addr('0720000111')
                    .inputs('baby')
                    .check.interaction({
                        state: 'state_end_unrecognised_baby',
                        reply: "We do not recognise your number and can therefore not subscribe you to baby messages."
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [2]);
                    })
                    .run();
            });
        });

        describe("when the user sends any other message", function() {
            it("should display helpdesk message", function() {
                return tester
                    .setup.user.addr('0720000111')
                    .inputs('go when the light is green')
                    .check.interaction({
                        state: 'state_end_helpdesk',
                        reply:
                            'Currently no helpdesk functionality is active. Reply STOP to unsubscribe.'
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [3]);
                    })
                    .run();
            });
        });

    });
});
<commit_msg>change nigeria reference to uganda<commit_after>var vumigo = require('vumigo_v02');
var fixtures = require('./fixtures_sms_inbound');
var AppTester = vumigo.AppTester;

describe("FamilyConnect SMS app", function() {
    describe("SMS inbound reply test", function() {
        var app;
        var tester;

        beforeEach(function() {
            app = new go.app.GoApp();
            tester = new AppTester(app);

            tester
                .setup.char_limit(160)
                .setup.config.app({
                    name: 'sms_inbound',
                    country_code: '256',  // uganda
                    channel: '2561234',
                    transport_name: 'aggregator_sms',
                    transport_type: 'sms',
                    testing_today: '2015-04-03',  // testing only
                    testing_message_id: '0170b7bb-978e-4b8a-35d2-662af5b6daee',  // testing only
                    services: {
                        identities: {
                            api_token: 'test_token_identities',
                            url: "http://localhost:8001/api/v1/"
                        },
                        registrations: {
                            api_token: 'test_token_registrations',
                            url: "http://localhost:8002/api/v1/"
                        },
                        voice_content: {
                            api_token: "test_token_voice_content",
                            url: "http://localhost:8004/api/v1/"
                        },
                        subscriptions: {
                            api_token: 'test_token_subscriptions',
                            url: "http://localhost:8005/api/v1/"
                        },
                        message_sender: {
                            api_token: 'test_token_message_sender',
                            url: "http://localhost:8006/api/v1/"
                        }
                    },
                })
                .setup(function(api) {
                    fixtures().forEach(api.http.fixtures.add);
                })
                ;
        });

        describe("when the user sends a STOP message", function() {
            it("should opt them out if contact found", function() {
                return tester
                    .setup.user.addr('0720000222')
                    .inputs('stop and wait for green')
                    .check.interaction({
                        state: 'state_end_opt_out',
                        reply: 'You will no longer receive messages from Hello Mama. Should you ever want to re-subscribe, contact your local community health extension worker'
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [0,1]);
                    })
                    .run();
            });
            it("should report problem if contact not found", function() {
                return tester
                    .setup.user.addr('0720000111')
                    .inputs('stop')
                    .check.interaction({
                        state: 'state_end_unrecognised_opt_out',
                        reply: "We do not recognise your number and can therefore not opt you out."
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [2]);
                    })
                    .run();
            });
        });

        describe("when the user sends a BABY message", function() {
            it("should switch them to baby messages if on prebirth messages", function() {
                return tester
                    .setup.user.addr('0720000222')
                    .inputs('baby')
                    .check.interaction({
                        state: 'state_end_postbirth_subscription',
                        reply: "Congratulations! You will now receive messages relating to your baby."
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [0,4,5,6]);
                    })
                    .run();
            });
            it("should NOT switch them to baby messages if already on postbirth messages", function() {
                return tester
                    .setup.user.addr('0720000333')
                    .inputs('baby is born')
                    .check.interaction({
                        state: 'state_end_already_postbirth',
                        reply: "You are already subscribed to baby messages."
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [7,8,9]);
                    })
                    .run();
            });
            it("should report problem if contact not found", function() {
                return tester
                    .setup.user.addr('0720000111')
                    .inputs('baby')
                    .check.interaction({
                        state: 'state_end_unrecognised_baby',
                        reply: "We do not recognise your number and can therefore not subscribe you to baby messages."
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [2]);
                    })
                    .run();
            });
        });

        describe("when the user sends any other message", function() {
            it("should display helpdesk message", function() {
                return tester
                    .setup.user.addr('0720000111')
                    .inputs('go when the light is green')
                    .check.interaction({
                        state: 'state_end_helpdesk',
                        reply:
                            'Currently no helpdesk functionality is active. Reply STOP to unsubscribe.'
                    })
                    .check(function(api) {
                        go.utils.check_fixtures_used(api, [3]);
                    })
                    .run();
            });
        });

    });
});
<|endoftext|>


<commit_before>import {Observable, Subject} from 'rx';
import {button, div} from '@cycle/dom';
import isolate from '@cycle/isolate'
import Item from './Item';

function intent(DOM, itemActions) {
  const addItem$ = Observable.merge(
    DOM.select('.add-one-btn')
      .events('click').map(() => 1),
    DOM.select('.add-many-btn')
      .events('click').map(() => 1000)
  );
  const removeItem$ = itemActions.destroy$
    .map(id => id);

  return {
    addItem$,
    removeItem$,
  };
}

function model(actions, itemFn) {
  function createRandomItemProps() {
    let hexColor = Math.floor(Math.random() * 16777215).toString(16);
    while (hexColor.length < 6) {
      hexColor = '0' + hexColor;
    }
    hexColor = '#' + hexColor;
    const randomWidth = Math.floor(Math.random() * 800 + 200);
    return {color: hexColor, width: randomWidth};
  }

  let mutableLastId = 0;

  function createNewItem(props) {
    const id = mutableLastId++;
    const sinks = itemFn(props, id);
    sinks.DOM = sinks.DOM.replay(null, 1);
    sinks.DOM.connect();
    sinks.destroy$ = sinks.destroy$.publish();
    sinks.destroy$.connect();
    return {id, DOM: sinks.DOM, destroy$: sinks.destroy$};
  }

  const initialState = [createNewItem({color: 'red', width: 300})]

  const addItemMod$ = actions.addItem$.map(amount => {
    let newItems = [];
    for (let i = 0; i < amount; i++) {
      newItems.push(createNewItem(createRandomItemProps()));
    }
    return function (listItems) {
      return listItems.concat(newItems);
    };
  });

  const removeItemMod$ = actions.removeItem$.map(id =>
    function (listItems) {
      return listItems.filter(item => item.id !== id);
    }
  );

  return Observable.merge(addItemMod$, removeItemMod$)
    .startWith(initialState)
    .scan((listItems, modification) => modification(listItems))
    .publishValue(initialState).refCount();
}

function view(itemDOMs$) {
  function renderTopButtons() {
    return div('.topButtons', [
      button('.add-one-btn', 'Add New Item'),
      button('.add-many-btn', 'Add Many Items')
    ]);
  }

  return itemDOMs$.map(itemDOMs =>
    div('.list',
      [renderTopButtons()].concat(itemDOMs)
    )
  );
}

function makeItemWrapper(DOM) {
  return function itemWrapper(props, id) {
    const propsObservables = {
      color$: Observable.just(props.color),
      width$: Observable.just(props.width),
    };
    const item = isolate(Item, `${id}`)({
      DOM, props: propsObservables
    });
    return {
      DOM: item.DOM,
      destroy$: item.destroy$.map(() => id)
    }
  }
}

function List(sources) {
  const itemActions = {destroy$: new Subject()};
  const actions = intent(sources.DOM, itemActions);
  const itemWrapper = makeItemWrapper(sources.DOM);
  const items$ = model(actions, itemWrapper);
  const itemDOMs$ = items$.map(items => items.map(item => item.DOM));
  const itemDestroy$ = items$
    .filter(items => items.length)
    .flatMapLatest(items =>
      Observable.merge(items.map(item => item.destroy$))
    );
  itemDestroy$.subscribe(itemActions.destroy$.asObserver());
  const vtree$ = view(itemDOMs$);

  return {
    DOM: vtree$
  };
}

export default List;
<commit_msg>refactor(many): remove redundant `scope` argument<commit_after>import {Observable, Subject} from 'rx';
import {button, div} from '@cycle/dom';
import isolate from '@cycle/isolate'
import Item from './Item';

function intent(DOM, itemActions) {
  const addItem$ = Observable.merge(
    DOM.select('.add-one-btn')
      .events('click').map(() => 1),
    DOM.select('.add-many-btn')
      .events('click').map(() => 1000)
  );
  const removeItem$ = itemActions.destroy$
    .map(id => id);

  return {
    addItem$,
    removeItem$,
  };
}

function model(actions, itemFn) {
  function createRandomItemProps() {
    let hexColor = Math.floor(Math.random() * 16777215).toString(16);
    while (hexColor.length < 6) {
      hexColor = '0' + hexColor;
    }
    hexColor = '#' + hexColor;
    const randomWidth = Math.floor(Math.random() * 800 + 200);
    return {color: hexColor, width: randomWidth};
  }

  let mutableLastId = 0;

  function createNewItem(props) {
    const id = mutableLastId++;
    const sinks = itemFn(props, id);
    sinks.DOM = sinks.DOM.replay(null, 1);
    sinks.DOM.connect();
    sinks.destroy$ = sinks.destroy$.publish();
    sinks.destroy$.connect();
    return {id, DOM: sinks.DOM, destroy$: sinks.destroy$};
  }

  const initialState = [createNewItem({color: 'red', width: 300})]

  const addItemMod$ = actions.addItem$.map(amount => {
    let newItems = [];
    for (let i = 0; i < amount; i++) {
      newItems.push(createNewItem(createRandomItemProps()));
    }
    return function (listItems) {
      return listItems.concat(newItems);
    };
  });

  const removeItemMod$ = actions.removeItem$.map(id =>
    function (listItems) {
      return listItems.filter(item => item.id !== id);
    }
  );

  return Observable.merge(addItemMod$, removeItemMod$)
    .startWith(initialState)
    .scan((listItems, modification) => modification(listItems))
    .publishValue(initialState).refCount();
}

function view(itemDOMs$) {
  function renderTopButtons() {
    return div('.topButtons', [
      button('.add-one-btn', 'Add New Item'),
      button('.add-many-btn', 'Add Many Items')
    ]);
  }

  return itemDOMs$.map(itemDOMs =>
    div('.list',
      [renderTopButtons()].concat(itemDOMs)
    )
  );
}

function makeItemWrapper(DOM) {
  return function itemWrapper(props, id) {
    const propsObservables = {
      color$: Observable.just(props.color),
      width$: Observable.just(props.width),
    };
    const item = isolate(Item)({DOM, props: propsObservables});
    return {
      DOM: item.DOM,
      destroy$: item.destroy$.map(() => id)
    }
  }
}

function List(sources) {
  const itemActions = {destroy$: new Subject()};
  const actions = intent(sources.DOM, itemActions);
  const itemWrapper = makeItemWrapper(sources.DOM);
  const items$ = model(actions, itemWrapper);
  const itemDOMs$ = items$.map(items => items.map(item => item.DOM));
  const itemDestroy$ = items$
    .filter(items => items.length)
    .flatMapLatest(items =>
      Observable.merge(items.map(item => item.destroy$))
    );
  itemDestroy$.subscribe(itemActions.destroy$.asObserver());
  const vtree$ = view(itemDOMs$);

  return {
    DOM: vtree$
  };
}

export default List;
<|endoftext|>