mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
Ported sell function. Add unit tests. Create sell route. Add integration tests
This commit is contained in:
@@ -151,5 +151,8 @@
|
|||||||
"countRequired": "\"req.query.count\" is required.",
|
"countRequired": "\"req.query.count\" is required.",
|
||||||
"petsReleased": "Pets released.",
|
"petsReleased": "Pets released.",
|
||||||
"mountsAndPetsReleased": "Mounts and pets released",
|
"mountsAndPetsReleased": "Mounts and pets released",
|
||||||
"mountsReleased": "Mounts released"
|
"mountsReleased": "Mounts released",
|
||||||
|
"typeNotSellable": "Type is not sellable. Must be one of the following <%= acceptedTypes %>",
|
||||||
|
"userItemsKeyNotFound": "Key not found for user.items <%= type %>",
|
||||||
|
"sold": "You sold a <%= key %> <%= type %>"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ import openMysteryItem from './ops/openMysteryItem';
|
|||||||
import releasePets from './ops/releasePets';
|
import releasePets from './ops/releasePets';
|
||||||
import releaseBoth from './ops/releaseBoth';
|
import releaseBoth from './ops/releaseBoth';
|
||||||
import releaseMounts from './ops/releaseMounts';
|
import releaseMounts from './ops/releaseMounts';
|
||||||
|
import sell from './ops/sell';
|
||||||
|
|
||||||
api.ops = {
|
api.ops = {
|
||||||
scoreTask,
|
scoreTask,
|
||||||
@@ -143,6 +144,7 @@ api.ops = {
|
|||||||
releasePets,
|
releasePets,
|
||||||
releaseBoth,
|
releaseBoth,
|
||||||
releaseMounts,
|
releaseMounts,
|
||||||
|
sell,
|
||||||
};
|
};
|
||||||
|
|
||||||
import handleTwoHanded from './fns/handleTwoHanded';
|
import handleTwoHanded from './fns/handleTwoHanded';
|
||||||
|
|||||||
@@ -1,23 +1,42 @@
|
|||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
|
import i18n from '../../../common/script/i18n';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import splitWhitespace from '../libs/splitWhitespace';
|
import splitWhitespace from '../libs/splitWhitespace';
|
||||||
|
import {
|
||||||
|
NotFound,
|
||||||
|
NotAuthorized,
|
||||||
|
BadRequest,
|
||||||
|
} from '../libs/errors';
|
||||||
|
|
||||||
module.exports = function(user, req, cb) {
|
const ACCEPTEDTYPES = ['eggs', 'hatchingPotions', 'food'];
|
||||||
var key, ref, type;
|
|
||||||
ref = req.params, key = ref.key, type = ref.type;
|
module.exports = function sell (user, req = {}) {
|
||||||
if (type !== 'eggs' && type !== 'hatchingPotions' && type !== 'food') {
|
let key = _.get(req.params, 'key');
|
||||||
return typeof cb === "function" ? cb({
|
let type = _.get(req.params, 'type');
|
||||||
code: 404,
|
|
||||||
message: ":type not found. Must bes in [eggs, hatchingPotions, food]"
|
if (!type) {
|
||||||
}) : void 0;
|
throw new BadRequest(i18n.t('typeRequired', req.language));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
throw new BadRequest(i18n.t('keyRequired', req.language));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ACCEPTEDTYPES.indexOf(type) === -1) {
|
||||||
|
throw new NotAuthorized(i18n.t('typeNotSellable', {acceptedTypes: ACCEPTEDTYPES.join(', ')}, req.language));
|
||||||
|
}
|
||||||
|
|
||||||
if (!user.items[type][key]) {
|
if (!user.items[type][key]) {
|
||||||
return typeof cb === "function" ? cb({
|
throw new NotFound(i18n.t('userItemsKeyNotFound', {type}, req.language));
|
||||||
code: 404,
|
|
||||||
message: ":key not found for user.items." + type
|
|
||||||
}) : void 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.items[type][key]--;
|
user.items[type][key]--;
|
||||||
user.stats.gp += content[type][key].value;
|
user.stats.gp += content[type][key].value;
|
||||||
return typeof cb === "function" ? cb(null, _.pick(user, splitWhitespace('stats items'))) : void 0;
|
|
||||||
|
let response = {
|
||||||
|
data: _.pick(user, splitWhitespace('stats items')),
|
||||||
|
message: i18n.t('sold', {type, key}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return response;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ const COMMON_FILES = [
|
|||||||
'!./common/script/ops/reroll.js',
|
'!./common/script/ops/reroll.js',
|
||||||
'!./common/script/ops/reset.js',
|
'!./common/script/ops/reset.js',
|
||||||
'!./common/script/ops/revive.js',
|
'!./common/script/ops/revive.js',
|
||||||
'!./common/script/ops/sell.js',
|
|
||||||
'!./common/script/ops/sortTag.js',
|
'!./common/script/ops/sortTag.js',
|
||||||
'!./common/script/ops/sortTask.js',
|
'!./common/script/ops/sortTask.js',
|
||||||
'!./common/script/ops/unlock.js',
|
'!./common/script/ops/unlock.js',
|
||||||
|
|||||||
42
test/api/v3/integration/user/POST-user_sell.test.js
Normal file
42
test/api/v3/integration/user/POST-user_sell.test.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration/v3';
|
||||||
|
import content from '../../../../../common/script/content';
|
||||||
|
|
||||||
|
describe('POST /user/sell/:type/:key', () => {
|
||||||
|
let user;
|
||||||
|
let type = 'eggs';
|
||||||
|
let key = 'Wolf';
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
// More tests in common code unit tests
|
||||||
|
|
||||||
|
it('returns an error when user does not have item', async () => {
|
||||||
|
await expect(user.post(`/user/sell/${type}/${key}`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('userItemsKeyNotFound', {type}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sells an item', async () => {
|
||||||
|
await user.update({
|
||||||
|
items: {
|
||||||
|
eggs: {
|
||||||
|
Wolf: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = await user.post(`/user/sell/${type}/${key}`);
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(response.message).to.equal(t('sold', {type, key}));
|
||||||
|
expect(user.stats.gp).to.equal(content[type][key].value);
|
||||||
|
});
|
||||||
|
});
|
||||||
81
test/common/ops/sell.js
Normal file
81
test/common/ops/sell.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import sell from '../../../common/script/ops/sell';
|
||||||
|
import i18n from '../../../common/script/i18n';
|
||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
} from '../../helpers/common.helper';
|
||||||
|
import {
|
||||||
|
NotAuthorized,
|
||||||
|
BadRequest,
|
||||||
|
NotFound,
|
||||||
|
} from '../../../common/script/libs/errors';
|
||||||
|
import content from '../../../common/script/content/index';
|
||||||
|
|
||||||
|
describe('shared.ops.sell', () => {
|
||||||
|
let user;
|
||||||
|
let type = 'eggs';
|
||||||
|
let key = 'Wolf';
|
||||||
|
let acceptedTypes = ['eggs', 'hatchingPotions', 'food'];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = generateUser();
|
||||||
|
user.items[type][key] = 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when type is not provided', (done) => {
|
||||||
|
try {
|
||||||
|
sell(user);
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
|
expect(err.message).to.equal(i18n.t('typeRequired'));
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when key is not provided', (done) => {
|
||||||
|
try {
|
||||||
|
sell(user, {params: { type } });
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
|
expect(err.message).to.equal(i18n.t('keyRequired'));
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when non-sellable type is provided', (done) => {
|
||||||
|
let nonSellableType = 'nonSellableType';
|
||||||
|
|
||||||
|
try {
|
||||||
|
sell(user, {params: { type: nonSellableType, key } });
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
|
expect(err.message).to.equal(i18n.t('typeNotSellable', {acceptedTypes: acceptedTypes.join(', ')}));
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when key is not found with type provided', (done) => {
|
||||||
|
let fakeKey = 'fakeKey';
|
||||||
|
|
||||||
|
try {
|
||||||
|
sell(user, {params: { type, key: fakeKey } });
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).to.be.an.instanceof(NotFound);
|
||||||
|
expect(err.message).to.equal(i18n.t('userItemsKeyNotFound', {type}));
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reduces item count from user', () => {
|
||||||
|
let response = sell(user, {params: { type, key } });
|
||||||
|
|
||||||
|
expect(response.message).to.equal(i18n.t('sold', {type, key}));
|
||||||
|
expect(user.items[type][key]).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('increases user\'s gold', () => {
|
||||||
|
let response = sell(user, {params: { type, key } });
|
||||||
|
|
||||||
|
expect(response.message).to.equal(i18n.t('sold', {type, key}));
|
||||||
|
expect(user.stats.gp).to.equal(content[type][key].value);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -828,4 +828,24 @@ api.userReleaseMounts = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @api {post} /user/sell/:type/:key Sells user's items.
|
||||||
|
* @apiVersion 3.0.0
|
||||||
|
* @apiName UserSell
|
||||||
|
* @apiGroup User
|
||||||
|
*
|
||||||
|
* @apiSuccess {Object} data `stats items`
|
||||||
|
*/
|
||||||
|
api.userSell = {
|
||||||
|
method: 'POST',
|
||||||
|
middlewares: [authWithHeaders(), cron],
|
||||||
|
url: '/user/sell/:type/:key',
|
||||||
|
async handler (req, res) {
|
||||||
|
let user = res.locals.user;
|
||||||
|
let sellResponse = common.ops.sell(user, req);
|
||||||
|
await user.save();
|
||||||
|
res.respond(200, sellResponse);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = api;
|
module.exports = api;
|
||||||
|
|||||||
Reference in New Issue
Block a user