diff --git a/sample-app/src/app.test.ts b/sample-app/src/app.test.ts index c2d8dfe..d7263fd 100644 --- a/sample-app/src/app.test.ts +++ b/sample-app/src/app.test.ts @@ -39,4 +39,37 @@ describe('Task API', () => { const res = await request(createApp()).get('/tasks/does-not-exist'); expect(res.status).toBe(404); }); + + it('rejects a missing title with 400 and does not create a task', async () => { + const app = createApp(); + + const res = await request(app).post('/tasks').send({}); + expect(res.status).toBe(400); + expect(res.body.error).toBeTruthy(); + + const list = await request(app).get('/tasks'); + expect(list.body).toHaveLength(0); + }); + + it('rejects a non-string title with 400 and does not create a task', async () => { + const app = createApp(); + + const res = await request(app).post('/tasks').send({ title: 42 }); + expect(res.status).toBe(400); + expect(res.body.error).toBeTruthy(); + + const list = await request(app).get('/tasks'); + expect(list.body).toHaveLength(0); + }); + + it('rejects a whitespace-only title with 400 and does not create a task', async () => { + const app = createApp(); + + const res = await request(app).post('/tasks').send({ title: ' ' }); + expect(res.status).toBe(400); + expect(res.body.error).toBeTruthy(); + + const list = await request(app).get('/tasks'); + expect(list.body).toHaveLength(0); + }); }); diff --git a/sample-app/src/tasks/routes.ts b/sample-app/src/tasks/routes.ts index e3eabca..9d4f799 100644 --- a/sample-app/src/tasks/routes.ts +++ b/sample-app/src/tasks/routes.ts @@ -10,9 +10,12 @@ export function createTaskRouter(store: TaskStore): Router { }); router.post('/', (req, res) => { - // Exercise 1: validate that req.body.title is a non-empty string and - // return 400 with a helpful message when it is not. const { title } = req.body ?? {}; + if (typeof title !== 'string' || title.trim() === '') { + return res.status(400).json({ + error: 'title is required and must be a non-empty string', + }); + } const task = store.create(title); res.status(201).json(task); });