mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
Added tests for challenge task scoring and moved scoring code to model
This commit is contained in:
@@ -0,0 +1,140 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
generateGroup,
|
||||||
|
generateChallenge,
|
||||||
|
} from '../../../../../helpers/api-integration/v3';
|
||||||
|
import { find } from 'lodash';
|
||||||
|
|
||||||
|
describe('POST /tasks/:id/score/:direction', () => {
|
||||||
|
let user;
|
||||||
|
let guild;
|
||||||
|
let challenge;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
user = await generateUser();
|
||||||
|
guild = await generateGroup(user);
|
||||||
|
challenge = await generateChallenge(user, guild);
|
||||||
|
});
|
||||||
|
|
||||||
|
context('habits', () => {
|
||||||
|
let habit;
|
||||||
|
let usersChallengeTaskId;
|
||||||
|
let previousTaskHistory;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
habit = await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||||
|
text: 'test habit',
|
||||||
|
type: 'habit',
|
||||||
|
});
|
||||||
|
let updatedUser = await user.get('/user');
|
||||||
|
usersChallengeTaskId = updatedUser.tasksOrder.habits[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scores and adds history', async () => {
|
||||||
|
await user.post(`/tasks/${usersChallengeTaskId}/score/up`);
|
||||||
|
|
||||||
|
let tasks = await user.get(`/tasks/challenge/${challenge._id}`);
|
||||||
|
let task = find(tasks, {_id: habit._id});
|
||||||
|
previousTaskHistory = task.history[0];
|
||||||
|
|
||||||
|
expect(task.value).to.equal(1);
|
||||||
|
expect(task.history).to.have.lengthOf(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update the history', async () => {
|
||||||
|
await user.post(`/tasks/${usersChallengeTaskId}/score/up`);
|
||||||
|
|
||||||
|
let tasks = await user.get(`/tasks/challenge/${challenge._id}`);
|
||||||
|
let task = find(tasks, {_id: habit._id});
|
||||||
|
|
||||||
|
expect(task.history).to.have.lengthOf(1);
|
||||||
|
expect(task.history[0].date).to.not.equal(previousTaskHistory.date);
|
||||||
|
expect(task.history[0].value).to.not.equal(previousTaskHistory.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('dailies', () => {
|
||||||
|
let daily;
|
||||||
|
let usersChallengeTaskId;
|
||||||
|
let previousTaskHistory;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
daily = await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||||
|
text: 'test daily',
|
||||||
|
type: 'daily',
|
||||||
|
});
|
||||||
|
let updatedUser = await user.get('/user');
|
||||||
|
usersChallengeTaskId = updatedUser.tasksOrder.dailys[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it scores and adds history', async () => {
|
||||||
|
await user.post(`/tasks/${usersChallengeTaskId}/score/up`);
|
||||||
|
|
||||||
|
let tasks = await user.get(`/tasks/challenge/${challenge._id}`);
|
||||||
|
let task = find(tasks, {_id: daily._id});
|
||||||
|
previousTaskHistory = task.history[0];
|
||||||
|
|
||||||
|
expect(task.history).to.have.lengthOf(1);
|
||||||
|
expect(task.value).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update the history', async () => {
|
||||||
|
await user.post(`/tasks/${usersChallengeTaskId}/score/up`);
|
||||||
|
|
||||||
|
let tasks = await user.get(`/tasks/challenge/${challenge._id}`);
|
||||||
|
let task = find(tasks, {_id: daily._id});
|
||||||
|
|
||||||
|
expect(task.history).to.have.lengthOf(1);
|
||||||
|
expect(task.history[0].date).to.not.equal(previousTaskHistory.date);
|
||||||
|
expect(task.history[0].value).to.not.equal(previousTaskHistory.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('todos', () => {
|
||||||
|
let todo;
|
||||||
|
let usersChallengeTaskId;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
todo = await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||||
|
text: 'test todo',
|
||||||
|
type: 'todo',
|
||||||
|
});
|
||||||
|
let updatedUser = await user.get('/user');
|
||||||
|
usersChallengeTaskId = updatedUser.tasksOrder.todos[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scores but does not add history', async () => {
|
||||||
|
await user.post(`/tasks/${usersChallengeTaskId}/score/up`);
|
||||||
|
|
||||||
|
let tasks = await user.get(`/tasks/challenge/${challenge._id}`);
|
||||||
|
let task = find(tasks, {_id: todo._id});
|
||||||
|
|
||||||
|
expect(task.history).to.not.exist;
|
||||||
|
expect(task.value).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('rewards', () => {
|
||||||
|
let reward;
|
||||||
|
let usersChallengeTaskId;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
reward = await user.post(`/tasks/challenge/${challenge._id}`, {
|
||||||
|
text: 'test reward',
|
||||||
|
type: 'reward',
|
||||||
|
});
|
||||||
|
let updatedUser = await user.get('/user');
|
||||||
|
usersChallengeTaskId = updatedUser.tasksOrder.todos[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not score', async () => {
|
||||||
|
await user.post(`/tasks/${usersChallengeTaskId}/score/up`);
|
||||||
|
|
||||||
|
let tasks = await user.get(`/tasks/challenge/${challenge._id}`);
|
||||||
|
let task = find(tasks, {_id: reward._id});
|
||||||
|
|
||||||
|
expect(task.history).to.not.exist;
|
||||||
|
expect(task.value).to.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,21 +1,7 @@
|
|||||||
import { preenHistory } from '../../../../../website/src/libs/api-v3/preening';
|
import { preenHistory } from '../../../../../website/src/libs/api-v3/preening';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import sinon from 'sinon'; // eslint-disable-line no-shadow
|
import sinon from 'sinon'; // eslint-disable-line no-shadow
|
||||||
|
import { generateHistory } from '../../../../helpers/api-unit.helper.js';
|
||||||
function generateHistory (days) {
|
|
||||||
let history = [];
|
|
||||||
let now = Number(moment().toDate());
|
|
||||||
|
|
||||||
while (days > 0) {
|
|
||||||
history.push({
|
|
||||||
value: days,
|
|
||||||
date: Number(moment(now).subtract(days, 'days').toDate()),
|
|
||||||
});
|
|
||||||
days--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return history;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('preenHistory', () => {
|
describe('preenHistory', () => {
|
||||||
let clock;
|
let clock;
|
||||||
|
|||||||
74
test/api/v3/unit/models/task.test.js
Normal file
74
test/api/v3/unit/models/task.test.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { model as Challenge } from '../../../../../website/src/models/challenge';
|
||||||
|
import { model as Group } from '../../../../../website/src/models/group';
|
||||||
|
import { model as User } from '../../../../../website/src/models/user';
|
||||||
|
import * as Tasks from '../../../../../website/src/models/task';
|
||||||
|
import { each } from 'lodash';
|
||||||
|
import { generateHistory } from '../../../../helpers/api-unit.helper.js';
|
||||||
|
|
||||||
|
describe('Task Model', () => {
|
||||||
|
let guild, leader, challenge, task;
|
||||||
|
let tasksToTest = {
|
||||||
|
habit: {
|
||||||
|
text: 'test habit',
|
||||||
|
type: 'habit',
|
||||||
|
up: false,
|
||||||
|
down: true,
|
||||||
|
},
|
||||||
|
daily: {
|
||||||
|
text: 'test daily',
|
||||||
|
type: 'daily',
|
||||||
|
frequency: 'daily',
|
||||||
|
everyX: 5,
|
||||||
|
startDate: new Date(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
guild = new Group({
|
||||||
|
name: 'test guild',
|
||||||
|
type: 'guild',
|
||||||
|
});
|
||||||
|
|
||||||
|
leader = new User({
|
||||||
|
guilds: [guild._id],
|
||||||
|
});
|
||||||
|
|
||||||
|
guild.leader = leader._id;
|
||||||
|
|
||||||
|
challenge = new Challenge({
|
||||||
|
name: 'Test Challenge',
|
||||||
|
shortName: 'Test',
|
||||||
|
leader: leader._id,
|
||||||
|
group: guild._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
leader.challenges = [challenge._id];
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
guild.save(),
|
||||||
|
leader.save(),
|
||||||
|
challenge.save(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
each(tasksToTest, (taskValue, taskType) => {
|
||||||
|
context(`${taskType}`, () => {
|
||||||
|
beforeEach(async() => {
|
||||||
|
task = new Tasks[`${taskType}`](Tasks.Task.sanitizeCreate(taskValue));
|
||||||
|
task.challenge.id = challenge._id;
|
||||||
|
task.history = generateHistory(396);
|
||||||
|
await task.save();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preens challenge tasks history when scored', async () => {
|
||||||
|
let historyLengthBeforePreen = task.history.length;
|
||||||
|
|
||||||
|
await task.scoreChallengeTask(1.2);
|
||||||
|
|
||||||
|
let updatedTask = await Tasks.Task.findOne({_id: task._id});
|
||||||
|
|
||||||
|
expect(historyLengthBeforePreen).to.be.greaterThan(updatedTask.history.length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -4,6 +4,7 @@ import { defaultsDeep as defaults } from 'lodash';
|
|||||||
import { model as User } from '../../website/src/models/user';
|
import { model as User } from '../../website/src/models/user';
|
||||||
import { model as Group } from '../../website/src/models/group';
|
import { model as Group } from '../../website/src/models/group';
|
||||||
import mongo from './mongo'; // eslint-disable-line
|
import mongo from './mongo'; // eslint-disable-line
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
afterEach((done) => {
|
afterEach((done) => {
|
||||||
sandbox.restore();
|
sandbox.restore();
|
||||||
@@ -48,3 +49,18 @@ export function generateReq (options = {}) {
|
|||||||
export function generateNext (func) {
|
export function generateNext (func) {
|
||||||
return func || sandbox.stub();
|
return func || sandbox.stub();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateHistory (days) {
|
||||||
|
let history = [];
|
||||||
|
let now = Number(moment().toDate());
|
||||||
|
|
||||||
|
while (days > 0) {
|
||||||
|
history.push({
|
||||||
|
value: days,
|
||||||
|
date: Number(moment(now).subtract(days, 'days').toDate()),
|
||||||
|
});
|
||||||
|
days--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import {
|
|||||||
import common from '../../../../common';
|
import common from '../../../../common';
|
||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import moment from 'moment';
|
|
||||||
import { preenHistory } from '../../libs/api-v3/preening';
|
|
||||||
|
|
||||||
let api = {};
|
let api = {};
|
||||||
|
|
||||||
@@ -438,31 +436,7 @@ api.scoreTask = {
|
|||||||
_id: task.challenge.taskId,
|
_id: task.challenge.taskId,
|
||||||
}).exec();
|
}).exec();
|
||||||
|
|
||||||
chalTask.value += delta;
|
await chalTask.scoreChallengeTask(delta);
|
||||||
|
|
||||||
if (chalTask.type === 'habit' || chalTask.type === 'daily') {
|
|
||||||
// Add only one history entry per day
|
|
||||||
if (moment(chalTask.history[chalTask.history.length - 1].date).isSame(new Date(), 'day')) {
|
|
||||||
chalTask.history[chalTask.history.length - 1] = {
|
|
||||||
date: Number(new Date()),
|
|
||||||
value: chalTask.value,
|
|
||||||
};
|
|
||||||
chalTask.markModified(`history.${chalTask.history.length - 1}`);
|
|
||||||
} else {
|
|
||||||
chalTask.history.push({
|
|
||||||
date: Number(new Date()),
|
|
||||||
value: chalTask.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only preen task history once a day when the task is scored first
|
|
||||||
if (chalTask.history.length > 365) {
|
|
||||||
chalTask.history = preenHistory(chalTask.history, true); // true means the challenge will retain as much entries as a subscribed user
|
|
||||||
chalTask.markModified(`history.${chalTask.history.length - 1}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await chalTask.save();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO handle
|
// TODO handle
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import validator from 'validator';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import baseModel from '../libs/api-v3/baseModel';
|
import baseModel from '../libs/api-v3/baseModel';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { preenHistory } from '../libs/api-v3/preening';
|
||||||
|
|
||||||
let Schema = mongoose.Schema;
|
let Schema = mongoose.Schema;
|
||||||
let discriminatorOptions = {
|
let discriminatorOptions = {
|
||||||
@@ -75,6 +76,38 @@ TaskSchema.statics.sanitizeReminder = function sanitizeReminder (reminderObj) {
|
|||||||
return reminderObj;
|
return reminderObj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TaskSchema.methods.scoreChallengeTask = async function scoreChallengeTask (delta) {
|
||||||
|
let chalTask = this;
|
||||||
|
|
||||||
|
chalTask.value += delta;
|
||||||
|
|
||||||
|
if (chalTask.type === 'habit' || chalTask.type === 'daily') {
|
||||||
|
// Add only one history entry per day
|
||||||
|
let lastChallengHistoryIndex = chalTask.history.length - 1;
|
||||||
|
|
||||||
|
if (chalTask.history[lastChallengHistoryIndex] &&
|
||||||
|
moment(chalTask.history[lastChallengHistoryIndex].date).isSame(new Date(), 'day')) {
|
||||||
|
chalTask.history[lastChallengHistoryIndex] = {
|
||||||
|
date: Number(new Date()),
|
||||||
|
value: chalTask.value,
|
||||||
|
};
|
||||||
|
chalTask.markModified(`history.${lastChallengHistoryIndex}`);
|
||||||
|
} else {
|
||||||
|
chalTask.history.push({
|
||||||
|
date: Number(new Date()),
|
||||||
|
value: chalTask.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only preen task history once a day when the task is scored first
|
||||||
|
if (chalTask.history.length > 365) {
|
||||||
|
chalTask.history = preenHistory(chalTask.history, true); // true means the challenge will retain as much entries as a subscribed user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await chalTask.save();
|
||||||
|
};
|
||||||
|
|
||||||
export let Task = mongoose.model('Task', TaskSchema);
|
export let Task = mongoose.model('Task', TaskSchema);
|
||||||
|
|
||||||
// habits and dailies shared fields
|
// habits and dailies shared fields
|
||||||
|
|||||||
Reference in New Issue
Block a user