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 9ac09f2..a8c933c 100644 --- a/apps/backend/controllers/flowsheet.controller.ts +++ b/apps/backend/controllers/flowsheet.controller.ts @@ -206,7 +206,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 || @@ -234,7 +234,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'); @@ -253,7 +253,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); @@ -279,7 +279,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); @@ -309,7 +309,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 { dj_id: global.primary_dj_id, album_id: 1, }) - .expect(200); + .expect(201); expectFields(res.body, 'album_id', 'dj_id'); expect(res.body.album_id).toBe(1); @@ -67,7 +67,7 @@ describe('DJ Bin', () => { album_id: 1, track_title: 'Carry the Zero', }) - .expect(200); + .expect(201); expectFields(res.body, 'album_id', 'track_title'); expect(res.body.album_id).toBe(1); diff --git a/tests/integration/flowsheet.spec.js b/tests/integration/flowsheet.spec.js index 5cda171..bd34b8e 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'); @@ -492,7 +492,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 () => { @@ -515,7 +515,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(); @@ -537,7 +537,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') @@ -546,7 +546,7 @@ describe('Shift Flowsheet Entries', () => { album_id: 2, //Ravyn Lenae - Crush track_title: 'Venom', }) - .expect(200); + .expect(201); await request .post('/flowsheet') @@ -555,7 +555,7 @@ describe('Shift Flowsheet Entries', () => { album_id: 3, //Jockstrap - I Love You Jennifer B track_title: 'Debra', }) - .expect(200); + .expect(201); }); afterEach(async () => { @@ -700,7 +700,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); }); @@ -771,7 +771,7 @@ describe('V1 API - entry_type field', () => { album_id: 1, track_title: 'Carry the Zero', }) - .expect(200); + .expect(201); // POST response includes entry_type expect(addRes.body.entry_type).toBe('track'); diff --git a/tests/integration/library.spec.js b/tests/integration/library.spec.js index 6dd4851..9b95ec7 100644 --- a/tests/integration/library.spec.js +++ b/tests/integration/library.spec.js @@ -87,7 +87,7 @@ describe('Library Catalog', () => { genre_id: 1, format_id: 1, }) - .expect(200); + .expect(201); expectFields(res.body, 'id', 'album_title'); expect(res.body.album_title).toContain('Test Album'); @@ -204,7 +204,7 @@ describe('Library Rotation', () => { album_id: 2, rotation_bin: 'M', }) - .expect(200); + .expect(201); expectFields(res.body, 'id', 'album_id', 'rotation_bin'); expect(res.body.album_id).toBe(2); @@ -323,7 +323,7 @@ describe('Library Artists', () => { genre_id: 1, code_number: 1, }) - .expect(200); + .expect(201); expectFields(res.body, 'id', 'artist_name', 'alphabetical_name', 'code_letters', 'code_number'); expect(res.body.artist_name).toContain('Test Artist'); @@ -394,7 +394,7 @@ describe('Library Artists', () => { genre_id: 1, code_number: 1, }) - .expect(200); + .expect(201); expect(res.body.alphabetical_name).toBe(`Band ${uniqueSuffix}, The`); }); @@ -448,7 +448,7 @@ describe('Library Formats', () => { .send({ name: `Test Format ${uniqueSuffix}`, }) - .expect(200); + .expect(201); expectFields(res.body, 'id', 'format_name'); expect(res.body.format_name).toContain('Test Format'); @@ -495,7 +495,7 @@ describe('Library Genres', () => { name: `Test Genre ${uniqueSuffix}`, description: 'A test genre for integration testing', }) - .expect(200); + .expect(201); expectFields(res.body, 'id', 'genre_name'); expect(res.body.genre_name).toContain('Test Genre'); diff --git a/tests/integration/metadata.spec.js b/tests/integration/metadata.spec.js index f204cee..dbadd98 100644 --- a/tests/integration/metadata.spec.js +++ b/tests/integration/metadata.spec.js @@ -34,7 +34,7 @@ describe('Metadata Fields in Flowsheet Response', () => { album_id: 4, // Sufjan Stevens - Illinois track_title: 'Chicago', }) - .expect(200); + .expect(201); // Get the flowsheet entries const getRes = await request.get('/flowsheet').query({ limit: 5 }).send().expect(200); @@ -65,7 +65,7 @@ describe('Metadata Fields in Flowsheet Response', () => { album_id: 5, // Kendrick Lamar - To Pimp a Butterfly track_title: 'Alright', }) - .expect(200); + .expect(201); const res = await request.get('/flowsheet/latest').expect(200); @@ -97,7 +97,7 @@ describe('Fire-and-Forget Metadata Fetch', () => { album_id: 4, // Sufjan Stevens - Illinois track_title: 'Casimir Pulaski Day', }) - .expect(200); + .expect(201); // Response should return immediately with the entry expect(addRes.body.id).toBeDefined(); @@ -124,7 +124,7 @@ describe('Fire-and-Forget Metadata Fetch', () => { album_title: 'OK Computer', track_title: 'Paranoid Android', }) - .expect(200); + .expect(201); // Response should return immediately expect(addRes.body.id).toBeDefined(); @@ -145,7 +145,7 @@ describe('Fire-and-Forget Metadata Fetch', () => { .send({ message: 'PSA: Station ID at the top of the hour', }) - .expect(200); + .expect(201); const entryId = addRes.body.id; @@ -183,7 +183,7 @@ describe('Flowsheet CRUD with Metadata', () => { album_id: 5, track_title: 'CRUD Test Track', }) - .expect(200); + .expect(201); // Get entries - should include new entry const res = await request.get('/flowsheet').query({ limit: 10 }).send().expect(200); @@ -201,7 +201,7 @@ describe('Flowsheet CRUD with Metadata', () => { album_id: 6, track_title: 'Delete Test Track', }) - .expect(200); + .expect(201); const entryId = addRes.body.id; @@ -230,7 +230,7 @@ describe('Flowsheet CRUD with Metadata', () => { album_id: 4, track_title: 'Update Test Original', }) - .expect(200); + .expect(201); const entryId = addRes.body.id; @@ -279,7 +279,7 @@ describe('Metadata with Rotation Entries', () => { track_title: 'Rotation Test Track', rotation_id: 2, }) - .expect(200); + .expect(201); // Get the entry const getRes = await request.get('/flowsheet').query({ limit: 5 }).send().expect(200); @@ -316,7 +316,7 @@ describe('Flowsheet Cache Behavior', () => { album_id: 4, track_title: 'Cache Consistency Test', }) - .expect(200); + .expect(201); // Query multiple times - should return consistent results const res1 = await request.get('/flowsheet').query({ limit: 10 }).send().expect(200); @@ -339,7 +339,7 @@ describe('Flowsheet Cache Behavior', () => { album_id: 5, track_title: 'Cache Invalidation Test Add', }) - .expect(200); + .expect(201); // Query - should include the new entry (proves cache was invalidated) const afterRes = await request.get('/flowsheet').query({ limit: 50 }).send().expect(200); @@ -357,7 +357,7 @@ describe('Flowsheet Cache Behavior', () => { album_id: 4, track_title: 'Cache Invalidation Test Delete', }) - .expect(200); + .expect(201); const entryId = addRes.body.id; @@ -386,7 +386,7 @@ describe('Flowsheet Cache Behavior', () => { album_id: 4, track_title: 'Cache Update Original', }) - .expect(200); + .expect(201); const entryId = addRes.body.id; @@ -440,7 +440,7 @@ describe('Conditional GET (304 Not Modified)', () => { album_id: 4, track_title: 'Last-Modified Header Test', }) - .expect(200); + .expect(201); const res = await request.get('/flowsheet/latest').expect(200); @@ -479,7 +479,7 @@ describe('Conditional GET (304 Not Modified)', () => { album_id: 4, track_title: 'Modification Test Track', }) - .expect(200); + .expect(201); // Request with old If-Modified-Since should return 200 with new data const updatedRes = await request @@ -504,7 +504,7 @@ describe('Conditional GET (304 Not Modified)', () => { album_id: 5, track_title: 'Latest 304 Test', }) - .expect(200); + .expect(201); // First request to get timestamp const initialRes = await request.get('/flowsheet/latest').expect(200); @@ -551,7 +551,7 @@ describe('Conditional GET (304 Not Modified)', () => { album_id: 4, track_title: 'Since Query Param Test', }) - .expect(200); + .expect(201); // Request with old since param should return 200 with new data const updatedRes = await request.get('/flowsheet').query({ limit: 10, since: lastModified }).send().expect(200); @@ -578,7 +578,7 @@ describe('Conditional GET (304 Not Modified)', () => { album_id: 4, track_title: 'Precedence Test Track', }) - .expect(200); + .expect(201); // Get the new Last-Modified const updatedRes = await request.get('/flowsheet').query({ limit: 10 }).send().expect(200); @@ -603,7 +603,7 @@ describe('Conditional GET (304 Not Modified)', () => { album_id: 5, track_title: 'Latest Since Test', }) - .expect(200); + .expect(201); // First request to get timestamp const initialRes = await request.get('/flowsheet/latest').expect(200); diff --git a/tests/integration/schedule.spec.js b/tests/integration/schedule.spec.js index f3c219a..0ab40dd 100644 --- a/tests/integration/schedule.spec.js +++ b/tests/integration/schedule.spec.js @@ -51,7 +51,7 @@ describe('Schedule', () => { show_duration: 8, }; - const res = await request.post('/schedule').send(newShift).expect(200); + const res = await request.post('/schedule').send(newShift).expect(201); expectFields(res.body, 'id', 'day', 'start_time', 'show_duration'); expect(res.body.day).toBe(0); @@ -71,7 +71,7 @@ describe('Schedule', () => { specialty_id: null, }; - const res = await request.post('/schedule').send(newShift).expect(200); + const res = await request.post('/schedule').send(newShift).expect(201); expect(res.body.day).toBe(2); @@ -96,7 +96,7 @@ describe('Schedule', () => { show_duration: 4, }; - const res = await request.post('/schedule').send(newShift).expect(200); + const res = await request.post('/schedule').send(newShift).expect(201); expect(res.body.day).toBe(day); @@ -113,7 +113,7 @@ describe('Schedule', () => { show_duration: 2, }; - const res = await request.post('/schedule').send(newShift).expect(200); + const res = await request.post('/schedule').send(newShift).expect(201); expect(res.body.start_time).toBe('08:30:00'); @@ -132,7 +132,7 @@ describe('Schedule', () => { show_duration: 4, }; - const postRes = await request.post('/schedule').send(newShift).expect(200); + const postRes = await request.post('/schedule').send(newShift).expect(201); if (postRes.body.id) { createdScheduleIds.push(postRes.body.id); diff --git a/tests/unit/controllers/create-status-codes.test.ts b/tests/unit/controllers/create-status-codes.test.ts new file mode 100644 index 0000000..12ded7f --- /dev/null +++ b/tests/unit/controllers/create-status-codes.test.ts @@ -0,0 +1,60 @@ +import { Request, Response, NextFunction } from 'express'; + +jest.mock('../../../apps/backend/services/djs.service'); +jest.mock('../../../apps/backend/services/schedule.service'); + +import * as DJService from '../../../apps/backend/services/djs.service'; +import * as ScheduleService from '../../../apps/backend/services/schedule.service'; +import { addToBin } from '../../../apps/backend/controllers/djs.controller'; +import { addToSchedule } from '../../../apps/backend/controllers/schedule.controller'; + +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: statusMock, + json: jsonMock, + send: sendMock, + } as unknown as Response; + const next = jest.fn() as unknown as NextFunction; + return { req, res, next, statusMock, jsonMock, sendMock }; +} + +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, statusMock, jsonMock } = mockReqResNext({ + dj_id: 'dj-1', + album_id: 10, + }); + + await addToBin(req, res, next); + + expect(statusMock).toHaveBeenCalledWith(201); + expect(jsonMock).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, statusMock, jsonMock } = mockReqResNext({ + day: 'Monday', + start_time: '10:00', + end_time: '12:00', + }); + + await addToSchedule(req, res, next); + + expect(statusMock).toHaveBeenCalledWith(201); + expect(jsonMock).toHaveBeenCalledWith(created); + }); + }); +});