From bd3c162b97695de257ad6c702a2b135232e67174 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sun, 20 Mar 2016 20:21:15 +0100 Subject: [PATCH] port allocateNow and autoAllocate --- common/script/fns/autoAllocate.js | 101 ++++++++++-------- common/script/index.js | 7 +- common/script/ops/allocateNow.js | 12 +-- tasks/gulp-eslint.js | 3 - .../user/POST-user_allocate_now.test.js | 32 ++++++ test/common/fns/autoAllocate.test.js | 86 +++++++++++++++ test/common/ops/allocateNow.js | 34 ++++++ website/src/controllers/api-v3/user.js | 20 ++++ 8 files changed, 237 insertions(+), 58 deletions(-) create mode 100644 test/api/v3/integration/user/POST-user_allocate_now.test.js create mode 100644 test/common/fns/autoAllocate.test.js create mode 100644 test/common/ops/allocateNow.js diff --git a/common/script/fns/autoAllocate.js b/common/script/fns/autoAllocate.js index ab037e628b..3d680d282d 100644 --- a/common/script/fns/autoAllocate.js +++ b/common/script/fns/autoAllocate.js @@ -7,50 +7,59 @@ import splitWhitespace from '../libs/splitWhitespace'; {update} if aggregated changes, pass in userObj as update. otherwise commits will be made immediately */ -module.exports = function(user) { - return user.stats[(function() { - var diff, ideal, lvlDiv7, preference, stats, suggested; - switch (user.preferences.allocationMode) { - case "flat": - stats = _.pick(user.stats, splitWhitespace('con str per int')); - return _.invert(stats)[_.min(stats)]; - case "classbased": - lvlDiv7 = user.stats.lvl / 7; - ideal = [lvlDiv7 * 3, lvlDiv7 * 2, lvlDiv7, lvlDiv7]; - preference = (function() { - switch (user.stats["class"]) { - case "wizard": - return ["int", "per", "con", "str"]; - case "rogue": - return ["per", "str", "int", "con"]; - case "healer": - return ["con", "int", "str", "per"]; - default: - return ["str", "con", "per", "int"]; - } - })(); - diff = [user.stats[preference[0]] - ideal[0], user.stats[preference[1]] - ideal[1], user.stats[preference[2]] - ideal[2], user.stats[preference[3]] - ideal[3]]; - suggested = _.findIndex(diff, (function(val) { - if (val === _.min(diff)) { - return true; - } - })); - if (~suggested) { - return preference[suggested]; - } else { - return "str"; - } - case "taskbased": - suggested = _.invert(user.stats.training)[_.max(user.stats.training)]; - _.merge(user.stats.training, { - str: 0, - int: 0, - con: 0, - per: 0 - }); - return suggested || "str"; - default: - return "str"; - } - })()]++; +function getStatToAllocate (user) { + let suggested; + + switch (user.preferences.allocationMode) { + case 'flat': + let stats = _.pick(user.stats, splitWhitespace('con str per int')); + return _.invert(stats)[_.min(stats)]; + case 'classbased': + let lvlDiv7 = user.stats.lvl / 7; + let ideal = [lvlDiv7 * 3, lvlDiv7 * 2, lvlDiv7, lvlDiv7]; + + let preference; + switch (user.stats.class) { + case 'wizard': + preference = ['int', 'per', 'con', 'str']; + break; + case 'rogue': + preference = ['per', 'str', 'int', 'con']; + break; + case 'healer': + preference = ['con', 'int', 'str', 'per']; + break; + default: + preference = ['str', 'con', 'per', 'int']; + } + + let diff = [ + user.stats[preference[0]] - ideal[0], + user.stats[preference[1]] - ideal[1], + user.stats[preference[2]] - ideal[2], + user.stats[preference[3]] - ideal[3], + ]; + + suggested = _.findIndex(diff, (val) => { + if (val === _.min(diff)) return true; + }); + + return suggested !== -1 ? preference[suggested] : 'str'; + case 'taskbased': + suggested = _.invert(user.stats.training)[_.max(user.stats.training)]; + + let training = user.stats.training; + training.str = 0; + training.int = 0; + training.con = 0; + training.per = 0; + + return suggested || 'str'; + default: + return 'str'; + } +} + +module.exports = function autoAllocate (user) { + return user.stats[getStatToAllocate(user)]++; }; diff --git a/common/script/index.js b/common/script/index.js index 01a9aee671..fff3f18450 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -108,6 +108,7 @@ import buy from './ops/buy'; import buyMysterySet from './ops/buyMysterySet'; import buyQuest from './ops/buyQuest'; import buySpecialSpell from './ops/buySpecialSpell'; +import allocateNow from './ops/allocateNow'; api.ops = { scoreTask, @@ -117,18 +118,21 @@ api.ops = { buyMysterySet, buySpecialSpell, buyQuest, + allocateNow, }; import handleTwoHanded from './fns/handleTwoHanded'; import predictableRandom from './fns/predictableRandom'; import randomVal from './fns/randomVal'; import ultimateGear from './fns/ultimateGear'; +import autoAllocate from './fns/autoAllocate'; api.fns = { handleTwoHanded, predictableRandom, randomVal, ultimateGear, + autoAllocate, }; @@ -191,7 +195,6 @@ api.wrap = function wrapUser (user, main = true) { reset: _.partial(importedOps.reset, user), reroll: _.partial(importedOps.reroll, user), rebirth: _.partial(importedOps.rebirth, user), - allocateNow: _.partial(importedOps.allocateNow, user), clearCompleted: _.partial(importedOps.clearCompleted, user), sortTask: _.partial(importedOps.sortTask, user), updateTask: _.partial(importedOps.updateTask, user), @@ -222,7 +225,6 @@ api.wrap = function wrapUser (user, main = true) { unlock: _.partial(importedOps.unlock, user), changeClass: _.partial(importedOps.changeClass, user), disableClasses: _.partial(importedOps.disableClasses, user), - allocate: _.partial(importedOps.allocate, user), readCard: _.partial(importedOps.readCard, user), openMysteryItem: _.partial(importedOps.openMysteryItem, user), scoreTask: _.partial(importedOps.scoreTask, user), @@ -235,7 +237,6 @@ api.wrap = function wrapUser (user, main = true) { dotSet: _.partial(importedFns.dotSet, user), dotGet: _.partial(importedFns.dotGet, user), randomDrop: _.partial(importedFns.randomDrop, user), - autoAllocate: _.partial(importedFns.autoAllocate, user), updateStats: _.partial(importedFns.updateStats, user), cron: _.partial(importedFns.cron, user), preenUserHistory: _.partial(importedFns.preenUserHistory, user), diff --git a/common/script/ops/allocateNow.js b/common/script/ops/allocateNow.js index 815c0b8959..fefd130fed 100644 --- a/common/script/ops/allocateNow.js +++ b/common/script/ops/allocateNow.js @@ -1,10 +1,10 @@ import _ from 'lodash'; +import autoAllocate from '../fns/autoAllocate'; -module.exports = function(user, req, cb) { - _.times(user.stats.points, user.fns.autoAllocate); +module.exports = function allocateNow (user) { + _.times(user.stats.points, () => autoAllocate(user)); user.stats.points = 0; - if (typeof user.markModified === "function") { - user.markModified('stats'); - } - return typeof cb === "function" ? cb(null, user.stats) : void 0; + return { + data: _.pick(user, 'stats'), + }; }; diff --git a/tasks/gulp-eslint.js b/tasks/gulp-eslint.js index 85c275bb8f..25a0f6a978 100644 --- a/tasks/gulp-eslint.js +++ b/tasks/gulp-eslint.js @@ -19,7 +19,6 @@ const COMMON_FILES = [ '!./common/script/ops/addTag.js', '!./common/script/ops/addTask.js', '!./common/script/ops/addWebhook.js', - '!./common/script/ops/allocateNow.js', '!./common/script/ops/blockUser.js', '!./common/script/ops/changeClass.js', '!./common/script/ops/clearCompleted.js', @@ -53,7 +52,6 @@ const COMMON_FILES = [ '!./common/script/ops/updateTag.js', '!./common/script/ops/updateTask.js', '!./common/script/ops/updateWebhook.js', - '!./common/script/fns/autoAllocate.js', '!./common/script/fns/crit.js', '!./common/script/fns/cron.js', '!./common/script/fns/dotGet.js', @@ -68,7 +66,6 @@ const COMMON_FILES = [ '!./common/script/libs/dotGet.js', '!./common/script/libs/dotSet.js', '!./common/script/libs/encodeiCalLink.js', - '!./common/script/libs/extendableBuiltin.js', '!./common/script/libs/friendlyTimestamp.js', '!./common/script/libs/gold.js', '!./common/script/libs/newChatMessages.js', diff --git a/test/api/v3/integration/user/POST-user_allocate_now.test.js b/test/api/v3/integration/user/POST-user_allocate_now.test.js new file mode 100644 index 0000000000..4b649cd187 --- /dev/null +++ b/test/api/v3/integration/user/POST-user_allocate_now.test.js @@ -0,0 +1,32 @@ +import { + generateUser, +} from '../../../../helpers/api-integration/v3'; + +describe('POST /user/allocate-now', () => { + // More tests in common code unit tests + + it('auto allocates all points', async () => { + let user = await generateUser({ + 'stats.points': 5, + 'stats.int': 3, + 'stats.con': 9, + 'stats.per': 9, + 'stats.str': 9, + 'preferences.allocationMode': 'flat', + }); + + let res = await user.post(`/user/allocate-now`); + await user.sync(); + + expect(res).to.eql({ + data: { + stats: user.stats, + }, + }); + expect(user.stats.points).to.equal(0); + expect(user.stats.con).to.equal(9); + expect(user.stats.int).to.equal(8); + expect(user.stats.per).to.equal(9); + expect(user.stats.str).to.equal(9); + }); +}); diff --git a/test/common/fns/autoAllocate.test.js b/test/common/fns/autoAllocate.test.js new file mode 100644 index 0000000000..e00f8dd17d --- /dev/null +++ b/test/common/fns/autoAllocate.test.js @@ -0,0 +1,86 @@ +import autoAllocate from '../../../common/script/fns/autoAllocate'; +import { + generateUser, +} from '../../helpers/common.helper'; + +describe('shared.fns.autoAllocate', () => { + let user; + + beforeEach(() => { + user = generateUser(); + }); + + it('user.preferences.allocationMode === flat', () => { + user.stats.con = 5; + user.stats.int = 5; + user.stats.per = 3; + user.stats.str = 8; + + user.preferences.allocationMode = 'flat'; + + autoAllocate(user); + + expect(user.stats.con).to.equal(5); + expect(user.stats.int).to.equal(5); + expect(user.stats.per).to.equal(4); + expect(user.stats.str).to.equal(8); + }); + + it('user.preferences.allocationMode === taskbased', () => { + user.stats.con = 5; + user.stats.int = 5; + user.stats.per = 3; + user.stats.str = 8; + user.stats.training.con = 2; + user.stats.training.int = 5; + user.stats.training.per = 7; + user.stats.training.str = 4; + + user.preferences.allocationMode = 'taskbased'; + + autoAllocate(user); + + expect(user.stats.con).to.equal(5); + expect(user.stats.int).to.equal(5); + expect(user.stats.per).to.equal(4); + expect(user.stats.str).to.equal(8); + + expect(user.stats.training.con).to.equal(0); + expect(user.stats.training.int).to.equal(0); + expect(user.stats.training.per).to.equal(0); + expect(user.stats.training.str).to.equal(0); + }); + + it('user.preferences.allocationMode === classbased', () => { + user.stats.lvl = 35; + user.stats.class = 'healer'; + user.stats.con = 5; + user.stats.int = 5; + user.stats.per = 3; + user.stats.str = 8; + + user.preferences.allocationMode = 'classbased'; + + autoAllocate(user); + + expect(user.stats.con).to.equal(6); + expect(user.stats.int).to.equal(5); + expect(user.stats.per).to.equal(3); + expect(user.stats.str).to.equal(8); + }); + + it('user.preferences.allocationMode === anything', () => { + user.stats.con = 5; + user.stats.int = 5; + user.stats.per = 3; + user.stats.str = 8; + user.preferences.allocationMode = 'wrong'; + + autoAllocate(user); + + expect(user.stats.con).to.equal(5); + expect(user.stats.int).to.equal(5); + expect(user.stats.per).to.equal(3); + expect(user.stats.str).to.equal(9); + }); +}); diff --git a/test/common/ops/allocateNow.js b/test/common/ops/allocateNow.js new file mode 100644 index 0000000000..21a7f6daf4 --- /dev/null +++ b/test/common/ops/allocateNow.js @@ -0,0 +1,34 @@ +import allocateNow from '../../../common/script/ops/allocateNow'; +import { + generateUser, +} from '../../helpers/common.helper'; + +describe('shared.ops.allocateNow', () => { + let user; + + beforeEach(() => { + user = generateUser(); + }); + + it('auto allocates all points', () => { + user.stats.points = 5; + user.stats.int = 3; + user.stats.con = 9; + user.stats.per = 9; + user.stats.str = 9; + user.preferences.allocationMode = 'flat'; + + let res = allocateNow(user); + + expect(user.stats.points).to.equal(0); + expect(user.stats.con).to.equal(9); + expect(user.stats.int).to.equal(8); + expect(user.stats.per).to.equal(9); + expect(user.stats.str).to.equal(9); + expect(res).to.eql({ + data: { + stats: user.stats, + }, + }); + }); +}); diff --git a/website/src/controllers/api-v3/user.js b/website/src/controllers/api-v3/user.js index be191596e3..11bd137fa5 100644 --- a/website/src/controllers/api-v3/user.js +++ b/website/src/controllers/api-v3/user.js @@ -196,6 +196,26 @@ api.allocate = { }, }; +/** + * @api {post} /user/allocate-now Allocate all attribute points. + * @apiVersion 3.0.0 + * @apiName UserAllocateNow + * @apiGroup User + * + * @apiSuccess {Object} data `stats` + */ +api.allocateNow = { + method: 'POST', + middlewares: [authWithHeaders(), cron], + url: '/user/allocate-now', + async handler (req, res) { + let user = res.locals.user; + let allocateNowRes = common.ops.allocateNow(user, req); + await user.save(); + res.respond(200, allocateNowRes); + }, +}; + /** * @api {post} /user/buy/:key Buy a content item. * @apiVersion 3.0.0