import { v4 as generateUUID } from 'uuid'; import { model as User } from '../../../../../website/server/models/user'; import { generateUser, translate as t, } from '../../../../helpers/api-integration/v3'; describe('PUT /heroes/:heroId', () => { let user; const heroFields = [ '_id', 'auth', 'balance', 'contributor', 'flags', 'items', 'lastCron', 'party', 'preferences', 'profile', 'purchased', 'secret', 'permissions', 'achievements', ]; before(async () => { user = await generateUser({ 'permissions.userSupport': true }); }); it('requires the caller to be an admin', async () => { const nonAdmin = await generateUser(); await expect(nonAdmin.put(`/hall/heroes/${user._id}`)).to.eventually.be.rejected.and.eql({ code: 401, error: 'NotAuthorized', message: t('noPrivAccess'), }); }); it('validates req.params.heroId', async () => { await expect(user.put('/hall/heroes/invalidUUID')).to.eventually.be.rejected.and.eql({ code: 400, error: 'BadRequest', message: t('invalidReqParams'), }); }); it('handles non-existing heroes', async () => { const dummyId = generateUUID(); await expect(user.put(`/hall/heroes/${dummyId}`)).to.eventually.be.rejected.and.eql({ code: 404, error: 'NotFound', message: t('userWithIDNotFound', { userId: dummyId }), }); }); it('change contributor level, balance, ads', async () => { const hero = await generateUser(); const prevBlockState = hero.auth.blocked; const prevSleepState = hero.preferences.sleep; const heroRes = await user.put(`/hall/heroes/${hero._id}`, { balance: 3, contributor: { level: 1 }, purchased: { ads: true }, }); // test response expect(heroRes).to.have.all.keys(heroFields); // works as: object has all and only these keys expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.balance).to.equal(3 + 2.5); // 3+2.5 for first contrib level expect(heroRes.contributor.level).to.equal(1); expect(heroRes.purchased.ads).to.equal(true); // test hero values await hero.sync(); expect(hero.balance).to.equal(3 + 2.5); // 3+2.5 for first contrib level expect(hero.contributor.level).to.equal(1); expect(hero.purchased.ads).to.equal(true); expect(hero.auth.blocked).to.equal(prevBlockState); expect(hero.preferences.sleep).to.equal(prevSleepState); expect(hero.notifications.length).to.equal(1); expect(hero.notifications[0].type).to.equal('NEW_CONTRIBUTOR_LEVEL'); }); it('block a user', async () => { const hero = await generateUser(); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { auth: { blocked: true }, preferences: { sleep: true }, }); // test response values expect(heroRes.auth.blocked).to.equal(true); // test hero values await hero.sync(); expect(hero.auth.blocked).to.equal(true); expect(hero.preferences.sleep).to.equal(true); }); it('unblock a user', async () => { const hero = await generateUser(); const prevSleepState = hero.preferences.sleep; const heroRes = await user.put(`/hall/heroes/${hero._id}`, { auth: { blocked: false }, }); // test response values expect(heroRes.auth.blocked).to.equal(false); // test hero values await hero.sync(); expect(hero.auth.blocked).to.equal(false); expect(hero.preferences.sleep).to.equal(prevSleepState); }); it('updates chatRevoked flag', async () => { const hero = await generateUser(); await user.put(`/hall/heroes/${hero._id}`, { flags: { chatRevoked: true }, }); await hero.sync(); expect(hero.flags.chatRevoked).to.eql(true); }); it('updates chatShadowMuted flag', async () => { const hero = await generateUser(); await user.put(`/hall/heroes/${hero._id}`, { flags: { chatShadowMuted: true }, }); await hero.sync(); expect(hero.flags.chatShadowMuted).to.eql(true); }); it('updates contributor level', async () => { const hero = await generateUser({ contributor: { level: 5 }, }); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { contributor: { level: 6 }, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.balance).to.equal(15); // 0+15 for sixth contrib level expect(heroRes.contributor.level).to.equal(6); expect(heroRes.items.pets['Dragon-Hydra']).to.equal(5); // test hero values await hero.sync(); expect(hero.balance).to.equal(15); // 0+15 for sixth contrib level expect(hero.contributor.level).to.equal(6); expect(hero.items.pets['Dragon-Hydra']).to.equal(5); }); it('updates contributor data', async () => { const hero = await generateUser({ contributor: { level: 5 }, }); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { contributor: { text: 'Astronaut' }, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.contributor.level).to.equal(5); // doesn't modify previous values expect(heroRes.contributor.text).to.equal('Astronaut'); // test hero values await hero.sync(); expect(hero.contributor.level).to.equal(5); // doesn't modify previous values expect(hero.contributor.text).to.equal('Astronaut'); }); it('updates contributor secret', async () => { const secretText = 'my super hero'; const hero = await generateUser({ contributor: { level: 5 }, secret: { text: 'supr hro typo', }, }); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { contributor: { text: 'Astronaut' }, secret: { text: secretText, }, }); // test response // works as: object has all and only these keys expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.contributor.level).to.equal(5); // doesn't modify previous values expect(heroRes.contributor.text).to.equal('Astronaut'); expect(heroRes.secret.text).to.equal(secretText); // test hero values await hero.sync(); expect(hero.contributor.level).to.equal(5); // doesn't modify previous values expect(hero.contributor.text).to.equal('Astronaut'); expect(hero.secret.text).to.equal(secretText); }); it('updates items', async () => { const hero = await generateUser(); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { itemPath: 'items.special.snowball', itemVal: 5, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.items.special.snowball).to.equal(5); // test hero values await hero.sync(); expect(hero.items.special.snowball).to.equal(5); }); it('does not accidentally update API Token', async () => { // This test has been included because hall.js will contain code to produce // a truncated version of the API Token, and we want to be sure that // the real Token is not modified by bugs in that code. const hero = await generateUser(); const originalToken = hero.apiToken; // make any change to the user except the Token await user.put(`/hall/heroes/${hero._id}`, { contributor: { text: 'Astronaut' }, }); const updatedHero = await User.findById(hero._id).exec(); expect(updatedHero.apiToken).to.equal(originalToken); expect(updatedHero.apiTokenObscured).to.not.exist; }); it('does update API Token when admin changes it', async () => { const hero = await generateUser(); const originalToken = hero.apiToken; // change the user's API Token await user.put(`/hall/heroes/${hero._id}`, { changeApiToken: true, }); const updatedHero = await User.findById(hero._id).exec(); expect(updatedHero.apiToken).to.not.equal(originalToken); expect(updatedHero.apiTokenObscured).to.not.exist; }); it('updates purchased hair customization', async () => { const hero = await generateUser(); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { purchasedPath: 'purchased.hair.bangs.1', purchasedVal: true, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.purchased.hair.bangs['1']).to.equal(true); // test hero values await hero.sync(); expect(hero.purchased.hair.bangs['1']).to.equal(true); }); it('updates purchased customization', async () => { const hero = await generateUser(); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { purchasedPath: 'purchased.background.beach', purchasedVal: true, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.purchased.background.beach).to.equal(true); // test hero values await hero.sync(); expect(hero.purchased.background.beach).to.equal(true); }); it('updates giving nested achievement', async () => { const hero = await generateUser(); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { achievementPath: 'achievements.quests.dilatory', achievementVal: 2, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.achievements.quests.dilatory).to.equal(2); // test hero values await hero.sync(); expect(hero.achievements.quests.dilatory).to.equal(2); }); it('updates taking away nested achievement', async () => { const hero = await generateUser({ 'achievements.quests.dilatory': 3 }); expect(hero.achievements.quests.dilatory).to.equal(3); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { achievementPath: 'achievements.quests.dilatory', achievementVal: 0, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.achievements.quests.dilatory).to.equal(0); // test hero values await hero.sync(); expect(hero.achievements.quests.dilatory).to.equal(0); }); it('updates giving achievement', async () => { const hero = await generateUser(); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { achievementPath: 'achievements.partyOn', achievementVal: true, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.achievements.partyOn).to.equal(true); // test hero values await hero.sync(); expect(hero.achievements.partyOn).to.equal(true); }); it('updates taking away achievement', async () => { const hero = await generateUser({ 'achievements.partyUp': true }); expect(hero.achievements.partyUp).to.equal(true); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { achievementPath: 'achievements.partyUp', achievementVal: false, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.achievements.partyUp).to.equal(false); // test hero values await hero.sync(); expect(hero.achievements.partyUp).to.equal(false); }); it('updates giving numbered achievement', async () => { const hero = await generateUser(); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { achievementPath: 'achievements.streak', achievementVal: 42, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.achievements.streak).to.equal(42); // test hero values await hero.sync(); expect(hero.achievements.streak).to.equal(42); }); it('updates setting numbered achievement to 0', async () => { const hero = await generateUser({ 'achievements.streak': 42 }); expect(hero.achievements.streak).to.equal(42); const heroRes = await user.put(`/hall/heroes/${hero._id}`, { achievementPath: 'achievements.streak', achievementVal: 0, }); // test response expect(heroRes).to.have.all.keys(heroFields); expect(heroRes.auth.local).not.to.have.keys(['salt', 'hashed_password']); expect(heroRes.profile).to.have.all.keys(['name']); // test response values expect(heroRes.achievements.streak).to.equal(0); // test hero values await hero.sync(); expect(hero.achievements.streak).to.equal(0); }); });