From 24b7b4c8389c2f01a2b08760653b07664371ab16 Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Sun, 3 Apr 2016 14:33:52 -0500 Subject: [PATCH 1/4] Ported release mounts, add unit tests, added release mounts route with integration tests --- common/locales/en/api-v3.json | 3 +- common/script/index.js | 2 + common/script/ops/releaseMounts.js | 60 +++++++++++-------- .../user/POST-user_release_mounts.test.js | 42 +++++++++++++ test/common/ops/releaseMounts.js | 57 ++++++++++++++++++ website/src/controllers/api-v3/user.js | 20 +++++++ 6 files changed, 158 insertions(+), 26 deletions(-) create mode 100644 test/api/v3/integration/user/POST-user_release_mounts.test.js create mode 100644 test/common/ops/releaseMounts.js diff --git a/common/locales/en/api-v3.json b/common/locales/en/api-v3.json index afb7b17f00..1e840c28ad 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.", + "mountsReleased": "Mounts released" } diff --git a/common/script/index.js b/common/script/index.js index ee7206f3b1..6cd1384dfd 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 releaseMounts from './ops/releaseMounts'; api.ops = { scoreTask, @@ -137,6 +138,7 @@ api.ops = { purchaseHourglass, readCard, openMysteryItem, + releaseMounts, }; import handleTwoHanded from './fns/handleTwoHanded'; diff --git a/common/script/ops/releaseMounts.js b/common/script/ops/releaseMounts.js index 4aefab6b40..55d2c23fcb 100644 --- a/common/script/ops/releaseMounts.js +++ b/common/script/ops/releaseMounts.js @@ -1,32 +1,42 @@ import content from '../content/index'; import i18n from '../i18n'; +import { + NotAuthorized, +} from '../libs/errors'; +import splitWhitespace from '../libs/splitWhitespace'; + +module.exports = function releaseMounts (user, req = {}, analytics) { + let mount; -module.exports = function(user, req, cb, analytics) { - var analyticsData, mount; if (user.balance < 1) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } else { - user.balance -= 1; - user.items.currentMount = ""; - for (mount in content.pets) { - user.items.mounts[mount] = null; - } - if (!user.achievements.mountMasterCount) { - user.achievements.mountMasterCount = 0; - } - user.achievements.mountMasterCount++; + throw new NotAuthorized(i18n.t('notEnoughGems', req.language)); } - analyticsData = { - uuid: user._id, - acquireMethod: 'Gems', - gemCost: 4, - category: 'behavior' + + user.balance -= 1; + user.items.currentMount = ''; + + for (mount in content.pets) { + user.items.mounts[mount] = null; + } + + if (!user.achievements.mountMasterCount) { + user.achievements.mountMasterCount = 0; + } + user.achievements.mountMasterCount++; + + if (analytics) { + analytics.track('release mounts', { + uuid: user._id, + acquireMethod: 'Gems', + gemCost: 4, + category: 'behavior' + }); + } + + let response = { + data: _.pick(user, splitWhitespace('mounts')), + message: i18n.t('mountsReleased'), }; - if (analytics != null) { - analytics.track('release mounts', analyticsData); - } - return typeof cb === "function" ? cb(null, user) : void 0; + + return response; }; diff --git a/test/api/v3/integration/user/POST-user_release_mounts.test.js b/test/api/v3/integration/user/POST-user_release_mounts.test.js new file mode 100644 index 0000000000..86391599f0 --- /dev/null +++ b/test/api/v3/integration/user/POST-user_release_mounts.test.js @@ -0,0 +1,42 @@ +import { + generateUser, + translate as t, +} from '../../../../helpers/api-integration/v3'; + +describe('POST /user/release-mounts', () => { + let user; + let animal = 'Wolf-Base'; + + beforeEach(async () => { + user = await generateUser({ + 'items.currentMount': animal, + 'items.mounts': {animal: true}, + }); + }); + + it('returns an error when user balance is too low', async () => { + await expect(user.post('/user/release-mounts')) + .to.eventually.be.rejected.and.to.eql({ + code: 401, + error: 'NotAuthorized', + message: t('notEnoughGems'), + }); + }); + + // More tests in common code unit tests + + it('releases mounts', async () => { + await user.update({ + balance: 1, + }); + + let response = await user.post('/user/release-mounts'); + await user.sync(); + + expect(response.message).to.equal(t('mountsReleased')); + expect(user.balance).to.equal(0); + expect(user.items.currentMount).to.be.empty; + expect(user.items.mounts[animal]).to.equal(null); + expect(user.achievements.mountMasterCount).to.equal(1); + }); +}); diff --git a/test/common/ops/releaseMounts.js b/test/common/ops/releaseMounts.js new file mode 100644 index 0000000000..80429b726f --- /dev/null +++ b/test/common/ops/releaseMounts.js @@ -0,0 +1,57 @@ +import releaseMounts from '../../../common/script/ops/releaseMounts'; +import i18n from '../../../common/script/i18n'; +import { + generateUser, +} from '../../helpers/common.helper'; +import { + NotAuthorized, +} from '../../../common/script/libs/errors'; + +describe('shared.ops.releaseMounts', () => { + let user; + let animal = 'Wolf-Base'; + + beforeEach(() => { + user = generateUser(); + user.items.currentMount = animal; + user.items.mounts[animal] = true; + user.balance = 1; + }); + + it('returns an error when user balance is too low', (done) => { + user.balance = 0; + + try { + releaseMounts(user); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('notEnoughGems')); + done(); + } + }); + + it('releases mounts', () => { + let response = releaseMounts(user); + + expect(response.message).to.equal(i18n.t('mountsReleased')); + expect(user.items.mounts[animal]).to.equal(null); + }); + + it('removes currentMount', () => { + releaseMounts(user); + + expect(user.items.currentMount).to.be.empty; + }); + + it('increases mountMasterCount achievement', () => { + releaseMounts(user); + + expect(user.achievements.mountMasterCount).to.equal(1); + }); + + it('subtracts gems from balance', () => { + releaseMounts(user); + + expect(user.balance).to.equal(0); + }); +}); diff --git a/website/src/controllers/api-v3/user.js b/website/src/controllers/api-v3/user.js index 49d2f2f05d..05a6904f3f 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-mounts Released mounts. +* @apiVersion 3.0.0 +* @apiName UserReleaseMounts +* @apiGroup User +* +* @apiSuccess {Object} data `mounts` +*/ +api.userReleaseMounts = { + method: 'POST', + middlewares: [authWithHeaders(), cron], + url: '/user/release-mounts', + async handler (req, res) { + let user = res.locals.user; + let releaseMountsResponse = common.ops.releaseMounts(user, req, res.analytics); + await user.save(); + res.respond(200, releaseMountsResponse); + }, +}; + module.exports = api; From 487a26ec435a8f772c65ac3c79569de3ca58397e Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Sun, 3 Apr 2016 14:37:20 -0500 Subject: [PATCH 2/4] Ported release pets, added unit tests, added route with integration tests --- common/locales/en/api-v3.json | 3 +- common/script/index.js | 2 + common/script/ops/releasePets.js | 58 +++++++++++-------- .../user/POST-user_release_pets.test.js | 42 ++++++++++++++ test/common/ops/releasePets.js | 57 ++++++++++++++++++ website/src/controllers/api-v3/user.js | 20 +++++++ 6 files changed, 156 insertions(+), 26 deletions(-) create mode 100644 test/api/v3/integration/user/POST-user_release_pets.test.js create mode 100644 test/common/ops/releasePets.js 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; From c916c747752a7db92facb024e47cb1c98ad7ee62 Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Sun, 3 Apr 2016 14:43:16 -0500 Subject: [PATCH 3/4] Ported release both, added unit tests, add release both route with integration tests --- common/locales/en/api-v3.json | 3 +- common/script/index.js | 2 + common/script/ops/releaseBoth.js | 93 ++++++++++-------- .../user/POST-user_release_both.test.js | 48 +++++++++ test/common/ops/releaseBoth.js | 98 +++++++++++++++++++ website/src/controllers/api-v3/user.js | 20 ++++ 6 files changed, 224 insertions(+), 40 deletions(-) create mode 100644 test/api/v3/integration/user/POST-user_release_both.test.js create mode 100644 test/common/ops/releaseBoth.js diff --git a/common/locales/en/api-v3.json b/common/locales/en/api-v3.json index afb7b17f00..940289e7ef 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.", + "mountsAndPetsReleased": "Mounts and pets released" } diff --git a/common/script/index.js b/common/script/index.js index ee7206f3b1..60cd5e120f 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 releaseBoth from './ops/releaseBoth'; api.ops = { scoreTask, @@ -137,6 +138,7 @@ api.ops = { purchaseHourglass, readCard, openMysteryItem, + releaseBoth, }; import handleTwoHanded from './fns/handleTwoHanded'; diff --git a/common/script/ops/releaseBoth.js b/common/script/ops/releaseBoth.js index a782581f85..a17d2e5f00 100644 --- a/common/script/ops/releaseBoth.js +++ b/common/script/ops/releaseBoth.js @@ -1,50 +1,65 @@ import content from '../content/index'; import i18n from '../i18n'; +import { + NotAuthorized, +} from '../libs/errors'; +import splitWhitespace from '../libs/splitWhitespace'; + +module.exports = function releaseBoth (user, req = {}, analytics) { + let animal; -module.exports = function(user, req, cb, analytics) { - var analyticsData, animal, giveTriadBingo; if (user.balance < 1.5 && !user.achievements.triadBingo) { - return typeof cb === "function" ? cb({ - code: 401, - message: i18n.t('notEnoughGems', req.language) - }) : void 0; - } else { - giveTriadBingo = true; - if (!user.achievements.triadBingo) { - analyticsData = { + throw new NotAuthorized(i18n.t('notEnoughGems', req.language)); + } + + let giveTriadBingo = true; + + if (!user.achievements.triadBingo) { + if (analytics) { + analytics.track('release pets & mounts', { uuid: user._id, acquireMethod: 'Gems', gemCost: 6, category: 'behavior' - }; - if (typeof analytics !== "undefined" && analytics !== null) { - analytics.track('release pets & mounts', analyticsData); - } - user.balance -= 1.5; - } - user.items.currentMount = ""; - user.items.currentPet = ""; - for (animal in content.pets) { - if (user.items.pets[animal] === -1) { - giveTriadBingo = false; - } - user.items.pets[animal] = 0; - user.items.mounts[animal] = null; - } - if (!user.achievements.beastMasterCount) { - user.achievements.beastMasterCount = 0; - } - user.achievements.beastMasterCount++; - if (!user.achievements.mountMasterCount) { - user.achievements.mountMasterCount = 0; - } - user.achievements.mountMasterCount++; - if (giveTriadBingo) { - if (!user.achievements.triadBingoCount) { - user.achievements.triadBingoCount = 0; - } - user.achievements.triadBingoCount++; + }); } + + user.balance -= 1.5; } - return typeof cb === "function" ? cb(null, user) : void 0; + + user.items.currentMount = ""; + user.items.currentPet = ""; + + for (animal in content.pets) { + if (user.items.pets[animal] === -1) { + giveTriadBingo = false; + } + + user.items.pets[animal] = 0; + user.items.mounts[animal] = null; + } + + if (!user.achievements.beastMasterCount) { + user.achievements.beastMasterCount = 0; + } + user.achievements.beastMasterCount++; + + if (!user.achievements.mountMasterCount) { + user.achievements.mountMasterCount = 0; + } + user.achievements.mountMasterCount++; + + if (giveTriadBingo) { + if (!user.achievements.triadBingoCount) { + user.achievements.triadBingoCount = 0; + } + user.achievements.triadBingoCount++; + } + + let response = { + data: _.pick(user, splitWhitespace('achievements')), + message: i18n.t('mountsAndPetsReleased'), + }; + + return response; }; diff --git a/test/api/v3/integration/user/POST-user_release_both.test.js b/test/api/v3/integration/user/POST-user_release_both.test.js new file mode 100644 index 0000000000..8c47d95dfe --- /dev/null +++ b/test/api/v3/integration/user/POST-user_release_both.test.js @@ -0,0 +1,48 @@ +import { + generateUser, + translate as t, +} from '../../../../helpers/api-integration/v3'; + +describe('POST /user/release-both', () => { + let user; + let animal = 'Wolf-Base'; + + beforeEach(async () => { + user = await generateUser({ + 'items.currentMount': animal, + 'items.currentPet': animal, + 'items.pets': {animal: 5}, + 'items.mounts': {animal: true}, + }); + }); + + it('returns an error when user balance is too low and user does not have triadBingo', async () => { + await expect(user.post('/user/release-both')) + .to.eventually.be.rejected.and.to.eql({ + code: 401, + error: 'NotAuthorized', + message: t('notEnoughGems'), + }); + }); + + // More tests in common code unit tests + + it('grants triad bingo with gems', async () => { + await user.update({ + balance: 1.5, + }); + + let response = await user.post('/user/release-both'); + await user.sync(); + + expect(response.message).to.equal(t('mountsAndPetsReleased')); + expect(user.balance).to.equal(0); + expect(user.items.currentMount).to.be.empty; + expect(user.items.currentPet).to.be.empty; + expect(user.items.pets[animal]).to.be.empty; + expect(user.items.mounts[animal]).to.equal(null); + expect(user.achievements.beastMasterCount).to.equal(1); + expect(user.achievements.mountMasterCount).to.equal(1); + expect(user.achievements.triadBingoCount).to.equal(1); + }); +}); diff --git a/test/common/ops/releaseBoth.js b/test/common/ops/releaseBoth.js new file mode 100644 index 0000000000..309734e7d4 --- /dev/null +++ b/test/common/ops/releaseBoth.js @@ -0,0 +1,98 @@ +import releaseBoth from '../../../common/script/ops/releaseBoth'; +import i18n from '../../../common/script/i18n'; +import { + generateUser, +} from '../../helpers/common.helper'; +import { + NotAuthorized, +} from '../../../common/script/libs/errors'; + +describe('shared.ops.releaseBoth', () => { + let user; + let animal = 'Wolf-Base'; + + beforeEach(() => { + user = generateUser(); + user.items.currentMount = animal; + user.items.currentPet = animal; + user.items.pets[animal] = 5; + user.items.mounts[animal] = true; + user.balance = 1.5; + }); + + it('returns an error when user balance is too low and user does not have triadBingo', (done) => { + user.balance = 0; + + try { + releaseBoth(user); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('notEnoughGems')); + done(); + } + }); + + it('grants triad bingo with gems', () => { + let response = releaseBoth(user); + + expect(response.message).to.equal(i18n.t('mountsAndPetsReleased')); + expect(user.achievements.triadBingoCount).to.equal(1); + }); + + it('grants triad bingo without gems', () => { + user.balance = 0; + user.achievements.triadBingo = 1; + user.achievements.triadBingoCount = 1; + + let response = releaseBoth(user); + + expect(response.message).to.equal(i18n.t('mountsAndPetsReleased')); + expect(user.achievements.triadBingoCount).to.equal(2); + }); + + it('releases pets', () => { + let response = releaseBoth(user); + + expect(response.message).to.equal(i18n.t('mountsAndPetsReleased')); + expect(user.items.pets[animal]).to.be.empty; + expect(user.items.mounts[animal]).to.equal(null); + }); + + it('releases mounts', () => { + let response = releaseBoth(user); + + expect(response.message).to.equal(i18n.t('mountsAndPetsReleased')); + expect(user.items.mounts[animal]).to.equal(null); + }); + + it('removes currentPet', () => { + releaseBoth(user); + + expect(user.items.currentMount).to.be.empty; + expect(user.items.currentPet).to.be.empty; + }); + + it('removes currentMount', () => { + releaseBoth(user); + + expect(user.items.currentMount).to.be.empty; + }); + + it('decreases user\'s balance', () => { + releaseBoth(user); + + expect(user.balance).to.equal(0); + }); + + it('incremenets beastMasterCount', () => { + releaseBoth(user); + + expect(user.achievements.beastMasterCount).to.equal(1); + }); + + it('incremenets mountMasterCount', () => { + releaseBoth(user); + + expect(user.achievements.mountMasterCount).to.equal(1); + }); +}); diff --git a/website/src/controllers/api-v3/user.js b/website/src/controllers/api-v3/user.js index 49d2f2f05d..57202e5e62 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-both Releases Pets and Mounts and grants Triad Bingo. +* @apiVersion 3.0.0 +* @apiName UserReleaseBoth +* @apiGroup User +* +* @apiSuccess {Object} data `user.items.gear.owned` +*/ +api.userReleaseBoth = { + method: 'POST', + middlewares: [authWithHeaders(), cron], + url: '/user/release-both', + async handler (req, res) { + let user = res.locals.user; + let releaseBothResponse = common.ops.releaseBoth(user, req, res.analytics); + await user.save(); + res.respond(200, releaseBothResponse); + }, +}; + module.exports = api; From 68ff26e6d6c14a7d22bae19b1db3003bbfaf8258 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 4 Apr 2016 17:16:36 +0200 Subject: [PATCH 4/4] v3: check that tasks are deleted when the user deletes the acocunt and misc fixes --- .../v3/integration/user/DELETE-user.test.js | 25 ++++++++++++++++++- website/src/controllers/api-v3/tasks.js | 5 ++-- website/src/controllers/api-v3/user.js | 4 +++ website/src/models/user.js | 1 - 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/test/api/v3/integration/user/DELETE-user.test.js b/test/api/v3/integration/user/DELETE-user.test.js index 69e928e343..f954903857 100644 --- a/test/api/v3/integration/user/DELETE-user.test.js +++ b/test/api/v3/integration/user/DELETE-user.test.js @@ -5,7 +5,12 @@ import { generateUser, translate as t, } from '../../../../helpers/api-integration/v3'; -import { find } from 'lodash'; +import { + find, + each, + map, +} from 'lodash'; +import Q from 'q'; describe('DELETE /user', () => { let user; @@ -38,6 +43,24 @@ describe('DELETE /user', () => { }); it('deletes the user', async () => { + // gets the user's tasks ids + let ids = []; + each(user.tasksOrder, (idsForOrder) => { + ids.push(...idsForOrder); + }); + + expect(ids.length).to.be.above(0); // make sure the user has some task to delete + + await user.del('/user', { + password, + }); + + await Q.all(map(ids, id => { + return expect(checkExistence('tasks', id)).to.eventually.eql(false); + })); + }); + + it('delete the user\'s tasks', async () => { await user.del('/user', { password, }); diff --git a/website/src/controllers/api-v3/tasks.js b/website/src/controllers/api-v3/tasks.js index e2adcffedb..f931283102 100644 --- a/website/src/controllers/api-v3/tasks.js +++ b/website/src/controllers/api-v3/tasks.js @@ -399,6 +399,7 @@ api.scoreTask = { if (direction === 'up') user.fns.randomDrop({task, delta}, req); // If a todo was completed or uncompleted move it in or out of the user.tasksOrder.todos list + // TODO move to common code? if (task.type === 'todo') { if (!wasCompleted && task.completed) { removeFromArray(user.tasksOrder.todos, task._id); @@ -406,9 +407,7 @@ api.scoreTask = { let hasTask = removeFromArray(user.tasksOrder.todos, task._id); if (!hasTask) { user.tasksOrder.todos.push(task._id); // TODO push at the top? - } else { // If for some reason it hadn't been removed TODO ok? - user.tasksOrder.push(task._id); - } + } // If for some reason it hadn't been removed previously don't do anything TODO ok? } } diff --git a/website/src/controllers/api-v3/user.js b/website/src/controllers/api-v3/user.js index 445c72d34e..0267e9511b 100644 --- a/website/src/controllers/api-v3/user.js +++ b/website/src/controllers/api-v3/user.js @@ -219,6 +219,10 @@ api.deleteUser = { await Q.all(groupLeavePromises); + await Tasks.Task.remove({ + userId: user._id, + }).exec(); + await user.remove(); res.respond(200, {}); diff --git a/website/src/models/user.js b/website/src/models/user.js index b552236c1b..b9946fb3e7 100644 --- a/website/src/models/user.js +++ b/website/src/models/user.js @@ -508,7 +508,6 @@ export let schema = new Schema({ habits: [{type: String, ref: 'Task'}], dailys: [{type: String, ref: 'Task'}], todos: [{type: String, ref: 'Task'}], - completedTodos: [{type: String, ref: 'Task'}], rewards: [{type: String, ref: 'Task'}], }, extra: {type: Schema.Types.Mixed, default: () => {