diff --git a/common/locales/en/api-v3.json b/common/locales/en/api-v3.json index afb7b17f00..fa635243e2 100644 --- a/common/locales/en/api-v3.json +++ b/common/locales/en/api-v3.json @@ -148,5 +148,6 @@ "noSudoAccess": "You don't have sudo access.", "couponCodeRequired": "The coupon code is required.", "eventRequired": "\"req.params.event\" is required.", - "countRequired": "\"req.query.count\" is required." + "countRequired": "\"req.query.count\" is required.", + "petsReleased": "Pets released." } diff --git a/common/script/index.js b/common/script/index.js index ee7206f3b1..4b854c8306 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -118,6 +118,7 @@ import purchase from './ops/purchase'; import purchaseHourglass from './ops/hourglassPurchase'; import readCard from './ops/readCard'; import openMysteryItem from './ops/openMysteryItem'; +import releasePets from './ops/releasePets'; api.ops = { scoreTask, @@ -137,6 +138,7 @@ api.ops = { purchaseHourglass, readCard, openMysteryItem, + releasePets, }; import handleTwoHanded from './fns/handleTwoHanded'; diff --git a/common/script/ops/releasePets.js b/common/script/ops/releasePets.js index a4b452cd86..12bb48a90f 100644 --- a/common/script/ops/releasePets.js +++ b/common/script/ops/releasePets.js @@ -1,32 +1,40 @@ import content from '../content/index'; import i18n from '../i18n'; +import { + NotAuthorized, +} from '../libs/errors'; +import splitWhitespace from '../libs/splitWhitespace'; -module.exports = function(user, req, cb, analytics) { - var analyticsData, pet; +module.exports = function releasePets (user, req = {}, analytics) { if (user.balance < 1) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } else { - user.balance -= 1; - for (pet in content.pets) { - user.items.pets[pet] = 0; - } - if (!user.achievements.beastMasterCount) { - user.achievements.beastMasterCount = 0; - } - user.achievements.beastMasterCount++; - user.items.currentPet = ""; + throw new NotAuthorized(i18n.t('notEnoughGems', req.language)); } - analyticsData = { - uuid: user._id, - acquireMethod: 'Gems', - gemCost: 4, - category: 'behavior' + + user.balance -= 1; + user.items.currentPet = ''; + + for (let pet in content.pets) { + user.items.pets[pet] = 0; + } + + if (!user.achievements.beastMasterCount) { + user.achievements.beastMasterCount = 0; + } + user.achievements.beastMasterCount++; + + if (analytics) { + analytics.track('release pets', { + uuid: user._id, + acquireMethod: 'Gems', + gemCost: 4, + category: 'behavior' + }); + } + + let response = { + data: _.pick(user, splitWhitespace('user.items.pets')), + message: i18n.t('petsReleased'), }; - if (analytics != null) { - analytics.track('release pets', analyticsData); - } - return typeof cb === "function" ? cb(null, user) : void 0; + + return response; }; diff --git a/test/api/v3/integration/user/POST-user_release_pets.test.js b/test/api/v3/integration/user/POST-user_release_pets.test.js new file mode 100644 index 0000000000..a7f7b9b66f --- /dev/null +++ b/test/api/v3/integration/user/POST-user_release_pets.test.js @@ -0,0 +1,42 @@ +import { + generateUser, + translate as t, +} from '../../../../helpers/api-integration/v3'; + +describe('POST /user/release-pets', () => { + let user; + let animal = 'Wolf-Base'; + + beforeEach(async () => { + user = await generateUser({ + 'items.currentPet': animal, + 'items.pets': {animal: 5}, + }); + }); + + it('returns an error when user balance is too low', async () => { + await expect(user.post('/user/release-pets')) + .to.eventually.be.rejected.and.to.eql({ + code: 401, + error: 'NotAuthorized', + message: t('notEnoughGems'), + }); + }); + + // More tests in common code unit tests + + it('releases pets', async () => { + await user.update({ + balance: 1, + }); + + let response = await user.post('/user/release-pets'); + await user.sync(); + + expect(response.message).to.equal(t('petsReleased')); + expect(user.balance).to.equal(0); + expect(user.items.currentPet).to.be.empty; + expect(user.items.pets[animal]).to.equal(0); + expect(user.achievements.beastMasterCount).to.equal(1); + }); +}); diff --git a/test/common/ops/releasePets.js b/test/common/ops/releasePets.js new file mode 100644 index 0000000000..11d69a5a31 --- /dev/null +++ b/test/common/ops/releasePets.js @@ -0,0 +1,57 @@ +import releasePets from '../../../common/script/ops/releasePets'; +import i18n from '../../../common/script/i18n'; +import { + generateUser, +} from '../../helpers/common.helper'; +import { + NotAuthorized, +} from '../../../common/script/libs/errors'; + +describe('shared.ops.releasePets', () => { + let user; + let animal = 'Wolf-Base'; + + beforeEach(() => { + user = generateUser(); + user.items.currentPet = animal; + user.items.pets[animal] = 5; + user.balance = 1; + }); + + it('returns an error when user balance is too low', (done) => { + user.balance = 0; + + try { + releasePets(user); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('notEnoughGems')); + done(); + } + }); + + it('releases pets', () => { + let response = releasePets(user); + + expect(response.message).to.equal(i18n.t('petsReleased')); + expect(user.items.pets[animal]).to.equal(0); + }); + + it('removes currentPet', () => { + releasePets(user); + + expect(user.items.currentPet).to.be.empty; + }); + + it('decreases user\'s balance', () => { + releasePets(user); + + expect(user.balance).to.equal(0); + }); + + it('incremenets beastMasterCount', () => { + releasePets(user); + + expect(user.achievements.beastMasterCount).to.equal(1); + }); +}); diff --git a/website/src/controllers/api-v3/user.js b/website/src/controllers/api-v3/user.js index 49d2f2f05d..7b7cf3dd57 100644 --- a/website/src/controllers/api-v3/user.js +++ b/website/src/controllers/api-v3/user.js @@ -764,4 +764,24 @@ api.userOpenMysteryItem = { }, }; +/** +* @api {post} /user/release-pets Releases pets. +* @apiVersion 3.0.0 +* @apiName UserReleasePets +* @apiGroup User +* +* @apiSuccess {Object} data `user.items.pets` +*/ +api.userReleasePets = { + method: 'POST', + middlewares: [authWithHeaders(), cron], + url: '/user/release-pets', + async handler (req, res) { + let user = res.locals.user; + let releasePetsResponse = common.ops.releasePets(user, req, res.analytics); + await user.save(); + res.respond(200, releasePetsResponse); + }, +}; + module.exports = api;