/*jslint node: true */ /*global describe, before, beforeEach, it*/ 'use strict'; var _ = require('lodash'); var expect = require('expect.js'); var async = require('async'); var superagentDefaults = require('superagent-defaults'); var request = superagentDefaults(); var conf = require('nconf'); conf.argv().env().file({file: __dirname + '../config.json'}).defaults(); conf.set('port','1337'); // Override normal ENV values with nconf ENV values (ENV values are used the same way without nconf) // FIXME can't get nconf file above to load... process.env.BASE_URL = conf.get("BASE_URL"); process.env.FACEBOOK_KEY = conf.get("FACEBOOK_KEY"); process.env.FACEBOOK_SECRET = conf.get("FACEBOOK_SECRET"); process.env.NODE_DB_URI = 'mongodb://localhost/habitrpg'; var User = require('../src/models/user').model; var Group = require('../src/models/group').model; var Challenge = require('../src/models/challenge').model; var app = require('../src/server'); var shared = require('habitrpg-shared'); // ###### Helpers & Variables ###### var model, uuid, taskPath, baseURL = 'http://localhost:3000/api/v2'; var expectCode = function (res, code) { if (code == 200) expect(res.body.err).to.be(undefined); expect(res.statusCode).to.be(code); }; describe('API', function () { var user, _id, apiToken, username, password; var registerNewUser = function(cb, main) { if (main === undefined) main = true; var randomID = shared.uuid(); if (main) { username = password = randomID; } request.post(baseURL + "/register") .set('Accept', 'application/json') .send({ username: randomID, password: randomID, confirmPassword: randomID, email: randomID + "@gmail.com" }) .end(function (res) { if (!main) return cb(null, res.body); _id = res.body._id; apiToken = res.body.apiToken; User.findOne({_id: _id, apiToken: apiToken}, function (err, _user) { expect(err).to.not.be.ok(); user = _user; request .set('Accept', 'application/json') .set('X-API-User', _id) .set('X-API-Key', apiToken); cb(null, res.body); }); }); }; before(function (done) { require('../src/server'); // start the server // then wait for it to do it's thing. TODO make a cb-compatible export of server setTimeout(done, 2000); }); describe('Without token or user id', function () { it('/api/v2/status', function (done) { request.get(baseURL + "/status") .set('Accept', 'application/json') .end(function (res) { expect(res.statusCode).to.be(200); expect(res.body.status).to.be('up'); done(); }); }); it('/api/v2/user', function (done) { request.get(baseURL + "/user") .set('Accept', 'application/json') .end(function (res) { expect(res.statusCode).to.be(401); expect(res.body.err).to.be('You must include a token and uid (user id) in your request'); done(); }); }); }); describe('With token and user id', function () { before(function (done) { registerNewUser(done, true); }); beforeEach(function (done) { User.findById(_id, function (err, _user) { user = _user; shared.wrap(user); done(); }); }); describe('Todos', function(){ it('Archives old todos',function(done){ request.post(baseURL + "/user/batch-update?_v=999") .send([ {op:'addTask',body:{type:'todo'}}, {op:'addTask',body:{type:'todo'}}, {op:'addTask',body:{type:'todo'}} ]) .end(function (res) { expectCode(res, 200); expect(_.size(res.body.todos)).to.be(4); request.post(baseURL + "/user/batch-update?_v=998") .send([ {op:'score',params:{direction:'up', id:res.body.todos[0].id}}, {op:'score',params:{direction:'up', id:res.body.todos[1].id}}, {op:'score',params:{direction:'up', id:res.body.todos[2].id}} ]) .end(function(res){ expectCode(res, 200); expect(_.size(res.body.todos)).to.be(4); request.post(baseURL + "/user/batch-update?_v=997") .send([ {op:'updateTask',params:{id:res.body.todos[0].id}, body:{dateCompleted:moment().subtract('days',4)}}, {op:'updateTask',params:{id:res.body.todos[1].id}, body:{dateCompleted:moment().subtract('days',4)}} ]) .end(function(res){ expect(_.size(res.body.todos)).to.be(2); done(); }) }) }); }); }) /** * GROUPS */ describe('Groups', function () { var group; before(function (done) { registerNewUser(function(){ request.post(baseURL + "/groups") .send({name:"TestGroup", type:"party"}) .end(function (res) { expectCode(res, 200); group = res.body; expect(group.members.length).to.be(1); expect(group.leader).to.be(user._id); done(); }); }); }) describe('Challenges', function () { var challenge, updateTodo; it('Creates a challenge', function (done) { request.post(baseURL + "/challenges") .send({ group: group._id, dailys: [{type:'daily', text:'Challenge Daily'}], todos: [{type:'todo', text:'Challenge Todo', notes:'Challenge Notes'}], rewards: [], habits: [], official: true }) .end(function (res) { expectCode(res, 200); async.parallel([ function (cb) { User.findById(_id, cb); }, function (cb) { Challenge.findById(res.body._id, cb); }, ], function (err, results) { var _user = results[0]; challenge = results[1]; expect(_user.dailys[_user.dailys.length-1].text).to.be('Challenge Daily'); updateTodo = _user.todos[_user.todos.length-1]; expect(updateTodo.text).to.be('Challenge Todo'); expect(challenge.official).to.be(false); done(); }); }); }); it('User updates challenge notes', function (done) { updateTodo.notes = "User overriden notes"; request.put(baseURL + "/user/tasks/" + updateTodo.id) .send(updateTodo) .end(function (res) { done(); // we'll do the check down below }); }); it('Change challenge daily', function (done) { challenge.dailys[0].text = 'Updated Daily'; challenge.todos[0].notes = 'Challenge Updated Todo Notes'; request.post(baseURL + "/challenges/" + challenge._id) .send(challenge) .end(function (res) { setTimeout(function () { User.findById(_id, function (err,_user) { expectCode(res, 200); expect(_user.dailys[_user.dailys.length-1].text).to.be('Updated Daily'); expect(res.body.todos[0].notes).to.be('Challenge Updated Todo Notes'); expect(_user.todos[_user.todos.length-1].notes).to.be('User overriden notes'); user = _user; done(); }); }, 500); // we have to wait a while for users' tasks to be updated, called async on server }); }); it('Shows user notes on challenge page', function (done) { request.get(baseURL + "/challenges/" + challenge._id + "/member/" + _id) .end(function (res) { expect(res.body.todos[res.body.todos.length-1].notes).to.be('User overriden notes'); done(); }); }); it('Complete To-Dos', function (done) { var u = user; request.post(baseURL + "/user/tasks/" + u.todos[0].id + "/up").end(function (res) { request.post(baseURL + "/user/tasks/" + u.todos[1].id + "/up").end(function (res) { request.post(baseURL + "/user/tasks/").send({type:'todo'}).end(function (res) { request.post(baseURL + "/user/tasks/clear-completed").end(function (res) { expect(_.size(res.body)).to.be(2); done(); }); }); }); }); }); it('Admin creates a challenge', function (done) { User.findByIdAndUpdate(_id, {$set:{'contributor.admin':true}}, function (err,_user) { expect(err).to.not.be.ok(); async.parallel([ function (cb) { request.post(baseURL + "/challenges") .send({group:group._id, dailys: [], todos: [], rewards: [], habits: [], official: false}).end(function (res) { expect(res.body.official).to.be(false); cb(); }); }, function (cb) { request.post(baseURL + "/challenges") .send({group:group._id, dailys: [], todos: [], rewards: [], habits: [], official: true}).end(function (res) { expect(res.body.official).to.be(true); cb(); }); }], done); }); }); }); describe('Quests', function () { var party, participating = [], notParticipating = []; it("Tallies progress",function(done){ request.post(baseURL+'/user/batch-update') .send([ {op:'score',params:{direction:'up',id:user.dailys[0].id}}, {op:'score',params:{direction:'up',id:user.dailys[1].id}} ]) .end(function(res){ user = res.body; expect(user.party.quest.progress.up).to.be.above(0); done(); }) }); it('Invites some members', function (done) { async.waterfall([ // Register new users function (cb) { async.parallel([ function (cb2) { registerNewUser(cb2,false); }, function (cb2) { registerNewUser(cb2,false); }, function (cb2) { registerNewUser(cb2,false); } ], cb); }, // Send them invitations function (_party, cb) { party = _party; var inviteURL = baseURL + "/groups/" + group._id + "/invite?uuid="; async.parallel([ function (cb2) { request.post(inviteURL + party[0]._id).end(cb2); }, function (cb2) { request.post(inviteURL + party[1]._id).end(cb2); }, function (cb2) { request.post(inviteURL + party[2]._id).end(cb2); } ], cb); }, // Accept / Reject function (results, cb) { console.log(2); // series since they'll be modifying the same group record var series = _.reduce(party, function (m,v,i) { m.push(function (cb2) { request.post(baseURL+"/groups/"+group._id+"/join") .set('X-API-User', party[i]._id) .set('X-API-Key', party[i].apiToken) .end(function(){cb2();}); }); return m; }, []) async.series(series, cb); }, // Make sure the invites stuck function (whatever, cb) { console.log(3); Group.findById(group._id, function (err, g) { expect(g.members.length).to.be(4); cb(); }); } ], done); }); it('Starts a quest', function (done) { console.log(5); async.waterfall([ function (cb) { request.post(baseURL + "/groups/" + group._id + "/questAccept?key=evilsanta") .end(function (res) { expectCode(res, 401); User.findByIdAndUpdate(_id, {$set: {'items.quests.evilsanta':1}}, cb); }); }, function (_user,cb) { request.post(baseURL + "/groups/" + group._id + "/questAccept?key=evilsanta") .end(function (res) { expectCode(res, 200); Group.findById(group._id, cb); }); }, function (_group,cb) { expect(_group.quest.key).to.be('evilsanta'); expect(_group.quest.active).to.be(false); request.post(baseURL + "/groups/" + group._id + "/questAccept") .set('X-API-User', party[0]._id).set('X-API-Key', party[0].apiToken) .end(function () { request.post(baseURL + "/groups/" + group._id + "/questAccept") .set('X-API-User', party[1]._id).set('X-API-Key', party[1].apiToken) .end(function (res) { request.post(baseURL + "/groups/" + group._id + "/questReject") .set('X-API-User', party[2]._id).set('X-API-Key', party[2].apiToken) .end(function (res) { group = res.body; expect(group.quest.active).to.be(true); cb(); }); }); }); }], done); }); it("Doesn't include people who aren't participating",function(done){ request.get(baseURL+'/groups/'+group._id).end(function(res){ expect(_.size(res.body.quest.members)).to.be(3); done(); }) }); it('Hurts the boss',function(done){ request.post(baseURL+'/user/batch-update') .end(function(res){ user = res.body; var up = user.party.quest.progress.up; expect(up).to.be.above(0); request.post(baseURL+'/user/batch-update') .send([ {op:'score',params:{direction:'up',id:user.dailys[2].id}}, {op:'score',params:{direction:'up',id:user.dailys[3].id}}, {op:'update',body:{lastCron:moment().subtract('days',1)}} // set day to yesterday, cron will then be triggered on next action ]) .end(function(res){ expect(res.body.party.quest.progress.up).to.be.above(up) request.post(baseURL+'/user/batch-update') .end(function(){ request.get(baseURL+'/groups/'+group._id).end(function(res){ expect(res.body.quest.progress.hp).to.be.below(shared.content.quests.evilsanta.boss.hp); done(); }) }) }) }) }) }); }); }); });