Ported rebirth. Add unit tests. Added rebirth route. Added integration tests

This commit is contained in:
Keith Holliday
2016-04-07 15:02:55 -05:00
parent 12255181d8
commit fec9ce3dbe
8 changed files with 352 additions and 44 deletions

View File

@@ -158,5 +158,7 @@
"pathRequired": "Path string is required",
"unlocked": "Items have been unlocked",
"alreadyUnlocked": "Item already unlocked",
"cannotRevive": "Cannot revive if not dead"
"cannotRevive": "Cannot revive if not dead",
"rebirthComplete": "You have been reborn!",
"petNotOwned": "You do not own this pet."
}

View File

@@ -124,6 +124,7 @@ import releaseMounts from './ops/releaseMounts';
import sell from './ops/sell';
import unlock from './ops/unlock';
import revive from './ops/revive';
import rebirth from './ops/rebirth';
api.ops = {
scoreTask,
@@ -149,6 +150,7 @@ api.ops = {
sell,
unlock,
revive,
rebirth,
};
import handleTwoHanded from './fns/handleTwoHanded';

View File

@@ -1,21 +1,30 @@
import content from '../content/index';
import i18n from '../i18n';
import _ from 'lodash';
import { capByLevel } from '../statHelpers';
import { MAX_LEVEL } from '../constants';
import {
NotAuthorized,
} from '../libs/errors';
import resetGear from './resetGear';
import equip from './equip';
const USERSTATSLIST = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'];
module.exports = function rebirth (user, tasks = [], req = {}, analytics) {
let analyticsData;
let flags;
let lvl;
let stats;
module.exports = function(user, req, cb, analytics) {
var analyticsData, flags, gear, lvl, stats;
if (user.balance < 2 && user.stats.lvl < MAX_LEVEL) {
return typeof cb === "function" ? cb({
code: 401,
message: i18n.t('notEnoughGems', req.language)
}) : void 0;
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
}
analyticsData = {
uuid: user._id,
category: 'behavior'
category: 'behavior',
};
if (user.stats.lvl < MAX_LEVEL) {
user.balance -= 2;
analyticsData.acquireMethod = 'Gems';
@@ -24,62 +33,52 @@ module.exports = function(user, req, cb, analytics) {
analyticsData.gemCost = 0;
analyticsData.acquireMethod = '> 100';
}
if (analytics != null) {
if (analytics) {
analytics.track('Rebirth', analyticsData);
}
lvl = capByLevel(user.stats.lvl);
_.each(user.tasks, function(task) {
_.each(tasks, function resetTasks (task) {
if (task.type !== 'reward') {
task.value = 0;
}
if (task.type === 'daily') {
return task.streak = 0;
task.streak = 0;
}
});
stats = user.stats;
stats.buffs = {};
stats.hp = 50;
stats.lvl = 1;
stats["class"] = 'warrior';
_.each(['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'], function(value) {
return stats[value] = 0;
});
// TODO during refactoring: move all gear code from rebirth() to its own function and then call it in reset() as well
gear = user.items.gear;
_.each(['equipped', 'costume'], function(type) {
gear[type] = {};
gear[type].armor = 'armor_base_0';
gear[type].weapon = 'weapon_warrior_0';
gear[type].head = 'head_base_0';
return gear[type].shield = 'shield_base_0';
stats.class = 'warrior';
_.each(USERSTATSLIST, function resetStats (value) {
stats[value] = 0;
});
resetGear(user);
if (user.items.currentPet) {
user.ops.equip({
equip(user, {
params: {
type: 'pet',
key: user.items.currentPet
}
key: user.items.currentPet,
},
});
}
if (user.items.currentMount) {
user.ops.equip({
equip(user, {
params: {
type: 'mount',
key: user.items.currentMount
}
key: user.items.currentMount,
},
});
}
_.each(gear.owned, function(v, k) {
if (gear.owned[k] && content.gear.flat[k].value) {
gear.owned[k] = false;
return true;
}
});
gear.owned.weapon_warrior_0 = true;
if (typeof user.markModified === "function") {
user.markModified('items.gear.owned');
}
user.preferences.costume = false;
flags = user.flags;
if (!user.achievements.beastMaster) {
flags.rebirthEnabled = false;
@@ -88,13 +87,21 @@ module.exports = function(user, req, cb, analytics) {
flags.dropsEnabled = false;
flags.classSelected = false;
flags.levelDrops = {};
if (!user.achievements.rebirths) {
user.achievements.rebirths = 1;
user.achievements.rebirthLevel = lvl;
} else if (lvl > user.achievements.rebirthLevel || lvl === 100) {
} else if (lvl > user.achievements.rebirthLevel || lvl === MAX_LEVEL) {
user.achievements.rebirths++;
user.achievements.rebirthLevel = lvl;
}
user.stats.buffs = {};
return typeof cb === "function" ? cb(null, user) : void 0;
let response = {
data: user,
message: i18n.t('rebirthComplete'),
};
return response;
};

View File

@@ -0,0 +1,25 @@
import _ from 'lodash';
import content from '../content/index';
module.exports = function resetGear (user) {
let gear = user.items.gear;
_.each(['equipped', 'costume'], function resetUserGear (type) {
gear[type] = {};
gear[type].armor = 'armor_base_0';
gear[type].weapon = 'weapon_warrior_0';
gear[type].head = 'head_base_0';
gear[type].shield = 'shield_base_0';
});
// Gear.owned is a Mongo object so the _.each function iterates over hidden properties.
// The content.gear.flat[k] check should prevent this causing an error
_.each(gear.owned, function resetOwnedGear (v, k) {
if (gear.owned[k] && content.gear.flat[k] && content.gear.flat[k].value) {
gear.owned[k] = false;
}
});
gear.owned.weapon_warrior_0 = true; // eslint-disable-line camelcase
user.preferences.costume = false;
};

View File

@@ -23,7 +23,6 @@ const COMMON_FILES = [
'!./common/script/ops/deleteWebhook.js',
'!./common/script/ops/getTag.js',
'!./common/script/ops/getTags.js',
'!./common/script/ops/rebirth.js',
'!./common/script/ops/releaseBoth.js',
'!./common/script/ops/releaseMounts.js',
'!./common/script/ops/releasePets.js',

View File

@@ -0,0 +1,56 @@
import {
generateUser,
generateDaily,
generateReward,
translate as t,
} from '../../../../helpers/api-integration/v3';
describe('POST /user/rebirth', () => {
let user;
beforeEach(async () => {
user = await generateUser();
});
it('returns an error when user balance is too low', async () => {
await expect(user.post('/user/rebirth'))
.to.eventually.be.rejected.and.to.eql({
code: 401,
error: 'NotAuthorized',
message: t('notEnoughGems'),
});
});
// More tests in common code unit tests
it('resets user\'s tasks', async () => {
await user.update({
balance: 2,
});
let daily = await generateDaily({
text: 'test habit',
type: 'daily',
streak: 1,
userId: user._id,
});
let reward = await generateReward({
text: 'test reward',
type: 'reward',
value: 1,
userId: user._id,
});
let response = await user.post('/user/rebirth');
await user.sync();
let updatedDaily = await user.get(`/tasks/${daily._id}`);
let updatedReward = await user.get(`/tasks/${reward._id}`);
expect(response.message).to.equal(t('rebirthComplete'));
expect(updatedDaily.streak).to.equal(0);
expect(updatedDaily.value).to.equal(0);
expect(updatedReward.value).to.equal(1);
});
});

188
test/common/ops/rebirth.js Normal file
View File

@@ -0,0 +1,188 @@
import rebirth from '../../../common/script/ops/rebirth';
import i18n from '../../../common/script/i18n';
import { MAX_LEVEL } from '../../../common/script/constants';
import {
generateUser,
generateDaily,
generateReward,
} from '../../helpers/common.helper';
import {
NotAuthorized,
} from '../../../common/script/libs/errors';
describe('shared.ops.rebirth', () => {
let user;
let animal = 'Wolf-Base';
let userStats = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'];
let tasks = [];
beforeEach(() => {
user = generateUser();
user.balance = 2;
tasks = [generateDaily(), generateReward()];
});
it('returns an error when user balance is too low and user is less than max level', (done) => {
user.balance = 0;
try {
rebirth(user);
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('notEnoughGems'));
done();
}
});
it('rebirths a user with enough gems', () => {
let response = rebirth(user);
expect(response.message).to.equal(i18n.t('rebirthComplete'));
});
it('rebirths a user with not enough gems but max level', () => {
user.balance = 0;
user.stats.lvl = MAX_LEVEL;
let response = rebirth(user);
expect(response.message).to.equal(i18n.t('rebirthComplete'));
});
it('resets user\'s taks values except for rewards to 0', () => {
tasks[0].value = 1;
tasks[1].value = 1;
rebirth(user, tasks);
expect(tasks[0].value).to.equal(0);
expect(tasks[1].value).to.equal(1);
});
it('resets user\'s daily streaks to 0', () => {
tasks[0].streak = 1;
rebirth(user, tasks);
expect(tasks[0].streak).to.equal(0);
});
it('resets a user\'s buffs', () => {
user.stats.buffs = {test: 'test'};
rebirth(user);
expect(user.stats.buffs).to.be.empty;
});
it('resets a user\'s health points', () => {
user.stats.hp = 40;
rebirth(user);
expect(user.stats.hp).to.equal(50);
});
it('resets a user\'s class', () => {
user.stats.class = 'rouge';
rebirth(user);
expect(user.stats.class).to.equal('warrior');
});
it('resets a user\'s stats', () => {
user.stats.class = 'rouge';
_.each(userStats, function setUsersStats (value) {
user.stats[value] = 10;
});
rebirth(user);
_.each(userStats, function resetUserStats (value) {
user.stats[value] = 0;
});
});
it('resets a user\'s gear', () => {
let gearReset = {
armor: 'armor_base_0',
weapon: 'weapon_warrior_0',
head: 'head_base_0',
shield: 'shield_base_0',
};
rebirth(user);
expect(user.items.gear.equipped).to.deep.equal(gearReset);
expect(user.items.gear.costume).to.deep.equal(gearReset);
expect(user.preferences.costume).to.be.false;
});
it('resets a user\'s gear owned', () => {
user.items.gear.owned.weapon_warrior_1 = true; // eslint-disable-line camelcase
rebirth(user);
expect(user.items.gear.owned.weapon_warrior_1).to.be.false;
expect(user.items.gear.owned.weapon_warrior_0).to.be.true;
});
it('resets a user\'s current pet', () => {
user.items.pets[animal] = true;
user.items.currentPet = animal;
rebirth(user);
expect(user.items.currentPet).to.be.empty;
});
it('resets a user\'s current mount', () => {
user.items.mounts[animal] = true;
user.items.currentMount = animal;
rebirth(user);
expect(user.items.currentMount).to.be.empty;
});
it('resets a user\'s flags', () => {
user.flags.itemsEnabled = true;
user.flags.dropsEnabled = true;
user.flags.classSelected = true;
user.flags.rebirthEnabled = true;
user.flags.levelDrops = {test: 'test'};
rebirth(user);
expect(user.flags.itemsEnabled).to.be.false;
expect(user.flags.dropsEnabled).to.be.false;
expect(user.flags.classSelected).to.be.false;
expect(user.flags.rebirthEnabled).to.be.false;
expect(user.flags.levelDrops).to.be.emtpy;
});
it('does not reset rebirthEnabled if user has beastMaster', () => {
user.achievements.beastMaster = 1;
user.flags.rebirthEnabled = true;
rebirth(user);
expect(user.flags.rebirthEnabled).to.be.true;
});
it('sets rebirth achievement', () => {
rebirth(user);
expect(user.achievements.rebirths).to.equal(1);
expect(user.achievements.rebirthLevel).to.equal(user.stats.lvl);
});
it('increments rebirth achievemnts', () => {
user.stats.lvl = 2;
user.achievements.rebirths = 1;
user.achievements.rebirthLevel = 1;
rebirth(user);
expect(user.achievements.rebirths).to.equal(2);
expect(user.achievements.rebirthLevel).to.equal(2);
});
});

View File

@@ -888,4 +888,33 @@ api.userRevive = {
},
};
/*
* @api {post} /user/rebirth Resets a user.
* @apiVersion 3.0.0
* @apiName UserRebirth
* @apiGroup User
*
* @apiSuccess {Object} data `user`
*/
api.userRebirth = {
method: 'POST',
middlewares: [authWithHeaders(), cron],
url: '/user/rebirth',
async handler (req, res) {
let user = res.locals.user;
let query = {
userId: user._id,
type: {$in: ['daily', 'habit', 'todo']},
};
let tasks = await Tasks.Task.find(query).exec();
let rebirthResponse = common.ops.rebirth(user, tasks, req, res.analytics);
await user.save();
await Q.all(tasks.map(task => task.save()));
res.respond(200, rebirthResponse);
},
};
module.exports = api;