Files
habitica/test/api/v3/integration/hall/PUT-hall_heores_heroId.test.js
Phillip Thelen 379afa9554 Improve Adminpanel with local logs (#15404)
* log armoire, quoest response and cron events to history

* show user history in admin panel

* allow stats to be edited from admin panel

* Improve admin panel stats input

* improve setting client in history

* fix tests

* fix lint

* fix armoire buying issue

* Improve hero saving

* Formatting fix

* Improve user history logging

* allow class to be changed from admin panel

* make terminating subscriptions easier

* support decimal extraMonths

* Fix editing some achievements in admin panel

* log if a user invites party to quest

* Log more quest events into user history

* make userhistory length configurable

* fix some numbered achievements

* fix extraMonths field

* Automatically set up group plan subs with admin panel

* show party info nicer in admin panel

* improve admin panel sub handling

* add missing brace

* display when there are unsaved changes

* fix setting group plan

* fix showing group id

* Display group plan info in admin panel

* fix setting hourglass promo date

* Improve termination handling in admin panel

* reload data after certain save events in admin panel

* remove console

* fix plan.extraMonths not being reset if terminating a sub

* add more options when cancelling subs

* reload data after group plan change

* Add a way to remove users from a party

* fix issue with removing user from party

* pass party id correctly

* correctly call async function

* Improve sub display in admin panel

* fix line length

* fix line

* shorter

* plaid

* fix(lint): vue code style

---------

Co-authored-by: Kalista Payne <sabrecat@gmail.com>
2025-03-17 16:48:21 -05:00

411 lines
14 KiB
JavaScript

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',
'stats',
];
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);
});
});