Added tests for challenge task scoring and moved scoring code to model

This commit is contained in:
Keith Holliday
2016-03-18 14:00:14 -05:00
parent 99ff440abc
commit b46219adc5
6 changed files with 265 additions and 42 deletions

View File

@@ -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);
});
});
});

View File

@@ -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;

View 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);
});
});
});
});

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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