<commit_before>var testId = rand()
process.env.MONGODB = 'mongodb://localhost:27017/mutest'+testId
process.env.RETHINKDB_NAME = 'test' + testId

global.WebSocket = require('ws')
global.EventEmitter = require('events').EventEmitter
global._ = require('lodash')
var request = require('supertest')
var mongoose = require('mongoose')
var mongo = require('mongodb')
var assert = require('assert')
global.WebSocketChannel = require('../../browser/scripts/wschannel')
var EditorChannel = require('../../browser/scripts/editorChannel')
var r = require('rethinkdb')
var session = require('client-sessions')
var secrets = require('../../config/secrets');
global.Flux = require('../../browser/vendor/flux')

var setupRethinkDatabase = require('../../tools/setup').setupRethinkDatabase

global.window = {
	location: { hostname: 'localhost', port: 8000 }
}

global.ga = function(){}

function rand() {
	return Math.floor(Math.random() * 100000)
}

var app = require('../../app.js')
var agent = request.agent(app)

function createClient(channelName, lastEditSeen) {
	var dispatcher = new Flux.Dispatcher()
	var chan = new EditorChannel(dispatcher)

	chan.connect({
		headers: {
			'Cookie': 'session='+session.util.encode({
				cookieName: 'session',
				secret: secrets.sessionSecret,
				duration: 4100421,
				activeDuration: 190248
			}, {
				userId: 'test1234' + (''+Date.now()).substring(8,12)
			})
		}
	})
	.on('ready', function() {
		chan.lastEditSeen = lastEditSeen

		if (channelName)
			chan.join(channelName)
	})

	chan.dispatcher = dispatcher

	return chan
}

var rethinkConnection
function setupDatabase() {
	var dbName = process.env.RETHINKDB_NAME

	return r.connect({
		host: 'localhost',
		port: 28015,
		db: dbName
	})
	.then(function(conn) {
		rethinkConnection = conn
		return setupRethinkDatabase()
	})
	.error(function(err) {
		throw err
	})
}

var s1, s2

var numbers = [ 'one', 'two', 'three', 'four', 'five',
	'six', 'seven', 'eight', 'nine', 'ten' ]

function burst() {
	var bn = numbers.slice()
	var interval = setInterval(function() {
		var n = bn.shift()
		if (n) {
			s1.send({
				actionType: 'uiPluginStateChanged',
				number: n
			})
		}else {
			clearInterval(interval)
			s1.close()
		}
	}, 1)
}

describe('Multiuser', function() {
	var db

	before(function(done) {
		global.E2 = {
			app: {
				growl: function() {}
			},
			core: {
				active_graph: { uid: 'root' }
			}
		}

		app._editorChannel.on('ready', function() {
			setupDatabase()
			.then(function() {
				db = new mongo.Db('mutest'+testId,
					new mongo.Server('localhost', 27017),
					{ safe: true })

				db.open(function() {
					done()
				})
			})
		})
	})

	after(function(done) {
		mongoose.models = {}
		mongoose.modelSchemas = {}
		mongoose.connection.close()

		r.dbDrop(process.env.RETHINKDB_NAME)
			.run(rethinkConnection, function() {
				db.dropDatabase()
				db.close()
				rethinkConnection.close()
				done()
			})
	})

	beforeEach(function() {})
	afterEach(function() {
		[s1, s2].map(function(s) {
			if (s) {
				s.close()
				s = null
			}
		})
	})

	it('should connect', function(done) {
		s1 = createClient()
		s1.on('ready', function() {
			done()
		})
	})

	it('should notify two users of each others joins', function(done) {
		s1 = createClient('test1')
		s2 = createClient('test1')

		function checkCondition() {
			if (usersSeen.length < 4)
				return;

			assert.ok(usersSeen.indexOf(s1.uid) > -1)
			assert.ok(usersSeen.indexOf(s2.uid) > -1)

			done()
		}

		var usersSeen = []
		s1.on('join', function(other) {
			usersSeen.push(other.id)
			checkCondition()
		})

		s2.on('join', function(other) {
			usersSeen.push(other.id)
			checkCondition()
		})
	})

	it('sends existing edit log on join', function(done) {
		var channel = 'test2'+Math.random()
		var edits = []

		s1 = createClient(channel)
		var numbers = [ 'one', 'two', 'three', 'four', 'five',
			'six', 'seven', 'eight', 'nine', 'ten' ]

		s1.once('join', function() {
			numbers.map(function(n) {
				s1.send({
					actionType: 'uiPluginStateChanged',
					number: n
				})
			})
			s1.close()
		})

		s1.on('disconnected', function() {
			s2 = createClient(channel)

			s2.dispatcher.register(function(m) {
				if (!m.actionType)
					return;

				edits.push(m)

				if (edits.length === 10) {
					assert.deepEqual(edits.map(function(e) { return e.number }),
						numbers)

					done()
				}
			})
		})
	})

	it('keeps order of live entries', function(done) {
		var channel = 'test3'+Math.random()
		var edits = []

		s1 = createClient(channel)
		s1.once('join', function() {
			s2 = createClient(channel)

			s2.once('join', burst)

			s2.dispatcher.register(function(m) {
				if (!m.actionType)
					return;

				edits.push(m)

				if (edits.length === 10) {
					assert.deepEqual(edits.map(function(e) { return e.number }),
						numbers)

					done()
				}
			})
		})

	})

	it('keeps order of replayed entries', function(done) {
		var channel = 'test3'+Math.random()
		var edits = []

		s1 = createClient(channel)
		s1.once('join', function() {
			burst()
		})

		s1.once('disconnected', function() {
			s2 = createClient(channel)
			s2.dispatcher.register(function(m) {
				if (!m.actionType)
					return;

				edits.push(m)

				if (edits.length === 10) {
					assert.deepEqual(edits.map(function(e) { return e.number }),
						numbers)

					done()
				}
			})
		})

	})

	it('sends log on join, leave, join back', function(done) {
		var channel = 'one-'+Math.random()
		var ogChannel = channel

		s1 = createClient(channel)

		s1.once('join', function() {
			s1.send({
				actionType: 'uiPluginStateChanged',
				number: 1
			})
			s1.close()

			var s3 = createClient(channel)
			s3.once('youJoined', function() {
				// join some other channel
				channel = 'part-two-'+Math.random()

				s3.once('youJoined', function() {
					// join original channel again
					s3.join(ogChannel, function() {
						s3.dispatcher.register(function(m) {
							assert.ok(m.number === 1)
							s3.close()
							done()
						})
					})
				})

				s3.join(channel, function() {})
			})
		})

	})

	it('sends log from where left off', function(done) {
		var channel = 'test'+Math.random()

		s2 = createClient(channel)
		s1 = createClient(channel)

		var firstId, lastId

		s2.dispatcher.register(function(m) {
			if (!firstId)
				firstId = m.id

			lastId = m.id

			// wait until last message
			if (m.number !== 2)
				return;

			// use another client to join with a lastEditSeen set
			var s3 = createClient(channel, firstId)
			s3.once('join', function() {
				// assert that we only get number 2
				s3.dispatcher.register(function(m) {
					assert.notEqual(m.number, 1)
					assert.equal(m.id, lastId)
					s3.close()
					s3.on('disconnected', done)
				})
			})

		})

		// send 1 and 2
		s1.once('join', function() {
			[1,2].forEach(function(n) {
				s1.send({
					actionType: 'uiPluginStateChanged',
					number: n
				})
			})
		})
	})

})

<commit_msg>more careful disconnection in mu test<commit_after>var testId = rand()
process.env.MONGODB = 'mongodb://localhost:27017/mutest'+testId
process.env.RETHINKDB_NAME = 'test' + testId

global.WebSocket = require('ws')
global.EventEmitter = require('events').EventEmitter
global._ = require('lodash')
var request = require('supertest')
var mongoose = require('mongoose')
var mongo = require('mongodb')
var assert = require('assert')
global.WebSocketChannel = require('../../browser/scripts/wschannel')
var EditorChannel = require('../../browser/scripts/editorChannel')
var r = require('rethinkdb')
var session = require('client-sessions')
var secrets = require('../../config/secrets');
global.Flux = require('../../browser/vendor/flux')

var setupRethinkDatabase = require('../../tools/setup').setupRethinkDatabase

global.window = {
	location: { hostname: 'localhost', port: 8000 }
}

global.ga = function(){}

function rand() {
	return Math.floor(Math.random() * 100000)
}

var app = require('../../app.js')
var agent = request.agent(app)

function createClient(channelName, lastEditSeen) {
	var dispatcher = new Flux.Dispatcher()
	var chan = new EditorChannel(dispatcher)

	chan.connect({
		headers: {
			'Cookie': 'session='+session.util.encode({
				cookieName: 'session',
				secret: secrets.sessionSecret,
				duration: 4100421,
				activeDuration: 190248
			}, {
				userId: 'test1234' + (''+Date.now()).substring(8,12)
			})
		}
	})
	.on('ready', function() {
		chan.lastEditSeen = lastEditSeen

		if (channelName)
			chan.join(channelName)
	})

	chan.dispatcher = dispatcher

	return chan
}

var rethinkConnection
function setupDatabase() {
	var dbName = process.env.RETHINKDB_NAME

	return r.connect({
		host: 'localhost',
		port: 28015,
		db: dbName
	})
	.then(function(conn) {
		rethinkConnection = conn
		return setupRethinkDatabase()
	})
	.error(function(err) {
		throw err
	})
}

var s1, s2

var numbers = [ 'one', 'two', 'three', 'four', 'five',
	'six', 'seven', 'eight', 'nine', 'ten' ]

function burst() {
	var bn = numbers.slice()
	var interval = setInterval(function() {
		var n = bn.shift()
		if (n) {
			s1.send({
				actionType: 'uiPluginStateChanged',
				number: n
			})
		}else {
			clearInterval(interval)
			s1.close()
		}
	}, 1)
}

describe('Multiuser', function() {
	var db

	before(function(done) {
		global.E2 = {
			app: {
				growl: function() {}
			},
			core: {
				active_graph: { uid: 'root' }
			}
		}

		app._editorChannel.on('ready', function() {
			setupDatabase()
			.then(function() {
				db = new mongo.Db('mutest'+testId,
					new mongo.Server('localhost', 27017),
					{ safe: true })

				db.open(function() {
					done()
				})
			})
		})
	})

	after(function(done) {
		mongoose.models = {}
		mongoose.modelSchemas = {}
		mongoose.connection.close()

		r.dbDrop(process.env.RETHINKDB_NAME)
			.run(rethinkConnection, function() {
				db.dropDatabase()
				db.close()
				rethinkConnection.close()
				done()
			})
	})

	beforeEach(function() {})
	afterEach(function() {
		[s1, s2].map(function(s) {
			if (s) {
				s.close()
				s = null
			}
		})
	})

	it('should connect', function(done) {
		s1 = createClient()
		s1.on('ready', function() {
			done()
		})
	})

	it('should notify two users of each others joins', function(done) {
		s1 = createClient('test1')
		s2 = createClient('test1')

		function checkCondition() {
			if (usersSeen.length < 4)
				return;

			assert.ok(usersSeen.indexOf(s1.uid) > -1)
			assert.ok(usersSeen.indexOf(s2.uid) > -1)

			done()
		}

		var usersSeen = []
		s1.on('join', function(other) {
			usersSeen.push(other.id)
			checkCondition()
		})

		s2.on('join', function(other) {
			usersSeen.push(other.id)
			checkCondition()
		})
	})

	it('sends existing edit log on join', function(done) {
		var channel = 'test2'+Math.random()
		var edits = []

		s1 = createClient(channel)
		var numbers = [ 'one', 'two', 'three', 'four', 'five',
			'six', 'seven', 'eight', 'nine', 'ten' ]

		s1.once('join', function() {
			numbers.map(function(n) {
				s1.send({
					actionType: 'uiPluginStateChanged',
					number: n
				})
			})
			s1.close()
		})

		s1.on('disconnected', function() {
			s2 = createClient(channel)

			s2.dispatcher.register(function(m) {
				if (!m.actionType)
					return;

				edits.push(m)

				if (edits.length === 10) {
					assert.deepEqual(edits.map(function(e) { return e.number }),
						numbers)

					done()
				}
			})
		})
	})

	it('keeps order of live entries', function(done) {
		var channel = 'test3'+Math.random()
		var edits = []

		s1 = createClient(channel)
		s1.once('join', function() {
			s2 = createClient(channel)

			s2.once('join', burst)

			s2.dispatcher.register(function(m) {
				if (!m.actionType)
					return;

				edits.push(m)

				if (edits.length === 10) {
					assert.deepEqual(edits.map(function(e) { return e.number }),
						numbers)

					done()
				}
			})
		})

	})

	it('keeps order of replayed entries', function(done) {
		var channel = 'test3'+Math.random()
		var edits = []

		s1 = createClient(channel)
		s1.once('join', function() {
			burst()
		})

		s1.once('disconnected', function() {
			s2 = createClient(channel)
			s2.dispatcher.register(function(m) {
				if (!m.actionType)
					return;

				edits.push(m)

				if (edits.length === 10) {
					assert.deepEqual(edits.map(function(e) { return e.number }),
						numbers)

					done()
				}
			})
		})

	})

	it('sends log on join, leave, join back', function(done) {
		var channel = 'one-'+Math.random()
		var ogChannel = channel

		s1 = createClient(channel)

		s1.once('join', function() {
			s1.send({
				actionType: 'uiPluginStateChanged',
				number: 1
			})
			s1.close()

			var s3 = createClient(channel)
			s3.once('youJoined', function() {
				// join some other channel
				channel = 'part-two-'+Math.random()

				s3.once('youJoined', function() {
					// join original channel again
					s3.join(ogChannel, function() {
						s3.dispatcher.register(function(m) {
							assert.ok(m.number === 1)
							s3.close()
							s3.on('disconnected', done)
						})
					})
				})

				s3.join(channel, function() {})
			})
		})

	})

	it('sends log from where left off', function(done) {
		var channel = 'test'+Math.random()

		s2 = createClient(channel)
		s1 = createClient(channel)

		var firstId, lastId

		s2.dispatcher.register(function(m) {
			if (!firstId)
				firstId = m.id

			lastId = m.id

			// wait until last message
			if (m.number !== 2)
				return;

			// use another client to join with a lastEditSeen set
			var s3 = createClient(channel, firstId)
			s3.once('join', function() {
				// assert that we only get number 2
				s3.dispatcher.register(function(m) {
					assert.notEqual(m.number, 1)
					assert.equal(m.id, lastId)
					s3.close()
					s3.on('disconnected', done)
				})
			})

		})

		// send 1 and 2
		s1.once('join', function() {
			[1,2].forEach(function(n) {
				s1.send({
					actionType: 'uiPluginStateChanged',
					number: n
				})
			})
		})
	})

})

<|endoftext|>


<commit_before>/*!
 * rtbkit-js
 * Copyright(c) 2017 Maksim G. Voronin <maxgcrow@gmail.com>
 * MIT Licensed
 */

'use strict';

const chai = require('chai')
    , expect = chai.expect
    , should = chai.should()
    , assert = chai.assert
;
const http = require('http');
const mockup = require('../lib/mockup.js')();

var conf = {
    ports: {
        acs: 9986,
        adserver: {
            win: 12340,
            event: 12341
        },
        banker: 9985
    }
};

describe ('Mockup', function () {
    before(function () {
        mockup.start();
    });

    after(function () {
        mockup.stop();
    });

    describe ('#ACS', function () {
        function get(path, callback) {
            return send('GET', conf.ports.acs, path, '', callback);
        };

        function post(path, data, callback) {
            return send('POST', conf.ports.acs, path, data, callback);
        };

        describe ('.getAgents', function () {
            it('should return a JSON array', function(done) {
                get('/v1/agents/', function (res) {
                    expect(res.headers).to.include.key({'method-name': 'getAgents'});
                    expect(res.statusCode).to.equal(200);
                    let agents = JSON.parse(res.data);
                    expect(agents).to.be.an('Array');
                    done();
                }).on('error', function (err) {
                    should.not.exist(err);
                    done(err);
                });

            });
        });

        describe ('.getAgentConfig', function () {
            it('should return an object', function(done) {
                var agent = 'test-agent';
                get(`/v1/agents/${agent}/config`, function (res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'getAgentConfig'});
                    expect(res.headers).to.include.key({'agent': agent});
                    let config = JSON.parse(res.data);
                    assert(typeof config === 'object', 'config should be an object or null');
                    done();
                }).on('error', function (err) {
                    should.not.exist(err);
                    done(err);
                });

            });
        });

        describe ('.setAgentConfig', function () {
            it('should return HTTP 200', function(done) {
                var agent = 'test-agent';
                var config = JSON.stringify({ account: ['hello', 'world'] });
                post(`/v1/agents/${agent}/config`, config, function (res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'setAgentConfig'});
                    expect(res.headers).to.include.key({'agent': agent});
                    expect(res.headers).to.include.key({'config': config});
                    done();
                }).on('error', function (err) {
                    should.not.exist(err);
                    done(err);
                });

            });
        });

    });
    //----------------------------------------------------------------
    describe ('#AdServer', function () {
        // function get(path, callback) {
        //     return send('GET', conf.ports.acs, path, '', callback);
        // };

        function post(port, data, callback) {
            return send('POST', port, '', data, callback);
        };

        describe ('.win', function () {
            var winData = {
                "timestamp":1365517883.9742889404,
                "bidRequestId":"4BZkWjMDeAWZ",
                "impid":"604356",
                "price":0.8493150684931507,
                "userIds" : ["some-user-id", "another-user-id"]
            };
            it('should return HTTP 200', function(done) {
                post(conf.ports.adserver.win, winData, function (res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'win'});
                    expect(res.headers).to.include.key({'data': JSON.stringify(winData)});
                    done();
                }).on('error', function (err) {
                    should.not.exist(err);
                    done(err);
                });

            });
        });

    });
    //----------------------------------------------------------------
    describe ('#Banker', function () {
        function get(path, callback) {
            return send('GET', conf.ports.banker, path, '', callback);
        };

        function post(path, data, callback) {
            return send('POST', conf.ports.banker, path, data, callback);
        };

        describe ('.ping()', function () {
            it('should return "pong"', function(done) {
                get('/ping', function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.ping'});
                    expect(res).to.have.a.property('data', '"pong"');
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });
        describe ('.summary()', function () {
            it('should return a JSON object', function(done) {
                get('/v1/summary', function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.summary'});
                    expect(res).to.have.a.property('data');
                    let data = JSON.parse(res.data);
                    expect(data).to.be.an('object');
                    //assert(typeof data === 'object', 'config should be an object or null');
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });
        describe ('.accounts()', function () {
            it('should return an Array', function(done) {
                get('/v1/accounts', function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.accounts'});
                    expect(res).to.have.a.property('data');
                    let data = JSON.parse(res.data);
                    expect(data).to.be.an('array');
                        done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });
        describe ('.getAccountSummary()', function () {
            it('should return an Object for a valid name', function(done) {
                var name = 'hello:world';
                get(`/v1/accounts/${name}/summary`, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.getAccountSummary'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res).to.have.a.property('data');
                    let data = JSON.parse(res.data);
                    expect(data).to.be.an('object');
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
            it('should return HTTP 400 for an invalid name', function(done) {
                var name = 'invalid:account';
                get(`/v1/accounts/${name}/summary`, function(res) {
                    expect(res.statusCode).to.equal(400);
                    expect(res.headers).to.include.key({'method-name': 'banker.getAccountSummary'});
                    expect(res.headers).to.include.key({'account-name': name});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

        describe ('.setBudget()', function () {
            var budget = { "USD/1M": 1000000 };
            it('should return HTTP 200 for the top-level account', function(done) {
                var name = 'hello';
                post(`/v1/accounts/${name}/budget`, budget, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.setBudget'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'budget': JSON.stringify(budget)});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
            it('should return HTTP 400 for non-top level accounts', function(done) {
                var name = 'hello:world';
                post(`/v1/accounts/${name}/budget`, budget, function(res) {
                    expect(res.statusCode).to.equal(400);
                    expect(res.headers).to.include.key({'method-name': 'banker.setBudget'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'budget': JSON.stringify(budget)});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

        describe ('.setBalance()', function () {
            var balance = { "USD/1M": 1000000 };
            it('should return HTTP 200 for the non-top-level account', function(done) {
                var name = 'hello:world';
                post(`/v1/accounts/${name}/balance`, balance, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.setBalance'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'balance': JSON.stringify(balance)});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
            it('should return HTTP 400 for top-level accounts', function(done) {
                var name = 'hello';
                post(`/v1/accounts/${name}/balance`, balance, function(res) {
                    expect(res.statusCode).to.equal(400);
                    expect(res.headers).to.include.key({'method-name': 'banker.setBalance'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'balance': JSON.stringify(balance)});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

        describe ('.getAccountChildren()', function () {
            var name = 'hello:world';
            var depth = 10;
            it('should return an Object', function(done) {
                get(`/v1/accounts/${name}/children?depth=${depth}`, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.getAccountChildren'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'depth': depth.toString()});
                    expect(res).to.have.a.property('data');
                    let data = JSON.parse(res.data);
                    expect(data).to.be.an('object');
                        done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

        describe ('.close()', function () {
            it('should return HTTP 200 for an existing account', function(done) {
                var name = 'hello:world';
                get(`/v1/accounts/${name}/close`, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.closeAccount'});
                    expect(res.headers).to.include.key({'account-name': name});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
            it('should return HTTP 400 for non-existing accounts', function(done) {
                var name = 'invalid:account';
                get(`/v1/accounts/${name}/close`, function(res) {
                    expect(res.statusCode).to.equal(400);
                    expect(res.headers).to.include.key({'method-name': 'banker.closeAccount'});
                    expect(res.headers).to.include.key({'account-name': name});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

    });
    //----------------------------------------------------------------
});

function send(method, port, path, data, callback) {
    var body = '';
    if(typeof data === 'string') {
        body = data;
    } else {
        body = JSON.stringify(data);
    }

    var options = {
        host: "127.0.0.1",
        port: port,
        path: path,
        method: method,
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': Buffer.byteLength(body)
        }
    };

    var req = http.request(options, function(res) {
        res.setEncoding('utf8');
        let rawData = '';
        res.on ('data', (chunk) => {
            rawData += chunk;
        }).on('end', function () {
            res.data = rawData;
            callback(res);
        });
    });

    req.write(body);
    req.end();

    return req;
}
<commit_msg>default win&events port<commit_after>/*!
 * rtbkit-js
 * Copyright(c) 2017 Maksim G. Voronin <maxgcrow@gmail.com>
 * MIT Licensed
 */

'use strict';

const chai = require('chai')
    , expect = chai.expect
    , should = chai.should()
    , assert = chai.assert
;
const http = require('http');
const mockup = require('../lib/mockup.js')();

var conf = {
    ports: {
        acs: 9986,
        adserver: {
            win: 18143,
            events: 18144
        },
        banker: 9985
    }
};

describe ('Mockup', function () {
    before(function () {
        mockup.start();
    });

    after(function () {
        mockup.stop();
    });

    describe ('#ACS', function () {
        function get(path, callback) {
            return send('GET', conf.ports.acs, path, '', callback);
        };

        function post(path, data, callback) {
            return send('POST', conf.ports.acs, path, data, callback);
        };

        describe ('.getAgents', function () {
            it('should return a JSON array', function(done) {
                get('/v1/agents/', function (res) {
                    expect(res.headers).to.include.key({'method-name': 'getAgents'});
                    expect(res.statusCode).to.equal(200);
                    let agents = JSON.parse(res.data);
                    expect(agents).to.be.an('Array');
                    done();
                }).on('error', function (err) {
                    should.not.exist(err);
                    done(err);
                });

            });
        });

        describe ('.getAgentConfig', function () {
            it('should return an object', function(done) {
                var agent = 'test-agent';
                get(`/v1/agents/${agent}/config`, function (res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'getAgentConfig'});
                    expect(res.headers).to.include.key({'agent': agent});
                    let config = JSON.parse(res.data);
                    assert(typeof config === 'object', 'config should be an object or null');
                    done();
                }).on('error', function (err) {
                    should.not.exist(err);
                    done(err);
                });

            });
        });

        describe ('.setAgentConfig', function () {
            it('should return HTTP 200', function(done) {
                var agent = 'test-agent';
                var config = JSON.stringify({ account: ['hello', 'world'] });
                post(`/v1/agents/${agent}/config`, config, function (res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'setAgentConfig'});
                    expect(res.headers).to.include.key({'agent': agent});
                    expect(res.headers).to.include.key({'config': config});
                    done();
                }).on('error', function (err) {
                    should.not.exist(err);
                    done(err);
                });

            });
        });

    });
    //----------------------------------------------------------------
    describe ('#AdServer', function () {
        // function get(path, callback) {
        //     return send('GET', conf.ports.acs, path, '', callback);
        // };

        function post(port, data, callback) {
            return send('POST', port, '', data, callback);
        };

        describe ('.win', function () {
            var winData = {
                "timestamp":1365517883.9742889404,
                "bidRequestId":"4BZkWjMDeAWZ",
                "impid":"604356",
                "price":0.8493150684931507,
                "userIds" : ["some-user-id", "another-user-id"]
            };
            it('should return HTTP 200', function(done) {
                post(conf.ports.adserver.win, winData, function (res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'win'});
                    expect(res.headers).to.include.key({'data': JSON.stringify(winData)});
                    done();
                }).on('error', function (err) {
                    should.not.exist(err);
                    done(err);
                });

            });
        });

    });
    //----------------------------------------------------------------
    describe ('#Banker', function () {
        function get(path, callback) {
            return send('GET', conf.ports.banker, path, '', callback);
        };

        function post(path, data, callback) {
            return send('POST', conf.ports.banker, path, data, callback);
        };

        describe ('.ping()', function () {
            it('should return "pong"', function(done) {
                get('/ping', function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.ping'});
                    expect(res).to.have.a.property('data', '"pong"');
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });
        describe ('.summary()', function () {
            it('should return a JSON object', function(done) {
                get('/v1/summary', function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.summary'});
                    expect(res).to.have.a.property('data');
                    let data = JSON.parse(res.data);
                    expect(data).to.be.an('object');
                    //assert(typeof data === 'object', 'config should be an object or null');
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });
        describe ('.accounts()', function () {
            it('should return an Array', function(done) {
                get('/v1/accounts', function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.accounts'});
                    expect(res).to.have.a.property('data');
                    let data = JSON.parse(res.data);
                    expect(data).to.be.an('array');
                        done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });
        describe ('.getAccountSummary()', function () {
            it('should return an Object for a valid name', function(done) {
                var name = 'hello:world';
                get(`/v1/accounts/${name}/summary`, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.getAccountSummary'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res).to.have.a.property('data');
                    let data = JSON.parse(res.data);
                    expect(data).to.be.an('object');
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
            it('should return HTTP 400 for an invalid name', function(done) {
                var name = 'invalid:account';
                get(`/v1/accounts/${name}/summary`, function(res) {
                    expect(res.statusCode).to.equal(400);
                    expect(res.headers).to.include.key({'method-name': 'banker.getAccountSummary'});
                    expect(res.headers).to.include.key({'account-name': name});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

        describe ('.setBudget()', function () {
            var budget = { "USD/1M": 1000000 };
            it('should return HTTP 200 for the top-level account', function(done) {
                var name = 'hello';
                post(`/v1/accounts/${name}/budget`, budget, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.setBudget'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'budget': JSON.stringify(budget)});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
            it('should return HTTP 400 for non-top level accounts', function(done) {
                var name = 'hello:world';
                post(`/v1/accounts/${name}/budget`, budget, function(res) {
                    expect(res.statusCode).to.equal(400);
                    expect(res.headers).to.include.key({'method-name': 'banker.setBudget'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'budget': JSON.stringify(budget)});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

        describe ('.setBalance()', function () {
            var balance = { "USD/1M": 1000000 };
            it('should return HTTP 200 for the non-top-level account', function(done) {
                var name = 'hello:world';
                post(`/v1/accounts/${name}/balance`, balance, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.setBalance'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'balance': JSON.stringify(balance)});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
            it('should return HTTP 400 for top-level accounts', function(done) {
                var name = 'hello';
                post(`/v1/accounts/${name}/balance`, balance, function(res) {
                    expect(res.statusCode).to.equal(400);
                    expect(res.headers).to.include.key({'method-name': 'banker.setBalance'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'balance': JSON.stringify(balance)});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

        describe ('.getAccountChildren()', function () {
            var name = 'hello:world';
            var depth = 10;
            it('should return an Object', function(done) {
                get(`/v1/accounts/${name}/children?depth=${depth}`, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.getAccountChildren'});
                    expect(res.headers).to.include.key({'account-name': name});
                    expect(res.headers).to.include.key({'depth': depth.toString()});
                    expect(res).to.have.a.property('data');
                    let data = JSON.parse(res.data);
                    expect(data).to.be.an('object');
                        done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

        describe ('.close()', function () {
            it('should return HTTP 200 for an existing account', function(done) {
                var name = 'hello:world';
                get(`/v1/accounts/${name}/close`, function(res) {
                    expect(res.statusCode).to.equal(200);
                    expect(res.headers).to.include.key({'method-name': 'banker.closeAccount'});
                    expect(res.headers).to.include.key({'account-name': name});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
            it('should return HTTP 400 for non-existing accounts', function(done) {
                var name = 'invalid:account';
                get(`/v1/accounts/${name}/close`, function(res) {
                    expect(res.statusCode).to.equal(400);
                    expect(res.headers).to.include.key({'method-name': 'banker.closeAccount'});
                    expect(res.headers).to.include.key({'account-name': name});
                    done();
                }).on('error', function(err) {
                    should.not.exist(err);
                    done(err);
                })
            });
        });

    });
    //----------------------------------------------------------------
});

function send(method, port, path, data, callback) {
    var body = '';
    if(typeof data === 'string') {
        body = data;
    } else {
        body = JSON.stringify(data);
    }

    var options = {
        host: "127.0.0.1",
        port: port,
        path: path,
        method: method,
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': Buffer.byteLength(body)
        }
    };

    var req = http.request(options, function(res) {
        res.setEncoding('utf8');
        let rawData = '';
        res.on ('data', (chunk) => {
            rawData += chunk;
        }).on('end', function () {
            res.data = rawData;
            callback(res);
        });
    });

    req.write(body);
    req.end();

    return req;
}
<|endoftext|>


<commit_before>import test from 'ava'
import { resolve } from 'path'
import rp from 'request-promise-native'

const port = 4006
const url = (route) => 'http://localhost:' + port + route

let nuxt = null
let server = null

// Init nuxt.js and create server listening on localhost:4000
test.before('Init Nuxt.js', async t => {
  const Nuxt = require('../')
  const rootDir = resolve(__dirname, 'fixtures/module')
  let config = require(resolve(rootDir, 'nuxt.config.js'))
  config.rootDir = rootDir
  config.dev = false
  nuxt = await new Nuxt(config)
  await nuxt.build()
  server = new nuxt.Server(nuxt)
  server.listen(port, 'localhost')
})

test('Vendor', async t => {
  t.true(nuxt.options.build.vendor.indexOf('lodash') !== -1, 'lodash added to config')
})

test('Plugin', async t => {
  t.true(nuxt.options.plugins[0].src.includes('~/.nuxt/basic.reverse.'), 'plugin added to config')
  const { html } = await nuxt.renderRoute('/')
  t.true(html.includes('
TXUN
'), 'plugin works')
})

test('Middleware', async t => {
  let response = await rp(url('/api'))
  t.is(response, 'It works!', '/api response is correct')
})

// Close server and ask nuxt to stop listening to file changes
test.after('Closing server and nuxt.js', t => {
  server.close()
  nuxt.close()
})
<commit_msg>test: fix appveyor<commit_after>import test from 'ava'
import { resolve } from 'path'
import rp from 'request-promise-native'

const port = 4006
const url = (route) => 'http://localhost:' + port + route

let nuxt = null
let server = null

const wp = p => /^win/.test(process.platform) ? p.replace(/[\\/]/g, '\\\\') : p

// Init nuxt.js and create server listening on localhost:4000
test.before('Init Nuxt.js', async t => {
  const Nuxt = require('../')
  const rootDir = resolve(__dirname, 'fixtures/module')
  let config = require(resolve(rootDir, 'nuxt.config.js'))
  config.rootDir = rootDir
  config.dev = false
  nuxt = await new Nuxt(config)
  await nuxt.build()
  server = new nuxt.Server(nuxt)
  server.listen(port, 'localhost')
})

test('Vendor', async t => {
  t.true(nuxt.options.build.vendor.indexOf('lodash') !== -1, 'lodash added to config')
})

test('Plugin', async t => {
  t.true(nuxt.options.plugins[0].src.includes(wp('fixtures/module/.nuxt/basic.reverse.')), 'plugin added to config')
  const { html } = await nuxt.renderRoute('/')
  t.true(html.includes('
TXUN
'), 'plugin works')
})

test('Middleware', async t => {
  let response = await rp(url('/api'))
  t.is(response, 'It works!', '/api response is correct')
})

// Close server and ask nuxt to stop listening to file changes
test.after('Closing server and nuxt.js', t => {
  server.close()
  nuxt.close()
})
<|endoftext|>


<commit_before>var exec = require('child_process').exec;
var async = require('async');

// The adapters being tested
var adapters = ['sails-postgresql', 'sails-memory', 'sails-disk', 'sails-mongo', 'sails-mysql'];

var status = {};
process.env.FORCE_COLORS = true;

console.time('total time elapsed');

var resultTable = "\n";
resultTable += " --------------------------------------------- \n";
resultTable += "| adapter          | status  | failed | total |\n";
resultTable += "|------------------|---------|--------|-------|\n";

async.eachSeries(adapters, function(adapterName, next){
  status[adapterName] = { failed: 0, total: 0, exitCode: 0 };

  console.log("\n");
  console.log("\033[0;34m-------------------------------------------------------------------------------------------\033[0m");
  console.log("\033[0;34m                                     %s \033[0m", adapterName);
  console.log("\033[0;34m-------------------------------------------------------------------------------------------\033[0m");
  console.log();

  var child = exec('node ./test/integration/runner.js ' + adapterName, { env: process.env });
  child.stdout.on('data', function(data) {
    if(isDot(data)) { status[adapterName].total++; }
    process.stdout.write(data);
  });
  child.stderr.on('data', function(data) {
    if(isDot(data)) {
      status[adapterName].total++;
      status[adapterName].failed++;
    }
    process.stdout.write(data);
  });
  child.on('close', function(code) {
    status[adapterName].exitCode = code;
    var message = code == 0 ? "\033[0;32msuccess\033[0m" : "\033[0;31mfailed \033[0m";
    resultTable += "| " + padRight(adapterName, 16)
      + " | " + message
      + " | " + padLeft(status[adapterName].failed, 6)
      + " | " + padLeft(status[adapterName].total, 5)
      + " |\n";

    console.log('exit code: ' + code);
    next();
  });
},
function(){
  resultTable += " --------------------------------------------- \n";
  console.log(resultTable);
  console.timeEnd('total time elapsed');
});

/**
 * Aux functions
 */
function isDot(data){
  return data == '․' || (data.length === 10 /*&& data[0] === '\u001b'*/ && data.charAt(5) === '․'.charAt(0));
}

function padRight(str, padding){
  var res = "" + str;
  for(var i=res.length; i<padding; i++){
    res += ' ';
  }
  return res;
}

function padLeft(str, padding){
  str = str + "";
  var pad = "";
  for(var i=str.length; i<padding; i++){
    pad += ' ';
  }
  return pad + str;
};
<commit_msg>integration tests: if a tests fails return code different than 0<commit_after>var exec = require('child_process').exec;
var async = require('async');

// The adapters being tested
var adapters = ['sails-postgresql', 'sails-memory', 'sails-disk', 'sails-mongo', 'sails-mysql'];

var status = {};
process.env.FORCE_COLORS = true;
var exitCode = 0;

console.time('total time elapsed');

var resultTable = "\n";
resultTable += " --------------------------------------------- \n";
resultTable += "| adapter          | status  | failed | total |\n";
resultTable += "|------------------|---------|--------|-------|\n";

async.eachSeries(adapters, function(adapterName, next){
  status[adapterName] = { failed: 0, total: 0, exitCode: 0 };

  console.log("\n");
  console.log("\033[0;34m-------------------------------------------------------------------------------------------\033[0m");
  console.log("\033[0;34m                                     %s \033[0m", adapterName);
  console.log("\033[0;34m-------------------------------------------------------------------------------------------\033[0m");
  console.log();

  var child = exec('node ./test/integration/runner.js ' + adapterName, { env: process.env });
  child.stdout.on('data', function(data) {
    if(isDot(data)) { status[adapterName].total++; }
    process.stdout.write(data);
  });
  child.stderr.on('data', function(data) {
    if(isDot(data)) {
      status[adapterName].total++;
      status[adapterName].failed++;
    }
    process.stdout.write(data);
  });
  child.on('close', function(code) {
    status[adapterName].exitCode = code;
    var message = code == 0 ? "\033[0;32msuccess\033[0m" : "\033[0;31mfailed \033[0m";
    resultTable += "| " + padRight(adapterName, 16)
      + " | " + message
      + " | " + padLeft(status[adapterName].failed, 6)
      + " | " + padLeft(status[adapterName].total, 5)
      + " |\n";

    console.log('exit code: ' + code);
    if(code != 0) { exitCode = code; }
    next();
  });
},
function(){
  resultTable += " --------------------------------------------- \n";
  console.log(resultTable);
  console.timeEnd('total time elapsed');
  process.exit(exitCode);
});

/**
 * Aux functions
 */
function isDot(data){
  return data == '․' || (data.length === 10 /*&& data[0] === '\u001b'*/ && data.charAt(5) === '․'.charAt(0));
}

function padRight(str, padding){
  var res = "" + str;
  for(var i=res.length; i<padding; i++){
    res += ' ';
  }
  return res;
}

function padLeft(str, padding){
  str = str + "";
  var pad = "";
  for(var i=str.length; i<padding; i++){
    pad += ' ';
  }
  return pad + str;
};
<|endoftext|>


<commit_before>import should from 'should/as-function';
import Syncano from '../../src/syncano';
import {ValidationError} from '../../src/errors';
import {suffix, credentials, createCleaner} from './utils';

describe('ScriptTrace', function() {
  this.timeout(15000);

  const cleaner = createCleaner();
  let connection = null;
  let Model = null;
  let Instance = null;

  const instanceName = suffix.get('ScriptTrace');
  const data = {
    instanceName,
  };

  before(function() {
    connection = Syncano(credentials.getCredentials());
    Instance = connection.Instance;
    Model = connection.ScriptTrace;

    return Instance.please().create({name: instanceName}).then(() => {
      return connection.Script.please().create({
        instanceName: instanceName,
        label: instanceName,
        runtime_name: 'python_library_v4.2',
        source: 'print "x"'
      });
    }).then((script) => {
      data.scriptId = script.id;
      return script.run();
    }).then((trace) => {
      console.log('trace', trace);
      data.id = trace.id;
    });
  });

  after(function() {
    return Instance.please().delete({name: instanceName});
  });

  afterEach(function() {
    return cleaner.clean();
  });

  it('should be validated', function() {
    should(Model().save()).be.rejectedWith(ValidationError);
  });

  it('should require "instanceName"', function() {
    should(Model().save()).be.rejectedWith(/instanceName/);
  });

  it('should require "scriptId"', function() {
    should(Model({instanceName}).save()).be.rejectedWith(/scriptId/);
  });

  describe('#please()', function() {

    it('should be able to list objects', function() {
      return Model.please().list(data).then((objects) => {
        should(objects).be.an.Array();
      });
    });

    it('should be able to get an object', function() {
      return Model.please().get(data).then((object) => {
        should(object).be.a.Object();
        should(object).have.property('id').which.is.Number();
        should(object).have.property('status').which.is.String();
        should(object).have.property('instanceName').which.is.String().equal(instanceName);
        should(object).have.property('scriptId').which.is.Number().equal(data.scriptId);
        should(object).have.property('executed_at');
        should(object).have.property('duration');
        should(object).have.property('links').which.is.Object();
        should(object).have.property('result').which.is.Object();
      });
    });

    it('should be able to get first object (SUCCESS)', function() {
      return Model.please().first(data).then((object) => {
        should(object).be.an.Object();
      });
    });

    it('should be able to change page size', function() {
      return Model.please(data).pageSize(1).then((objects) => {
        should(objects).be.an.Array().with.length(1);
      });
    });

    it('should be able to get raw data', function() {
      return Model.please().list(data).raw().then((response) => {
        should(response).be.a.Object();
        should(response).have.property('objects').which.is.Array();
        should(response).have.property('next').which.is.null();
        should(response).have.property('prev').which.is.null();
      });
    });

  });

});
<commit_msg>[LIB-704] lint<commit_after>import should from 'should/as-function';
import Syncano from '../../src/syncano';
import {ValidationError} from '../../src/errors';
import {suffix, credentials, createCleaner} from './utils';

describe('ScriptTrace', function() {
  this.timeout(15000);

  const cleaner = createCleaner();
  let connection = null;
  let Model = null;
  let Instance = null;

  const instanceName = suffix.get('ScriptTrace');
  const data = {instanceName};

  before(function() {
    connection = Syncano(credentials.getCredentials());
    Instance = connection.Instance;
    Model = connection.ScriptTrace;

    return Instance.please().create({name: instanceName}).then(() => {
      return connection.Script.please().create({
        instanceName: instanceName,
        label: instanceName,
        runtime_name: 'python_library_v4.2',
        source: 'print "x"'
      });
    }).then((script) => {
      data.scriptId = script.id;
      return script.run();
    }).then((trace) => {
      data.id = trace.id;
    });
  });

  after(function() {
    return Instance.please().delete({name: instanceName});
  });

  afterEach(function() {
    return cleaner.clean();
  });

  it('should be validated', function() {
    should(Model().save()).be.rejectedWith(ValidationError);
  });

  it('should require "instanceName"', function() {
    should(Model().save()).be.rejectedWith(/instanceName/);
  });

  it('should require "scriptId"', function() {
    should(Model({instanceName}).save()).be.rejectedWith(/scriptId/);
  });

  describe('#please()', function() {

    it('should be able to list objects', function() {
      return Model.please().list(data).then((objects) => {
        should(objects).be.an.Array();
      });
    });

    it('should be able to get an object', function() {
      return Model.please().get(data).then((object) => {
        should(object).be.a.Object();
        should(object).have.property('id').which.is.Number();
        should(object).have.property('status').which.is.String();
        should(object).have.property('instanceName').which.is.String().equal(instanceName);
        should(object).have.property('scriptId').which.is.Number().equal(data.scriptId);
        should(object).have.property('executed_at');
        should(object).have.property('duration');
        should(object).have.property('links').which.is.Object();
        should(object).have.property('result').which.is.Object();
      });
    });

    it('should be able to get first object (SUCCESS)', function() {
      return Model.please().first(data).then((object) => {
        should(object).be.an.Object();
      });
    });

    it('should be able to change page size', function() {
      return Model.please(data).pageSize(1).then((objects) => {
        should(objects).be.an.Array().with.length(1);
      });
    });

    it('should be able to get raw data', function() {
      return Model.please().list(data).raw().then((response) => {
        should(response).be.a.Object();
        should(response).have.property('objects').which.is.Array();
        should(response).have.property('next').which.is.null();
        should(response).have.property('prev').which.is.null();
      });
    });

  });

});
<|endoftext|>


<commit_before>'use strict';

var Autoconnector = require('./apis/Autoconnector');
var autoconnector = new Autoconnector();
var BatchProcessor = require('./apis/BatchProcessor');
var batchProcessor = new BatchProcessor();
var PartialFiller = require('./apis/PartialFiller');
var partialFiller = new PartialFiller();
var intervalId;
var AUTOCONNECTOR_INTERVAL = 60000;

var autoconnectorRunErrorHandler = function (err) {
    if (err) return console.error('Autoconnector run error: ' + err);
};

exports.batchProcess = function (callback) {

    batchProcessor.run(callback);
};

exports.runAutoconnector = function (errorHandler) {

    if (!errorHandler) {
        errorHandler = autoconnectorRunErrorHandler;
    }

    autoconnector.run(errorHandler);
};

exports.startInterval = function () {

    console.log('starting periodic jobs');
    intervalId = setInterval(function () {

        var randomNumber = Math.ceil((Math.random() * 10));

        // most of the time, run the autoconnector
        if (randomNumber > 2) {
            exports.runAutoconnector();
        } else if (randomNumber == 2) {
            // but sometimes, do the batch rollup
            exports.batchProcess();
        } else {
            // and sometimes fill partials
            console.log('filling partials...');
            partialFiller.run(function (err) {
                if (err) console.error('Partial filling error: ' + err);
            });
        }

    }, AUTOCONNECTOR_INTERVAL);
};

exports.stopInterval = function () {

    console.log('stopping periodic jobs');
    clearInterval(intervalId);
};
<commit_msg>Added logging<commit_after>'use strict';

var Autoconnector = require('./apis/Autoconnector');
var autoconnector = new Autoconnector();
var BatchProcessor = require('./apis/BatchProcessor');
var batchProcessor = new BatchProcessor();
var PartialFiller = require('./apis/PartialFiller');
var partialFiller = new PartialFiller();
var intervalId;
var AUTOCONNECTOR_INTERVAL = 60000;

var autoconnectorRunErrorHandler = function (err) {
    if (err) return console.error('Autoconnector run error: ' + err);
};

exports.batchProcess = function (callback) {

    batchProcessor.run(callback);
};

exports.runAutoconnector = function (errorHandler) {

    if (!errorHandler) {
        errorHandler = autoconnectorRunErrorHandler;
    }

    autoconnector.run(errorHandler);
};

exports.startInterval = function () {

    console.log('starting periodic jobs');
    intervalId = setInterval(function () {

        var randomNumber = Math.ceil((Math.random() * 10));

        // most of the time, run the autoconnector
        if (randomNumber > 2) {
            console.log('running autoconnector...');
            exports.runAutoconnector();
        } else if (randomNumber == 2) {
            console.log('running batch process...');
            // but sometimes, do the batch rollup
            exports.batchProcess();
        } else {
            // and sometimes fill partials
            console.log('filling partials...');
            partialFiller.run(function (err) {
                if (err) console.error('Partial filling error: ' + err);
            });
        }

    }, AUTOCONNECTOR_INTERVAL);
};

exports.stopInterval = function () {

    console.log('stopping periodic jobs');
    clearInterval(intervalId);
};
<|endoftext|>


<commit_before>var mock = require('./mock');
var JSONOutput = require('../lib/output/json');

describe('JSON Output', function() {

    describe('Sample book', function() {
        var output;

        before(function() {
            return mock.outputDefaultBook(JSONOutput)
            .then(function(_output) {
                output = _output;
            });
        });

        it('should correctly generate a README.json', function() {
            output.should.have.file('README.json');
        });

    });

    describe('Multilingual Book', function() {
        var output;

        before(function() {
            return mock.outputBook(JSONOutput, {
                'LANGS.md': '# Languages\n\n'
                    + '* [en](./en)\n'
                    + '* [fr](./fr)\n\n',
                'en/README.md': '# Hello',
                'fr/README.md': '# Bonjour'

            })
            .then(function(_output) {
                output = _output;
            });
        });

        it('should correctly generate a README.json for each language', function() {
            output.should.have.file('en/README.json');
            output.should.have.file('fr/README.json');
        });

        it('should correctly add languages list to all json', function() {
            var jsonFR = require(output.resolve('fr/README.json'));
            var jsonEN = require(output.resolve('en/README.json'));

            jsonFR.should.have.property('languages')
                .with.property('list').with.lengthOf(2);
            jsonEN.should.have.property('languages')
                .with.property('list').with.lengthOf(2);
        });

        it('should correctly generate a README.json for the whole book', function() {
            output.should.have.file('README.json');

            var json = require(output.resolve('README.json'));

            json.should.have.property('languages')
                .with.property('list').with.lengthOf(2);
        });
    });
});

<commit_msg>Add test for main language in json output<commit_after>var mock = require('./mock');
var JSONOutput = require('../lib/output/json');

describe('JSON Output', function() {

    describe('Sample book', function() {
        var output;

        before(function() {
            return mock.outputDefaultBook(JSONOutput)
            .then(function(_output) {
                output = _output;
            });
        });

        it('should correctly generate a README.json', function() {
            output.should.have.file('README.json');
        });

    });

    describe('Multilingual Book', function() {
        var output;

        before(function() {
            return mock.outputBook(JSONOutput, {
                'LANGS.md': '# Languages\n\n'
                    + '* [en](./en)\n'
                    + '* [fr](./fr)\n\n',
                'en/README.md': '# Hello',
                'fr/README.md': '# Bonjour'

            })
            .then(function(_output) {
                output = _output;
            });
        });

        it('should correctly generate a README.json for each language', function() {
            output.should.have.file('en/README.json');
            output.should.have.file('fr/README.json');
        });

        it('should correctly add languages list to all json', function() {
            var jsonFR = require(output.resolve('fr/README.json'));
            var jsonEN = require(output.resolve('en/README.json'));

            jsonFR.should.have.property('languages')
                .with.property('list').with.lengthOf(2);
            jsonEN.should.have.property('languages')
                .with.property('list').with.lengthOf(2);
        });

        it('should correctly generate a README.json for the whole book', function() {
            output.should.have.file('README.json');
            var json = require(output.resolve('README.json'));

            json.book.language.should.equal('en');

            json.should.have.property('languages')
                .with.property('list').with.lengthOf(2);
        });
    });
});

<|endoftext|>


<commit_before>const db = require('../../../../src/learn/db');
const helper = require('../../../../src/learn/math/drill/helper');
const constants = require('../../../../src/learn/common/constants');
const util = require('../../../../src/learn/common/util');

const { fillArray } = util;

const {
  RECORD_NEW,
  RECORD_EQUAL,
  RECORD_MISS,
  RECORD_NOT_EXIST,
  // BADGE_BOUNDARIES: badgeBoundaries,
  // COLOR_HTML,
} = constants;

describe('Helper', () => {
  beforeEach(() => {
    db.getScores = jest.fn();
    db.appendScore = jest.fn();
  });

  afterEach(() => {
    db.getScores.mockClear();
    db.appendScore.mockClear();
  });

  test('should get a pair of numbers from level #1 plus', () => {
    const pair = helper.getLowerUpper(0, [0]);
    expect(pair[0] === 1 || pair[1] === 1).toBe(true);
  });

  test('should get a random pair of numbers', () => {
    const pair = helper.getLowerUpper(4, [0]);
    expect(pair[0]).toBeLessThan(8);
    expect(pair[1]).toBeLessThan(8);
  });

  test('should default to [] when scores undefined in getScoreBarTimes()', () => {
    db.getScores.mockReturnValueOnce(undefined);

    const actual = helper.getScoreBarTimes(0, [0]);

    const expected = [];

    expect(actual).toEqual(expected);
  });

  test('should getScoreBarTimes() for zero items', () => {
    const scores = [];

    const actual = helper.getScoreBarTimes(0, [0], scores);

    const expected = [];

    expect(actual).toEqual(expected);
  });

  test('should getScoreBarTimes() for single item', () => {
    const scores = [{
      key: '00',
      correctCount: 10,
      date: 1,
      timePerQuestion: 1.3,
    }];

    const actual = helper.getScoreBarTimes(0, [0], scores);

    const expected = [{
      date: 1,
      timePerQuestion: 1.3,
    }];

    expect(actual).toEqual(expected);
  });

  test('should sort getScoreBarTimes() for two items', () => {
    const scores = [{
      key: '00',
      correctCount: 22,
      date: 2,
      timePerQuestion: 2.2,
    }, {
      key: '00',
      correctCount: 11,
      date: 1,
      timePerQuestion: 1.1,
    }];

    const actual = helper.getScoreBarTimes(0, [0], scores);

    const expected = [{
      date: 1,
      timePerQuestion: 1.1,
    }, {
      date: 2,
      timePerQuestion: 2.2,
    }];

    expect(actual).toEqual(expected);
  });

  test(
    'should not return more than 5 items getScoreBarTimes() for six items',
    () => {
      const scores = [{
        key: '00',
        correctCount: 66,
        date: 6,
        timePerQuestion: 6.6,
      }, {
        key: '00',
        correctCount: 55,
        date: 5,
        timePerQuestion: 5.5,
      }, {
        key: '00',
        correctCount: 44,
        date: 4,
        timePerQuestion: 4.4,
      }, {
        key: '00',
        correctCount: 33,
        date: 3,
        timePerQuestion: 3.3,
      }, {
        key: '00',
        correctCount: 22,
        date: 2,
        timePerQuestion: 2.2,
      }, {
        key: '00',
        correctCount: 11,
        date: 1,
        timePerQuestion: 1.1,
      }];

      const actual = helper.getScoreBarTimes(0, [0], scores);

      const expected = [{
        date: 2,
        timePerQuestion: 2.2,
      }, {
        date: 3,
        timePerQuestion: 3.3,
      }, {
        date: 4,
        timePerQuestion: 4.4,
      }, {
        date: 5,
        timePerQuestion: 5.5,
      }, {
        date: 6,
        timePerQuestion: 6.6,
      }];

      expect(actual).toEqual(expected);
    },
  );

  test('should calculate all Answer operators', () => {
    const { calculateAnswer } = helper;
    const inputs = [
      [[6, 3], 0, 9], // 6 + 3 = 9
      [[6, 3], 1, 3], // 6 - 3 = 3
      [[6, 3], 2, 18], // 6 x 3 = 18
      [[6, 3], 3, 2], // 6 / 3 = 2
      [[6, 3], 4, 0], // Unknown operator returns 0
    ];

    inputs.forEach((input) => {
      const [pair, operator, expected] = input;
      const actual = calculateAnswer(pair, operator);
      expect(actual).toBe(expected);
    });
  });

  test('should get current record if data exists', () => {
    db.getScores.mockReturnValueOnce([{
      key: '001',
      timePerQuestion: 2,
    }, {
      key: '002',
      timePerQuestion: 0.5,
    }, {
      key: '001',
      timePerQuestion: 1,
    }]);
    const { getCurrentRecord } = helper;

    const actual = getCurrentRecord(0, [0, 1]);
    expect(actual).toEqual({
      key: '001',
      timePerQuestion: 1,
    });
  });

  test('should get undefined for current record if no data exists', () => {
    db.getScores.mockReturnValueOnce(undefined);
    const { getCurrentRecord } = helper;

    const actual = getCurrentRecord(0, [0, 1]);
    expect(actual).toBeUndefined();
  });

  test('should append score with no existing record', () => {
    db.getScores.mockReturnValueOnce(undefined);
    const { appendScore } = helper;
    const results = {
      correctCount: 10,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'This is the first time you\'ve done this problem. You took \
6 seconds per question. Do this test again to see if you can \
beat this score. Good luck!',
      newRecordInfo: RECORD_NOT_EXIST,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).toHaveBeenCalled();
  });

  test('should not append score if no correct answers', () => {
    db.getScores.mockReturnValueOnce(undefined);
    const { appendScore } = helper;
    const results = {
      correctCount: 0,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'You didn\'t answer any questions correctly so we are not going to use this score \
as part of your high scores. If you are struggling with these problems then try an \
easier level or an easier operator.',
      newRecordInfo: RECORD_MISS,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).not.toHaveBeenCalled();
  });

  test('should report an equal record', () => {
    db.getScores.mockReturnValueOnce([{
      timePerQuestion: 6,
      key: '00',
    }]);
    const { appendScore } = helper;
    const results = {
      correctCount: 10,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'Your time is equal to your best score of 6 seconds per question. Great job!',
      newRecordInfo: RECORD_EQUAL,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).toHaveBeenCalled();
  });

  test('should report a new record', () => {
    db.getScores.mockReturnValueOnce([{
      timePerQuestion: 7,
      key: '00',
    }]);
    const { appendScore } = helper;
    const results = {
      correctCount: 10,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'NEW RECORD! Awesome work! Your new best score is 6 seconds per question.',
      newRecordInfo: RECORD_NEW,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).toHaveBeenCalled();
  });

  test('should report a new record', () => {
    db.getScores.mockReturnValueOnce([{
      timePerQuestion: 5,
      key: '00',
    }]);
    const { appendScore } = helper;
    const results = {
      correctCount: 10,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'You answered the questions at a rate of 6 seconds \
per question. (Your best score is 5 seconds per question.)',
      newRecordInfo: RECORD_MISS,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).toHaveBeenCalled();
  });

  describe('getScoreboard', () => {
    test('should get null, false and 0 for no scores', () => {
      db.getScores.mockReturnValueOnce(undefined);
      const { getScoreboard } = helper;
      const expected = {
        levels: fillArray(26, null),
        ops: fillArray(4, false),
        totals: fillArray(4, 0),
      };
      const actual = getScoreboard();

      expect(actual).toEqual(expected);
    });

    test('should get values for one score', () => {
      db.getScores.mockReturnValueOnce([{
        levelIndex: 0,
        correctCount: 10,
        opIndexes: [0],
        timePerQuestion: 6,
      }]);
      const { getScoreboard } = helper;
      const expected = {
        levels: fillArray(26, null),
        ops: fillArray(4, false),
        totals: fillArray(4, 0),
      };
      expected.levels[0] = [[0, 0, 0, 1]]; // Blue badge for Level A
      expected.ops[0] = true; // Operation 0 - addition
      expected.totals[3] = 1; // One blue (index 3) badge in total

      const actual = getScoreboard();

      expect(actual).toEqual(expected);
    });

    test('should get null, false and 0 for correct answers under 10', () => {
      db.getScores.mockReturnValueOnce([{
        levelIndex: 0,
        correctCount: 9,
        opIndexes: [0],
        timePerQuestion: 6,
      }]);
      const { getScoreboard } = helper;
      const expected = {
        levels: fillArray(26, null),
        ops: fillArray(4, false),
        totals: fillArray(4, 0),
      };
      const actual = getScoreboard();

      expect(actual).toEqual(expected);
    });
  });
});
<commit_msg>Add some tests for newRecords. Issue #223<commit_after>const db = require('../../../../src/learn/db');
const helper = require('../../../../src/learn/math/drill/helper');
const constants = require('../../../../src/learn/common/constants');
const util = require('../../../../src/learn/common/util');

const { fillArray } = util;

const {
  RECORD_NEW,
  RECORD_EQUAL,
  RECORD_MISS,
  RECORD_NOT_EXIST,
  // BADGE_BOUNDARIES: badgeBoundaries,
  // COLOR_HTML,
} = constants;

describe('Helper', () => {
  beforeEach(() => {
    db.getScores = jest.fn();
    db.appendScore = jest.fn();
  });

  afterEach(() => {
    db.getScores.mockClear();
    db.appendScore.mockClear();
  });

  test('should get a pair of numbers from level #1 plus', () => {
    const pair = helper.getLowerUpper(0, [0]);
    expect(pair[0] === 1 || pair[1] === 1).toBe(true);
  });

  test('should get a random pair of numbers', () => {
    const pair = helper.getLowerUpper(4, [0]);
    expect(pair[0]).toBeLessThan(8);
    expect(pair[1]).toBeLessThan(8);
  });

  test('should default to [] when scores undefined in getScoreBarTimes()', () => {
    db.getScores.mockReturnValueOnce(undefined);

    const actual = helper.getScoreBarTimes(0, [0]);

    const expected = [];

    expect(actual).toEqual(expected);
  });

  test('should getScoreBarTimes() for zero items', () => {
    const scores = [];

    const actual = helper.getScoreBarTimes(0, [0], scores);

    const expected = [];

    expect(actual).toEqual(expected);
  });

  test('should getScoreBarTimes() for single item', () => {
    const scores = [{
      key: '00',
      correctCount: 10,
      date: 1,
      timePerQuestion: 1.3,
    }];

    const actual = helper.getScoreBarTimes(0, [0], scores);

    const expected = [{
      date: 1,
      timePerQuestion: 1.3,
    }];

    expect(actual).toEqual(expected);
  });

  test('should sort getScoreBarTimes() for two items', () => {
    const scores = [{
      key: '00',
      correctCount: 22,
      date: 2,
      timePerQuestion: 2.2,
    }, {
      key: '00',
      correctCount: 11,
      date: 1,
      timePerQuestion: 1.1,
    }];

    const actual = helper.getScoreBarTimes(0, [0], scores);

    const expected = [{
      date: 1,
      timePerQuestion: 1.1,
    }, {
      date: 2,
      timePerQuestion: 2.2,
    }];

    expect(actual).toEqual(expected);
  });

  test(
    'should not return more than 5 items getScoreBarTimes() for six items',
    () => {
      const scores = [{
        key: '00',
        correctCount: 66,
        date: 6,
        timePerQuestion: 6.6,
      }, {
        key: '00',
        correctCount: 55,
        date: 5,
        timePerQuestion: 5.5,
      }, {
        key: '00',
        correctCount: 44,
        date: 4,
        timePerQuestion: 4.4,
      }, {
        key: '00',
        correctCount: 33,
        date: 3,
        timePerQuestion: 3.3,
      }, {
        key: '00',
        correctCount: 22,
        date: 2,
        timePerQuestion: 2.2,
      }, {
        key: '00',
        correctCount: 11,
        date: 1,
        timePerQuestion: 1.1,
      }];

      const actual = helper.getScoreBarTimes(0, [0], scores);

      const expected = [{
        date: 2,
        timePerQuestion: 2.2,
      }, {
        date: 3,
        timePerQuestion: 3.3,
      }, {
        date: 4,
        timePerQuestion: 4.4,
      }, {
        date: 5,
        timePerQuestion: 5.5,
      }, {
        date: 6,
        timePerQuestion: 6.6,
      }];

      expect(actual).toEqual(expected);
    },
  );

  test('should calculate all Answer operators', () => {
    const { calculateAnswer } = helper;
    const inputs = [
      [[6, 3], 0, 9], // 6 + 3 = 9
      [[6, 3], 1, 3], // 6 - 3 = 3
      [[6, 3], 2, 18], // 6 x 3 = 18
      [[6, 3], 3, 2], // 6 / 3 = 2
      [[6, 3], 4, 0], // Unknown operator returns 0
    ];

    inputs.forEach((input) => {
      const [pair, operator, expected] = input;
      const actual = calculateAnswer(pair, operator);
      expect(actual).toBe(expected);
    });
  });

  test('should get current record if data exists', () => {
    db.getScores.mockReturnValueOnce([{
      key: '001',
      timePerQuestion: 2,
    }, {
      key: '002',
      timePerQuestion: 0.5,
    }, {
      key: '001',
      timePerQuestion: 1,
    }]);
    const { getCurrentRecord } = helper;

    const actual = getCurrentRecord(0, [0, 1]);
    expect(actual).toEqual({
      key: '001',
      timePerQuestion: 1,
    });
  });

  test('should get undefined for current record if no data exists', () => {
    db.getScores.mockReturnValueOnce(undefined);
    const { getCurrentRecord } = helper;

    const actual = getCurrentRecord(0, [0, 1]);
    expect(actual).toBeUndefined();
  });

  test('should append score with no existing record', () => {
    db.getScores.mockReturnValueOnce(undefined);
    const { appendScore } = helper;
    const results = {
      correctCount: 10,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'This is the first time you\'ve done this problem. You took \
6 seconds per question. Do this test again to see if you can \
beat this score. Good luck!',
      newRecordInfo: RECORD_NOT_EXIST,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).toHaveBeenCalled();
  });

  test('should not append score if no correct answers', () => {
    db.getScores.mockReturnValueOnce(undefined);
    const { appendScore } = helper;
    const results = {
      correctCount: 0,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'You didn\'t answer any questions correctly so we are not going to use this score \
as part of your high scores. If you are struggling with these problems then try an \
easier level or an easier operator.',
      newRecordInfo: RECORD_MISS,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).not.toHaveBeenCalled();
  });

  test('should report an equal record', () => {
    db.getScores.mockReturnValueOnce([{
      timePerQuestion: 6,
      key: '00',
    }]);
    const { appendScore } = helper;
    const results = {
      correctCount: 10,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'Your time is equal to your best score of 6 seconds per question. Great job!',
      newRecordInfo: RECORD_EQUAL,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).toHaveBeenCalled();
  });

  test('should report a new record', () => {
    db.getScores.mockReturnValueOnce([{
      timePerQuestion: 7,
      key: '00',
    }]);
    const { appendScore } = helper;
    const results = {
      correctCount: 10,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'NEW RECORD! Awesome work! Your new best score is 6 seconds per question.',
      newRecordInfo: RECORD_NEW,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).toHaveBeenCalled();
  });

  test('should report a new record', () => {
    db.getScores.mockReturnValueOnce([{
      timePerQuestion: 5,
      key: '00',
    }]);
    const { appendScore } = helper;
    const results = {
      correctCount: 10,
      levelIndex: 0,
      minutes: 10, // in minutes
      opIndexes: [0],
      previousResults: [], // TODO: A test where this is undefined
      questionsRemaining: 0,
      timeLeft: 540, // in seconds
      totalProblems: 10,
    };

    const actual = appendScore(results);
    expect(actual).toEqual({
      // eslint-disable-next-line no-multi-str
      text: 'You answered the questions at a rate of 6 seconds \
per question. (Your best score is 5 seconds per question.)',
      newRecordInfo: RECORD_MISS,
    });
    expect(db.getScores).toHaveBeenCalled();
    expect(db.appendScore).toHaveBeenCalled();
  });

  describe('getScoreboard', () => {
    test('should get null, false and 0 for no scores', () => {
      db.getScores.mockReturnValueOnce(undefined);
      const { getScoreboard } = helper;
      const expected = {
        levels: fillArray(26, null),
        ops: fillArray(4, false),
        totals: fillArray(4, 0),
      };
      const actual = getScoreboard();

      expect(actual).toEqual(expected);
    });

    test('should get values for one score', () => {
      db.getScores.mockReturnValueOnce([{
        levelIndex: 0,
        correctCount: 10,
        opIndexes: [0],
        timePerQuestion: 6,
      }]);
      const { getScoreboard } = helper;
      const expected = {
        levels: fillArray(26, null),
        ops: fillArray(4, false),
        totals: fillArray(4, 0),
      };
      expected.levels[0] = [[0, 0, 0, 1]]; // Blue badge for Level A
      expected.ops[0] = true; // Operation 0 - addition
      expected.totals[3] = 1; // One blue (index 3) badge in total

      const actual = getScoreboard();

      expect(actual).toEqual(expected);
    });

    test('should get null, false and 0 for correct answers under 10', () => {
      db.getScores.mockReturnValueOnce([{
        levelIndex: 0,
        correctCount: 9,
        opIndexes: [0],
        timePerQuestion: 6,
      }]);
      const { getScoreboard } = helper;
      const expected = {
        levels: fillArray(26, null),
        ops: fillArray(4, false),
        totals: fillArray(4, 0),
      };
      const actual = getScoreboard();

      expect(actual).toEqual(expected);
    });
  });

  describe('newRecord()', () => {
    test('should return false and NaN if no data', () => {
      const { newRecord } = helper;
      const input = {
        currentRecord: {},
        previousResults: [],
        startTime: 0,
      };
      const expected = {
        isNewRecord: false,
        currentTimePerQuestion: NaN,
        existingRecordTimePerQuestion: NaN,
      };
      const actual = newRecord(input);

      expect(actual).toEqual(expected);
    });

    test('should return false and NaN if current record missing', () => {
      const { newRecord } = helper;
      const input = {
        previousResults: [],
        startTime: 0,
      };
      const expected = {
        isNewRecord: false,
        currentTimePerQuestion: NaN,
        existingRecordTimePerQuestion: NaN,
      };
      const actual = newRecord(input);

      expect(actual).toEqual(expected);
    });
  });
});
<|endoftext|>


<commit_before>import {buildFilePatch} from '../../../lib/models/patch';
import {assertInFilePatch} from '../../helpers';

describe('FilePatch', function() {
  describe('getStagePatchForLines()', function() {
    it('returns a new FilePatch that applies only the selected lines', function() {
      const filePatch = buildFilePatch([{
        oldPath: 'a.txt',
        oldMode: '100644',
        newPath: 'a.txt',
        newMode: '100644',
        hunks: [
          {
            oldStartLine: 1,
            oldLineCount: 1,
            newStartLine: 1,
            newLineCount: 3,
            lines: [
              '+line-0',
              '+line-1',
              ' line-2',
            ],
          },
          {
            oldStartLine: 5,
            oldLineCount: 5,
            newStartLine: 7,
            newLineCount: 4,
            lines: [
              ' line-3',
              '-line-4',
              '-line-5',
              '+line-6',
              '+line-7',
              '+line-8',
              '-line-9',
              '-line-10',
            ],
          },
          {
            oldStartLine: 20,
            oldLineCount: 2,
            newStartLine: 19,
            newLineCount: 2,
            lines: [
              '-line-11',
              '+line-12',
              ' line-13',
              '\\No newline at end of file',
            ],
          },
        ],
      }]);

      assert.strictEqual(
        filePatch.getBufferText(),
        'line-0\nline-1\nline-2\nline-3\nline-4\nline-5\nline-6\nline-7\nline-8\nline-9\nline-10\n' +
        'line-11\nline-12\nline-13\nNo newline at end of file\n',
      );

      const stagePatch0 = filePatch.getStagePatchForLines(new Set([4, 5, 6]));
      assertInFilePatch(stagePatch0).hunks(
        {
          startRow: 3,
          endRow: 10,
          header: '@@ -5,5 +5,6 @@',
          deletions: {strings: ['*line-4\n*line-5\n'], ranges: [[[4, 0], [5, 0]]]},
          additions: {strings: ['*line-6\n'], ranges: [[[6, 0], [6, 0]]]},
        },
      );

      const stagePatch1 = filePatch.getStagePatchForLines(new Set([0, 4, 5, 6, 11]));
      assertInFilePatch(stagePatch1).hunks(
        {
          startRow: 0,
          endRow: 2,
          header: '@@ -1,1 +1,3 @@',
          additions: {strings: ['*line-0\n'], ranges: [[[0, 0], [0, 0]]]},
        },
        {
          startRow: 3,
          endRow: 10,
          header: '@@ -5,5 +6,6 @@',
          deletions: {strings: ['*line-4\n*line-5\n'], ranges: [[[4, 0], [5, 0]]]},
          additions: {strings: ['*line-6\n'], ranges: [[[6, 0], [6, 0]]]},
        },
        {
          startRow: 11,
          endRow: 14,
          header: '@@ -20,2 +18,3 @@',
          deletions: {strings: ['*line-11\n'], ranges: [[[11, 0], [11, 0]]]},
          noNewline: {string: '*No newline at end of file\n', range: [[14, 0], [14, 0]]},
        },
      );
    });

    describe('staging lines from deleted files', function() {
      it('handles staging part of the file', function() {
        const filePatch = buildFilePatch([{
          oldPath: 'a.txt',
          oldMode: '100644',
          newPath: null,
          newMode: null,
          status: 'deleted',
          hunks: [
            {
              oldStartLine: 1,
              newStartLine: 0,
              oldLineCount: 3,
              newLineCount: 0,
              lines: [
                '-line-1',
                '-line-2',
                '-line-3',
              ],
            },
            {
              oldStartLine: 19,
              newStartLine: 21,
              oldLineCount: 2,
              newLineCount: 2,
              lines: [
                '-line-13',
                '+line-12',
                ' line-14',
                '\\No newline at end of file',
              ],
            },
          ],
        }]);

        assert.strictEqual(filePatch.getBufferText(),
          'line-1\nline-2\nline-3\nline-13\nline-12\nline-14\n' +
          'No newline at end of file\n');

        const stagePatch = filePatch.getStagePatchForLines(new Set([0, 1]));
        assertInFilePatch(stagePatch).hunks(
          {
            startRow: 0,
            endRow: 2,
            header: '@@ -1,3 +0,1 @@',
            deletions: {strings: ['*line-1\n*line-2\n'], ranges: [[[0, 0], [1, 0]]]},
          },
        );
      });

      it('handles staging all lines, leaving nothing unstaged', function() {
        const filePatch = buildFilePatch([{
          oldPath: 'a.txt',
          oldMode: '100644',
          newPath: null,
          newMode: null,
          status: 'deleted',
          hunks: [
            {
              oldStartLine: 1,
              oldLineCount: 3,
              newStartLine: 1,
              newLineCount: 0,
              lines: [
                '-line-1',
                '-line-2',
                '-line-3',
              ],
            },
          ],
        }]);

        assert.strictEqual(filePatch.getBufferText(), 'line-1\nline-2\nline-3\n');

        const stagePatch = filePatch.getStagePatchForLines(new Set([0, 1, 2]));
        assertInFilePatch(stagePatch).hunks(
          {
            startRow: 0,
            endRow: 2,
            header: '@@ -1,3 +1,0 @@',
            deletions: {strings: ['*line-1\n*line-2\n*line-3\n'], ranges: [[[0, 0], [2, 0]]]},
          },
        );
      });
    });
  });

  describe('getUnstagePatchForLines()', function() {
    it('returns a new FilePatch that unstages only the specified lines', function() {
      const filePatch = buildFilePatch([{
        oldPath: 'a.txt',
        oldMode: '100644',
        newPath: 'a.txt',
        newMode: '100644',
        status: 'modified',
        hunks: [
          {
            oldStartLine: 1,
            oldLineCount: 1,
            newStartLine: 1,
            newLineCount: 3,
            lines: [
              '+line-0',
              '+line-1',
              ' line-2',
            ],
          },
          {
            oldStartLine: 5,
            oldLineCount: 5,
            newStartLine: 7,
            newLineCount: 4,
            lines: [
              ' line-3',
              '-line-4',
              '-line-5',
              '+line-6',
              '+line-7',
              '+line-8',
              '-line-9',
              '-line-10',
            ],
          },
          {
            oldStartLine: 20,
            oldLineCount: 2,
            newStartLine: 21,
            newLineCount: 2,
            lines: [
              '-line-11',
              '+line-12',
              ' line-13',
              '\\No newline at end of file',
            ],
          },
        ],
      }]);

      assert.strictEqual(
        filePatch.getBufferText(),
        'line-0\nline-1\nline-2\nline-3\nline-4\nline-5\nline-6\nline-7\nline-8\nline-9\nline-10\n' +
        'line-11\nline-12\nline-13\nNo newline at end of file\n',
      );

      const unstagedPatch0 = filePatch.getUnstagePatchForLines(new Set([4, 5, 6, 7, 11, 12, 13]));
      console.log(unstagedPatch0.toString());
      assertInFilePatch(unstagedPatch0).hunks(
        {
          startRow: 3,
          endRow: 10,
          header: '@@ -7,4 +7,4 @@',
          additions: {strings: ['*line-4\n*line-5\n'], ranges: [[[4, 0], [5, 0]]]},
          deletions: {strings: ['*line-6\n*line-7\n'], ranges: [[[5, 0], [6, 0]]]},
        },
        {
          startRow: 11,
          endRow: 14,
          header: '@@ -19,2 +21,2 @@',
          additions: {strings: ['*line-11\n'], ranges: [[[11, 0], [11, 0]]]},
          deletions: {strings: ['*line-12\n'], ranges: [[[12, 0], [12, 0]]]},
          noNewline: {string: '*No newline at end of file\n', range: [[14, 0], [14, 0]]},
        },
      );
    });

    describe('unstaging lines from an added file', function() {
      it('handles unstaging part of the file');

      it('handles unstaging all lines, leaving nothing staged');
    });
  });

  it('handles newly added files');

  describe('toString()', function() {
    it('converts the patch to the standard textual format');

    it('correctly formats new files with no newline at the end');

    describe('typechange file patches', function() {
      it('handles typechange patches for a symlink replaced with a file');

      it('handles typechange patches for a file replaced with a symlink');
    });
  });

  describe('getHeaderString()', function() {
    it('formats paths with git path separators');
  });

  it('getByteSize() returns the size in bytes');
});
<commit_msg>Start FilePatch tests from scratch<commit_after>import FilePatch from '../../../lib/models/patch/file-patch';
import File from '../../../lib/models/patch/file';
import Patch from '../../../lib/models/patch/patch';
import Hunk from '../../../lib/models/patch/hunk';
import {Addition, Deletion} from '../../../lib/models/patch/region';
import IndexedRowRange from '../../../lib/models/indexed-row-range';
import {assertInFilePatch} from '../../helpers';

describe('FilePatch', function() {
  describe('getStagePatchForLines()', function() {
    it('returns a new FilePatch that applies only the selected lines', function() {
      const bufferText = '0000\n0001\n0002\n0003\n0004\n';
      const hunks = [
        new Hunk({
          oldStartRow: 5,
          oldRowCount: 3,
          newStartRow: 5,
          newRowCount: 4,
          rowRange: new IndexedRowRange({bufferRange: [[0, 0], [4, 0]], startOffset: 0, endOffset: 25}),
          changes: [
            new Addition(new IndexedRowRange({bufferRange: [[1, 0], [2, 0]], startOffset: 5, endOffset: 15})),
            new Deletion(new IndexedRowRange({bufferRange: [[3, 0], [3, 0]], startOffset: 15, endOffset: 20})),
          ],
        }),
      ];
      const patch = new Patch({status: 'modified', hunks, bufferText});
      const oldFile = new File({path: 'file.txt', mode: '100644'});
      const newFile = new File({path: 'file.txt', mode: '100644'});
      const filePatch = new FilePatch(oldFile, newFile, patch);

      const stagedPatch = filePatch.getStagePatchForLines(new Set([1, 3]));
      assert.strictEqual(stagedPatch.getStatus(), 'modified');
      assert.strictEqual(stagedPatch.getOldPath(), 'file.txt');
      assert.strictEqual(stagedPatch.getOldMode(), '100644');
      assert.strictEqual(stagedPatch.getNewPath(), 'file.txt');
      assert.strictEqual(stagedPatch.getNewMode(), '100644');
      assert.strictEqual(stagedPatch.getBufferText(), '0000\n0001\n0003\n0004\n');
      assertInFilePatch(stagedPatch).hunks(
        {
          startRow: 0,
          endRow: 3,
          header: '@@ -5,3 +5,3 @@',
          changes: [
            {kind: 'addition', string: '+0001\n', range: [[1, 0], [1, 0]]},
            {kind: 'deletion', string: '-0003\n', range: [[2, 0], [2, 0]]},
          ],
        },
      );
    });

    describe('staging lines from deleted files', function() {
      it('handles staging part of the file');

      it('handles staging all lines, leaving nothing unstaged');
    });
  });

  describe('getUnstagePatchForLines()', function() {
    it('returns a new FilePatch that unstages only the specified lines');

    describe('unstaging lines from an added file', function() {
      it('handles unstaging part of the file');

      it('handles unstaging all lines, leaving nothing staged');
    });
  });

  it('handles newly added files');

  describe('toString()', function() {
    it('converts the patch to the standard textual format');

    it('correctly formats new files with no newline at the end');

    describe('typechange file patches', function() {
      it('handles typechange patches for a symlink replaced with a file');

      it('handles typechange patches for a file replaced with a symlink');
    });
  });

  describe('getHeaderString()', function() {
    it('formats paths with git path separators');
  });

  it('getByteSize() returns the size in bytes');
});
<|endoftext|>