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 moment from 'moment';
|
||||
import sinon from 'sinon'; // eslint-disable-line no-shadow
|
||||
|
||||
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;
|
||||
}
|
||||
import { generateHistory } from '../../../../helpers/api-unit.helper.js';
|
||||
|
||||
describe('preenHistory', () => {
|
||||
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 Group } from '../../website/src/models/group';
|
||||
import mongo from './mongo'; // eslint-disable-line
|
||||
import moment from 'moment';
|
||||
|
||||
afterEach((done) => {
|
||||
sandbox.restore();
|
||||
@@ -48,3 +49,18 @@ export function generateReq (options = {}) {
|
||||
export function generateNext (func) {
|
||||
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 Q from 'q';
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { preenHistory } from '../../libs/api-v3/preening';
|
||||
|
||||
let api = {};
|
||||
|
||||
@@ -438,31 +436,7 @@ api.scoreTask = {
|
||||
_id: task.challenge.taskId,
|
||||
}).exec();
|
||||
|
||||
chalTask.value += 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();
|
||||
await chalTask.scoreChallengeTask(delta);
|
||||
} catch (e) {
|
||||
// TODO handle
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import validator from 'validator';
|
||||
import moment from 'moment';
|
||||
import baseModel from '../libs/api-v3/baseModel';
|
||||
import _ from 'lodash';
|
||||
import { preenHistory } from '../libs/api-v3/preening';
|
||||
|
||||
let Schema = mongoose.Schema;
|
||||
let discriminatorOptions = {
|
||||
@@ -75,6 +76,38 @@ TaskSchema.statics.sanitizeReminder = function sanitizeReminder (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);
|
||||
|
||||
// habits and dailies shared fields
|
||||
|
||||
Reference in New Issue
Block a user