Files
habitica/test/api.mocha.js

422 lines
15 KiB
JavaScript

/*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();
})
})
})
})
})
});
});
});
});