From d9e786ebaaf9f5fb50e6046fa22780d3aeb29914 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Wed, 16 Dec 2015 12:29:03 +0100 Subject: [PATCH] checklists tests --- ...LETE-tasks_taskId_checklist_itemId.test.js | 86 ++++++++++++++++++ .../POST-tasks_taskId_checklist.test.js | 76 ++++++++++++++++ ...asks_taskId_checklist_itemId_score.test.js | 85 ++++++++++++++++++ .../PUT-tasks_taskId_checklist_itemId.test.js | 87 +++++++++++++++++++ website/src/controllers/api-v3/tasks.js | 5 +- website/src/models/task.js | 6 ++ 6 files changed, 342 insertions(+), 3 deletions(-) diff --git a/test/api/v3/integration/tasks/checklists/DELETE-tasks_taskId_checklist_itemId.test.js b/test/api/v3/integration/tasks/checklists/DELETE-tasks_taskId_checklist_itemId.test.js index e69de29bb2..013ba69725 100644 --- a/test/api/v3/integration/tasks/checklists/DELETE-tasks_taskId_checklist_itemId.test.js +++ b/test/api/v3/integration/tasks/checklists/DELETE-tasks_taskId_checklist_itemId.test.js @@ -0,0 +1,86 @@ +import { + generateUser, + requester, + translate as t, +} from '../../../../../helpers/api-integration.helper'; +import { v4 as generateUUID } from 'uuid'; + +describe('DELETE /tasks/:taskId/checklist/:itemId', () => { + let user, api; + + before(() => { + return generateUser().then((generatedUser) => { + user = generatedUser; + api = requester(user); + }); + }); + + it('deletes a checklist item', () => { + let task; + + return api.post('/tasks', { + type: 'daily', + text: 'Daily with checklist', + }).then(createdTask => { + task = createdTask; + return api.post(`/tasks/${task._id}/checklist`, {text: 'Checklist Item 1', completed: false}); + }).then((savedTask) => { + return api.del(`/tasks/${task._id}/checklist/${savedTask.checklist[0]._id}`); + }).then(() => { + return api.get(`/tasks/${task._id}`); + }).then((savedTask) => { + expect(savedTask.checklist.length).to.equal(0); + }); + }); + + it('does not work with habits', () => { + let habit; + return expect(api.post('/tasks', { + type: 'habit', + text: 'habit with checklist', + }).then(createdTask => { + habit = createdTask; + return api.del(`/tasks/${habit._id}/checklist/${generateUUID()}`); + })).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('checklistOnlyDailyTodo'), + }); + }); + + it('does not work with rewards', () => { + let reward; + return expect(api.post('/tasks', { + type: 'reward', + text: 'reward with checklist', + }).then(createdTask => { + reward = createdTask; + return api.del(`/tasks/${reward._id}/checklist/${generateUUID()}`); + }).then(checklistItem => {})).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('checklistOnlyDailyTodo'), + }); + }); + + it('fails on task not found', () => { + return expect(api.del(`/tasks/${generateUUID()}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('taskNotFound'), + }); + }); + + it('fails on checklist item not found', () => { + return expect(api.post('/tasks', { + type: 'daily', + text: 'daily with checklist', + }).then(createdTask => { + return api.del(`/tasks/${createdTask._id}/checklist/${generateUUID()}`); + })).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('checklistItemNotFound'), + }); + }); +}); diff --git a/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist.test.js b/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist.test.js index e69de29bb2..e3ec919308 100644 --- a/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist.test.js +++ b/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist.test.js @@ -0,0 +1,76 @@ +import { + generateUser, + requester, + translate as t, +} from '../../../../../helpers/api-integration.helper'; +import { v4 as generateUUID } from 'uuid'; + +describe('POST /tasks/:taskId/checklist/', () => { + let user, api; + + before(() => { + return generateUser().then((generatedUser) => { + user = generatedUser; + api = requester(user); + }); + }); + + it('adds a checklist item to a task', () => { + let task; + + return api.post('/tasks', { + type: 'daily', + text: 'Daily with checklist', + }).then(createdTask => { + task = createdTask; + return api.post(`/tasks/${task._id}/checklist`, {text: 'Checklist Item 1', ignored: false, _id: 123}); + }).then((savedTask) => { + expect(savedTask.checklist.length).to.equal(1); + expect(savedTask.checklist[0].text).to.equal('Checklist Item 1'); + expect(savedTask.checklist[0].completed).to.equal(false); + expect(savedTask.checklist[0]._id).to.be.a('string'); + expect(savedTask.checklist[0]._id).to.not.equal('123'); + expect(savedTask.checklist[0].ignored).to.be.an('undefined'); + }); + }); + + it('does not add a checklist to habits', () => { + let habit; + return expect(api.post('/tasks', { + type: 'habit', + text: 'habit with checklist', + }).then(createdTask => { + habit = createdTask; + return api.post(`/tasks/${habit._id}/checklist`, {text: 'Checklist Item 1'}); + })).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('checklistOnlyDailyTodo'), + }); + }); + + it('does not add a checklist to rewards', () => { + let reward; + return expect(api.post('/tasks', { + type: 'reward', + text: 'reward with checklist', + }).then(createdTask => { + reward = createdTask; + return api.post(`/tasks/${reward._id}/checklist`, {text: 'Checklist Item 1'}); + })).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('checklistOnlyDailyTodo'), + }); + }); + + it('fails on task not found', () => { + return expect(api.post(`/tasks/${generateUUID()}/checklist`, { + text: 'Checklist Item 1' + })).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('taskNotFound'), + }); + }); +}); diff --git a/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist_itemId_score.test.js b/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist_itemId_score.test.js index e69de29bb2..32c09ba1c8 100644 --- a/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist_itemId_score.test.js +++ b/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist_itemId_score.test.js @@ -0,0 +1,85 @@ +import { + generateUser, + requester, + translate as t, +} from '../../../../../helpers/api-integration.helper'; +import { v4 as generateUUID } from 'uuid'; + +describe('POST /tasks/:taskId/checklist/:itemId/score', () => { + let user, api; + + before(() => { + return generateUser().then((generatedUser) => { + user = generatedUser; + api = requester(user); + }); + }); + + it('scores a checklist item', () => { + let task; + + return api.post('/tasks', { + type: 'daily', + text: 'Daily with checklist', + }).then(createdTask => { + task = createdTask; + return api.post(`/tasks/${task._id}/checklist`, {text: 'Checklist Item 1', completed: false}); + }).then((savedTask) => { + return api.post(`/tasks/${task._id}/checklist/${savedTask.checklist[0]._id}/score`); + }).then((savedTask) => { + expect(savedTask.checklist.length).to.equal(1); + expect(savedTask.checklist[0].completed).to.equal(true); + }); + }); + + it('fails on habits', () => { + let habit; + return expect(api.post('/tasks', { + type: 'habit', + text: 'habit with checklist', + }).then(createdTask => { + habit = createdTask; + return api.post(`/tasks/${habit._id}/checklist/${generateUUID()}/score`, {text: 'Checklist Item 1'}); + })).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('checklistOnlyDailyTodo'), + }); + }); + + it('fails on rewards', () => { + let reward; + return expect(api.post('/tasks', { + type: 'reward', + text: 'reward with checklist', + }).then(createdTask => { + reward = createdTask; + return api.post(`/tasks/${reward._id}/checklist/${generateUUID()}/score`); + }).then(checklistItem => {})).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('checklistOnlyDailyTodo'), + }); + }); + + it('fails on task not found', () => { + return expect(api.post(`/tasks/${generateUUID()}/checklist/${generateUUID()}/score`)).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('taskNotFound'), + }); + }); + + it('fails on checklist item not found', () => { + return expect(api.post('/tasks', { + type: 'daily', + text: 'daily with checklist', + }).then(createdTask => { + return api.post(`/tasks/${createdTask._id}/checklist/${generateUUID()}/score`); + })).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('checklistItemNotFound'), + }); + }); +}); diff --git a/test/api/v3/integration/tasks/checklists/PUT-tasks_taskId_checklist_itemId.test.js b/test/api/v3/integration/tasks/checklists/PUT-tasks_taskId_checklist_itemId.test.js index e69de29bb2..e272d05da8 100644 --- a/test/api/v3/integration/tasks/checklists/PUT-tasks_taskId_checklist_itemId.test.js +++ b/test/api/v3/integration/tasks/checklists/PUT-tasks_taskId_checklist_itemId.test.js @@ -0,0 +1,87 @@ +import { + generateUser, + requester, + translate as t, +} from '../../../../../helpers/api-integration.helper'; +import { v4 as generateUUID } from 'uuid'; + +describe('PUT /tasks/:taskId/checklist/:itemId', () => { + let user, api; + + before(() => { + return generateUser().then((generatedUser) => { + user = generatedUser; + api = requester(user); + }); + }); + + it('updates a checklist item', () => { + let task; + + return api.post('/tasks', { + type: 'daily', + text: 'Daily with checklist', + }).then(createdTask => { + task = createdTask; + return api.post(`/tasks/${task._id}/checklist`, {text: 'Checklist Item 1', completed: false}); + }).then((savedTask) => { + return api.put(`/tasks/${task._id}/checklist/${savedTask.checklist[0]._id}`, {text: 'updated', completed: true, _id: 123}); + }).then((savedTask) => { + expect(savedTask.checklist.length).to.equal(1); + expect(savedTask.checklist[0].text).to.equal('updated'); + expect(savedTask.checklist[0].completed).to.equal(true); + expect(savedTask.checklist[0]._id).to.not.equal('123'); + }); + }); + + it('fails on habits', () => { + let habit; + return expect(api.post('/tasks', { + type: 'habit', + text: 'habit with checklist', + }).then(createdTask => { + habit = createdTask; + return api.put(`/tasks/${habit._id}/checklist/${generateUUID()}`); + })).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('checklistOnlyDailyTodo'), + }); + }); + + it('fails on rewards', () => { + let reward; + return expect(api.post('/tasks', { + type: 'reward', + text: 'reward with checklist', + }).then(createdTask => { + reward = createdTask; + return api.put(`/tasks/${reward._id}/checklist/${generateUUID()}`); + }).then(checklistItem => {})).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('checklistOnlyDailyTodo'), + }); + }); + + it('fails on task not found', () => { + return expect(api.put(`/tasks/${generateUUID()}/checklist/${generateUUID()}`)).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('taskNotFound'), + }); + }); + + it('fails on checklist item not found', () => { + return expect(api.post('/tasks', { + type: 'daily', + text: 'daily with checklist', + }).then(createdTask => { + return api.put(`/tasks/${createdTask._id}/checklist/${generateUUID()}`); + })).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('checklistItemNotFound'), + }); + }); +}); diff --git a/website/src/controllers/api-v3/tasks.js b/website/src/controllers/api-v3/tasks.js index 019b7c058c..9221787a63 100644 --- a/website/src/controllers/api-v3/tasks.js +++ b/website/src/controllers/api-v3/tasks.js @@ -368,7 +368,7 @@ api.addChecklistItem = { if (!task) throw new NotFound(res.t('taskNotFound')); if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); - task.checklist.push(req.body); + task.checklist.push(Tasks.Task.sanitizeChecklist(req.body)); return task.save(); }) .then((savedTask) => res.respond(200, savedTask)) // TODO what to return @@ -454,8 +454,7 @@ api.updateChecklistItem = { let item = _.find(task.checklist, {_id: req.params.itemId}); if (!item) throw new NotFound(res.t('checklistItemNotFound')); - delete req.body.id; // Simple sanitization to prevent the ID to be changed - _.merge(item, req.body); + _.merge(item, Tasks.Task.sanitizeChecklist(req.body)); return task.save(); }) .then((savedTask) => res.respond(200, savedTask)) // TODO what to return diff --git a/website/src/models/task.js b/website/src/models/task.js index 3ba4f21b22..a4e376446d 100644 --- a/website/src/models/task.js +++ b/website/src/models/task.js @@ -57,6 +57,12 @@ TaskSchema.statics.sanitizeUpdate = function sanitizeUpdate (updateObj) { return Task.sanitize(updateObj, noUpdate); // eslint-disable-line no-use-before-define }; +// Sanitize checklist objects (disallowing _id) +TaskSchema.statics.sanitizeChecklist = function sanitizeChecklist (checklistObj) { + delete checklistObj._id; + return checklistObj; +}; + export let Task = mongoose.model('Task', TaskSchema); // habits and dailies shared fields