From 46f729fb89123b4ed5bcff050080e5d60e03ae0c Mon Sep 17 00:00:00 2001 From: Jake Bromberg Date: Sat, 21 Feb 2026 08:27:23 -0800 Subject: [PATCH 1/3] fix: return 201 Created for all resource creation endpoints All create endpoints (flowsheet entries, bin entries, albums, artists, rotation, formats, genres, schedule) returned 200 instead of 201. Co-authored-by: Cursor --- apps/backend/controllers/djs.controller.ts | 2 +- .../controllers/flowsheet.controller.ts | 6 +- .../backend/controllers/library.controller.ts | 10 ++-- .../controllers/schedule.controller.ts | 2 +- .../controllers/create-status-codes.test.ts | 57 +++++++++++++++++++ 5 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 tests/unit/controllers/create-status-codes.test.ts diff --git a/apps/backend/controllers/djs.controller.ts b/apps/backend/controllers/djs.controller.ts index 1e160cf..d179fc7 100644 --- a/apps/backend/controllers/djs.controller.ts +++ b/apps/backend/controllers/djs.controller.ts @@ -21,7 +21,7 @@ export const addToBin: RequestHandler = async (req, re }; try { const added_bin_item = await DJService.addToBin(bin_entry); - res.status(200).json(added_bin_item); + res.status(201).json(added_bin_item); } catch (e) { console.error('Server error: Failed to insert into bin'); console.error(e); diff --git a/apps/backend/controllers/flowsheet.controller.ts b/apps/backend/controllers/flowsheet.controller.ts index c8074c5..3fd0b84 100644 --- a/apps/backend/controllers/flowsheet.controller.ts +++ b/apps/backend/controllers/flowsheet.controller.ts @@ -182,7 +182,7 @@ export const addEntry: RequestHandler = async (req: Request console.error('[Flowsheet] Metadata fetch failed:', err)); } - res.status(200).json(completedEntry); + res.status(201).json(completedEntry); } else if ( body.album_title === undefined || body.artist_name === undefined || @@ -210,7 +210,7 @@ export const addEntry: RequestHandler = async (req: Request console.error('[Flowsheet] Metadata fetch failed:', err)); } - res.status(200).json(completedEntry); + res.status(201).json(completedEntry); } } catch (e) { console.error('Error: Failed to add track to flowsheet'); @@ -229,7 +229,7 @@ export const addEntry: RequestHandler = async (req: Request = } else { try { const rotationRelease: RotationRelease = await libraryService.addToRotation(req.body); - res.status(200).json(rotationRelease); + res.status(201).json(rotationRelease); } catch (e) { console.error(e); next(e); @@ -228,7 +228,7 @@ export const addFormat: RequestHandler = async (req, res, next) => { }; const insertion = await libraryService.insertFormat(newFormat); - res.status(200).json(insertion); + res.status(201).json(insertion); } catch (e) { console.error('Failed to add new format'); console.error(e); @@ -258,7 +258,7 @@ export const addGenre: RequestHandler = async (req, res, next) => { const insertion = await libraryService.insertGenre(newGenre); - res.status(200).json(insertion); + res.status(201).json(insertion); } catch (e) { console.error('Failed to add new genre'); console.error(e); diff --git a/apps/backend/controllers/schedule.controller.ts b/apps/backend/controllers/schedule.controller.ts index 48b4153..db6aa43 100644 --- a/apps/backend/controllers/schedule.controller.ts +++ b/apps/backend/controllers/schedule.controller.ts @@ -17,7 +17,7 @@ export const addToSchedule: RequestHandler = async (req: Request = {}) { + const req = { body } as unknown as Request; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + send: jest.fn().mockReturnThis(), + } as unknown as Response; + const next = jest.fn() as unknown as NextFunction; + return { req, res, next }; +} + +describe('create endpoints return 201', () => { + describe('addToBin', () => { + it('should return 201 when a bin entry is created', async () => { + const created = { id: 1, dj_id: 'dj-1', album_id: 10, track_title: null }; + (DJService.addToBin as jest.Mock).mockResolvedValue(created); + + const { req, res, next } = mockReqResNext({ + dj_id: 'dj-1', + album_id: 10, + }); + + await addToBin(req, res, next); + + expect(res.status).toHaveBeenCalledWith(201); + expect(res.json).toHaveBeenCalledWith(created); + }); + }); + + describe('addToSchedule', () => { + it('should return 201 when a schedule entry is created', async () => { + const created = { id: 1, day: 'Monday', start_time: '10:00', end_time: '12:00' }; + (ScheduleService.addToSchedule as jest.Mock).mockResolvedValue(created); + + const { req, res, next } = mockReqResNext({ + day: 'Monday', + start_time: '10:00', + end_time: '12:00', + }); + + await addToSchedule(req, res, next); + + expect(res.status).toHaveBeenCalledWith(201); + expect(res.json).toHaveBeenCalledWith(created); + }); + }); +}); From ed77db3de2caf8a956c422577f903a8943f3e1d3 Mon Sep 17 00:00:00 2001 From: Jake Bromberg Date: Fri, 27 Feb 2026 11:04:50 -0800 Subject: [PATCH 2/3] fix: resolve lint errors --- .../controllers/create-status-codes.test.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/tests/unit/controllers/create-status-codes.test.ts b/tests/unit/controllers/create-status-codes.test.ts index 1db5fe2..12ded7f 100644 --- a/tests/unit/controllers/create-status-codes.test.ts +++ b/tests/unit/controllers/create-status-codes.test.ts @@ -10,13 +10,16 @@ import { addToSchedule } from '../../../apps/backend/controllers/schedule.contro function mockReqResNext(body: Record = {}) { const req = { body } as unknown as Request; + const statusMock = jest.fn().mockReturnThis(); + const jsonMock = jest.fn().mockReturnThis(); + const sendMock = jest.fn().mockReturnThis(); const res = { - status: jest.fn().mockReturnThis(), - json: jest.fn().mockReturnThis(), - send: jest.fn().mockReturnThis(), + status: statusMock, + json: jsonMock, + send: sendMock, } as unknown as Response; const next = jest.fn() as unknown as NextFunction; - return { req, res, next }; + return { req, res, next, statusMock, jsonMock, sendMock }; } describe('create endpoints return 201', () => { @@ -25,15 +28,15 @@ describe('create endpoints return 201', () => { const created = { id: 1, dj_id: 'dj-1', album_id: 10, track_title: null }; (DJService.addToBin as jest.Mock).mockResolvedValue(created); - const { req, res, next } = mockReqResNext({ + const { req, res, next, statusMock, jsonMock } = mockReqResNext({ dj_id: 'dj-1', album_id: 10, }); await addToBin(req, res, next); - expect(res.status).toHaveBeenCalledWith(201); - expect(res.json).toHaveBeenCalledWith(created); + expect(statusMock).toHaveBeenCalledWith(201); + expect(jsonMock).toHaveBeenCalledWith(created); }); }); @@ -42,7 +45,7 @@ describe('create endpoints return 201', () => { const created = { id: 1, day: 'Monday', start_time: '10:00', end_time: '12:00' }; (ScheduleService.addToSchedule as jest.Mock).mockResolvedValue(created); - const { req, res, next } = mockReqResNext({ + const { req, res, next, statusMock, jsonMock } = mockReqResNext({ day: 'Monday', start_time: '10:00', end_time: '12:00', @@ -50,8 +53,8 @@ describe('create endpoints return 201', () => { await addToSchedule(req, res, next); - expect(res.status).toHaveBeenCalledWith(201); - expect(res.json).toHaveBeenCalledWith(created); + expect(statusMock).toHaveBeenCalledWith(201); + expect(jsonMock).toHaveBeenCalledWith(created); }); }); }); From 09930c454e25d934ce2bd71cc89f2eaa945612d5 Mon Sep 17 00:00:00 2001 From: Jake Bromberg Date: Fri, 27 Feb 2026 14:26:36 -0800 Subject: [PATCH 3/3] test: update flowsheet integration tests to expect 201 for creation endpoints The addEntry controller now returns 201 Created instead of 200 OK. Update all POST /flowsheet assertions in the integration tests accordingly. --- tests/integration/flowsheet.spec.js | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/integration/flowsheet.spec.js b/tests/integration/flowsheet.spec.js index 28196b4..94d714d 100644 --- a/tests/integration/flowsheet.spec.js +++ b/tests/integration/flowsheet.spec.js @@ -167,7 +167,7 @@ describe('Add to Flowsheet', () => { track_title: 'Carry the Zero', // record_label: 'Warner Bros', }) - .expect(200); + .expect(201); expect(res.body).toBeDefined(); expect(res.body.album_title).toEqual('Keep it Like a Secret'); @@ -182,7 +182,7 @@ describe('Add to Flowsheet', () => { track_title: 'Carry the Zero', rotation_id: 1, }) - .expect(200); + .expect(201); expect(res.body).toBeDefined(); expect(res.body.album_title).toEqual('Keep it Like a Secret'); @@ -197,7 +197,7 @@ describe('Add to Flowsheet', () => { track_title: 'Carry the Zero', record_label: 'Warner Bros', }) - .expect(200); + .expect(201); expect(res.body.album_title).toEqual('Keep it Like a Secret'); expect(res.body.track_title).toEqual('Carry the Zero'); @@ -213,7 +213,7 @@ describe('Add to Flowsheet', () => { track_title: 'Carry the Zero', request_flag: true, }) - .expect(200); + .expect(201); expect(res.body).toBeDefined(); expect(res.body.album_title).toEqual('Keep it Like a Secret'); @@ -229,7 +229,7 @@ describe('Add to Flowsheet', () => { album_title: 'Keep it Like a Secret', track_title: 'Carry the Zero', }) - .expect(200); + .expect(201); expect(res.body).toBeDefined(); expect(res.body.album_title).toEqual('Keep it Like a Secret'); @@ -246,7 +246,7 @@ describe('Add to Flowsheet', () => { track_title: 'Carry the Zero', record_label: 'Warner Bros', }) - .expect(200); + .expect(201); expect(res.body).toBeDefined(); expect(res.body.album_title).toEqual('Keep it Like a Secret'); @@ -264,7 +264,7 @@ describe('Add to Flowsheet', () => { track_title: 'Carry the Zero', request_flag: true, }) - .expect(200); + .expect(201); expect(res.body).toBeDefined(); expect(res.body.album_title).toEqual('Keep it Like a Secret'); @@ -278,7 +278,7 @@ describe('Add to Flowsheet', () => { .send({ message: 'Test Message', }) - .expect(200); + .expect(201); expect(res.body).toBeDefined(); expect(res.body.message).toEqual('Test Message'); @@ -477,7 +477,7 @@ describe('Retrieve Now Playing', () => { album_id: 1, //Built to Spill - Keep it Like a Secret track_title: 'Carry the Zero', }) - .expect(200); + .expect(201); }); afterEach(async () => { @@ -500,7 +500,7 @@ describe('Retrieve Now Playing', () => { album_id: 2, //Ravyn Lenae - Crush track_title: 'Venom', }) - .expect(200); + .expect(201); res = await request.get('/flowsheet/latest').expect(200); expect(res.body).toBeDefined(); @@ -522,7 +522,7 @@ describe('Shift Flowsheet Entries', () => { album_id: 1, //Built to Spill - Keep it Like a Secret track_title: 'Carry the Zero', }) - .expect(200); + .expect(201); await request .post('/flowsheet') @@ -531,7 +531,7 @@ describe('Shift Flowsheet Entries', () => { album_id: 2, //Ravyn Lenae - Crush track_title: 'Venom', }) - .expect(200); + .expect(201); await request .post('/flowsheet') @@ -540,7 +540,7 @@ describe('Shift Flowsheet Entries', () => { album_id: 3, //Jockstrap - I Love You Jennifer B track_title: 'Debra', }) - .expect(200); + .expect(201); }); afterEach(async () => { @@ -685,7 +685,7 @@ describe('Retrieve Playlist Object', () => { album_id: 3, //Jockstrap - I Love You Jennifer B track_title: 'Debra', }) - .expect(200); + .expect(201); await fls_util.leave_show(global.primary_dj_id, global.access_token); }); @@ -735,7 +735,7 @@ describe('V2 Playlist - Discriminated Union Format', () => { album_id: 3, //Jockstrap - I Love You Jennifer B track_title: 'Debra', }) - .expect(200); + .expect(201); await fls_util.leave_show(global.primary_dj_id, global.access_token); }); @@ -793,7 +793,7 @@ describe('V1 API - entry_type field', () => { album_id: 1, track_title: 'Carry the Zero', }) - .expect(200); + .expect(201); // V1 response should now include entry_type (additive change) expect(addRes.body.entry_type).toBe('track');