diff --git a/CHANGELOG.md b/CHANGELOG.md index d929b82..17179c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,5 +5,6 @@ what it changes for someone who uses the API. CI checks that it did. ## Unreleased +- `POST /tasks/:id/reopen` marks a task not done again. - `GET /tasks/count` says how many tasks there are, and how many are done. - CI checks that a pull request adds a line here. diff --git a/README.md b/README.md index 79997e0..f06e334 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ and in an adopter's first run. It is small on purpose, so a job is cheap and fas | GET | `/tasks/count` | Says how many tasks there are, and how many are done | | GET | `/tasks/:id` | Returns one task | | POST | `/tasks/:id/complete` | Marks a task done | +| POST | `/tasks/:id/reopen` | Marks a task not done again | | DELETE | `/tasks/:id` | Removes a task | ## Working on it diff --git a/src/server.ts b/src/server.ts index 024a1cb..076aca6 100644 --- a/src/server.ts +++ b/src/server.ts @@ -10,6 +10,7 @@ import { TaskList, ValidationError } from './tasks.ts'; * GET /tasks/count how many tasks, and how many are done * GET /tasks/:id one task * POST /tasks/:id/complete mark a task done + * POST /tasks/:id/reopen mark a task not done again * DELETE /tasks/:id remove a task */ export function createApp(tasks: TaskList = new TaskList()): Server { @@ -46,6 +47,7 @@ async function handle(tasks: TaskList, request: IncomingMessage, response: Serve return tasks.remove(id) ? send(response, 204) : send(response, 404, { error: 'not found' }); } if (parts.length === 3 && parts[2] === 'complete' && method === 'POST') return found(response, tasks.complete(id)); + if (parts.length === 3 && parts[2] === 'reopen' && method === 'POST') return found(response, tasks.reopen(id)); return send(response, 404, { error: 'not found' }); } diff --git a/src/tasks.ts b/src/tasks.ts index e9e84c5..c2fd36f 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -33,6 +33,13 @@ export class TaskList { return task; } + /** Mark a task not done again. Returns it, or undefined when there is no such task. */ + reopen(id: number): Task | undefined { + const task = this.tasks.get(id); + if (task) task.done = false; + return task; + } + remove(id: number): boolean { return this.tasks.delete(id); } diff --git a/test/server.test.ts b/test/server.test.ts index 8aaa404..cf0e0a8 100644 --- a/test/server.test.ts +++ b/test/server.test.ts @@ -40,6 +40,18 @@ describe('the HTTP API', () => { assert.equal((await call('GET', `/tasks/${id}`)).status, 404); }); + it('reopens a task, done or not', async () => { + const id = (await call('POST', '/tasks', { title: 'Open again' })).json!.id as number; + const open = { status: 200, json: { id, title: 'Open again', done: false } }; + + await call('POST', `/tasks/${id}/complete`); + assert.deepEqual(await call('POST', `/tasks/${id}/reopen`), open); + assert.deepEqual(await call('POST', `/tasks/${id}/reopen`), open); + assert.deepEqual(await call('GET', `/tasks/${id}`), open); + + await call('DELETE', `/tasks/${id}`); + }); + it('counts the tasks, and the ones that are done', async () => { const start = await call('GET', '/tasks/count'); assert.equal(start.status, 200); @@ -66,6 +78,7 @@ describe('the HTTP API', () => { assert.equal((await call('GET', '/nothing')).status, 404); assert.equal((await call('GET', '/tasks/abc')).status, 404); assert.equal((await call('POST', '/tasks/99/complete')).status, 404); + assert.equal((await call('POST', '/tasks/99/reopen')).status, 404); assert.equal((await call('PUT', '/tasks', { title: 'x' })).status, 405); }); }); diff --git a/test/tasks.test.ts b/test/tasks.test.ts index 85c072e..a7e5162 100644 --- a/test/tasks.test.ts +++ b/test/tasks.test.ts @@ -26,6 +26,18 @@ describe('the task list', () => { assert.equal(tasks.complete(99), undefined); }); + it('reopens a task, and says so when there is no such task', () => { + const tasks = new TaskList(); + const { id } = tasks.add('Ship it'); + tasks.complete(id); + assert.deepEqual(tasks.reopen(id), { id, title: 'Ship it', done: false }); + assert.equal(tasks.get(id)?.done, false); + assert.deepEqual(tasks.count(), { total: 1, done: 0 }); + + assert.deepEqual(tasks.reopen(id), { id, title: 'Ship it', done: false }); + assert.equal(tasks.reopen(99), undefined); + }); + it('removes a task once', () => { const tasks = new TaskList(); const { id } = tasks.add('Clean up');