Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions sample-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ npm start # run the compiled server
| Method | Path | Description |
| --- | --- | --- |
| `GET` | `/health` | Liveness check → `{ "status": "ok" }` |
| `GET` | `/tasks` | List all tasks |
| `GET` | `/tasks` | List all tasks; use `?completed=true` or `?completed=false` to filter |
| `POST` | `/tasks` | Create a task from `{ "title": "..." }` |
| `GET` | `/tasks/:id` | Get one task (404 if missing) |
| `PATCH` | `/tasks/:id` | Set `{ "completed": true \| false }` |
Expand All @@ -36,7 +36,6 @@ These are the hooks for the [exercises](../exercises/README.md). Don't "fix" the
1. `POST /tasks` does **no input validation**.
2. There is **no `DELETE /tasks/:id`** (and `TaskStore.remove()` is missing).
3. `setCompleted` never refreshes `updatedAt` — a subtle bug.
4. `GET /tasks` has **no `?completed=` filter**.

## Layout

Expand Down
20 changes: 20 additions & 0 deletions sample-app/src/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ describe('Task API', () => {
expect(patched.body.completed).toBe(true);
});

it('lists only completed tasks when filtered', async () => {
const app = createApp();
const completed = await request(app).post('/tasks').send({ title: 'Done' });
await request(app).post('/tasks').send({ title: 'Not done' });
await request(app).patch(`/tasks/${completed.body.id}`).send({ completed: true });

const res = await request(app).get('/tasks?completed=true');

expect(res.status).toBe(200);
expect(res.body).toHaveLength(1);
expect(res.body[0].title).toBe('Done');
});

it('rejects an invalid completed filter', async () => {
const res = await request(createApp()).get('/tasks?completed=maybe');

expect(res.status).toBe(400);
expect(res.body.error).toBe("completed must be 'true' or 'false'");
});

it('returns 404 for an unknown task', async () => {
const res = await request(createApp()).get('/tasks/does-not-exist');
expect(res.status).toBe(404);
Expand Down
11 changes: 8 additions & 3 deletions sample-app/src/tasks/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import type { TaskStore } from './store.js';
export function createTaskRouter(store: TaskStore): Router {
const router = Router();

// Exercise 4: support filtering with GET /tasks?completed=true|false
router.get('/', (_req, res) => {
res.json(store.list());
router.get('/', (req, res) => {
const { completed } = req.query;
if (completed === undefined) return res.json(store.list());
if (completed !== 'true' && completed !== 'false') {
return res.status(400).json({ error: "completed must be 'true' or 'false'" });
}
const want = completed === 'true';
res.json(store.list().filter((task) => task.completed === want));
});

router.post('/', (req, res) => {
Expand Down