diff --git a/apps/backend/controllers/djs.controller.ts b/apps/backend/controllers/djs.controller.ts index 1e160cf..72ad915 100644 --- a/apps/backend/controllers/djs.controller.ts +++ b/apps/backend/controllers/djs.controller.ts @@ -12,7 +12,7 @@ export type binBody = { export const addToBin: RequestHandler = async (req, res, next) => { if (req.body.album_id === undefined || req.body.dj_id === undefined) { console.error('Bad Request, Missing Album Identifier: album_id'); - res.status(400).send('Bad Request, Missing DJ or album identifier: album_id'); + res.status(400).json({ message: 'Bad Request, Missing DJ or album identifier: album_id' }); } else { const bin_entry: NewBinEntry = { dj_id: req.body.dj_id, @@ -40,7 +40,7 @@ export type binQuery = { export const deleteFromBin: RequestHandler = async (req, res, next) => { if (req.query.album_id === undefined || req.query.dj_id === undefined) { console.error('Bad Request, Missing Bin Entry Identifier: album_id or dj_id'); - res.status(400).send('Bad Request, Missing Bin Entry Identifier: album_id or dj_id'); + res.status(400).json({ message: 'Bad Request, Missing Bin Entry Identifier: album_id or dj_id' }); } else { try { //check that the dj_id === dj_id of bin entry @@ -56,7 +56,7 @@ export const deleteFromBin: RequestHandler = export const getBin: RequestHandler = async (req, res, next) => { if (req.query.dj_id === undefined) { console.error('Bad Request, Missing DJ Identifier: dj_id'); - res.status(400).send('Bad Request, Missing DJ Identifier: dj_id'); + res.status(400).json({ message: 'Bad Request, Missing DJ Identifier: dj_id' }); } else { try { const dj_bin = await DJService.getBinFromDB(req.query.dj_id); @@ -72,7 +72,7 @@ export const getBin: RequestHandler export const getPlaylistsForDJ: RequestHandler = async (req, res, next) => { if (req.query.dj_id === undefined) { console.error('Bad Request, Missing DJ Identifier: dj_id'); - res.status(400).send('Bad Request, Missing DJ Identifier: dj_id'); + res.status(400).json({ message: 'Bad Request, Missing DJ Identifier: dj_id' }); } else { try { const playlists = await DJService.getPlaylistsForDJ(req.query.dj_id); @@ -88,7 +88,7 @@ export const getPlaylistsForDJ: RequestHandler = async (req, res, next) => { if (req.query.playlist_id === undefined) { console.error('Bad Request, Missing Playlist Identifier: playlist_id'); - res.status(400).send('Bad Request, Missing Playlist Identifier: playlist_id'); + res.status(400).json({ message: 'Bad Request, Missing Playlist Identifier: playlist_id' }); } else { try { const playlist = await DJService.getPlaylist(parseInt(req.query.playlist_id)); diff --git a/apps/backend/controllers/flowsheet.controller.ts b/apps/backend/controllers/flowsheet.controller.ts index c8074c5..c4d056a 100644 --- a/apps/backend/controllers/flowsheet.controller.ts +++ b/apps/backend/controllers/flowsheet.controller.ts @@ -109,7 +109,7 @@ export const getLatest: RequestHandler = async (req, res, next) => { res.status(200).json(latest[0]); } else { console.error('No Tracks found'); - res.status(404).send('Error: No Tracks found'); + res.status(404).json({ message: 'No Tracks found' }); } } catch (e) { console.error('Error: Failed to retrieve track'); @@ -142,13 +142,13 @@ export const addEntry: RequestHandler = async (req: Request const { entry_id } = req.body; if (entry_id === undefined) { console.error('Bad Request, Missing entry identifier: entry_id'); - res.status(400).send('Bad Request, Missing entry identifier: entry_id'); + res.status(400).json({ message: 'Bad Request, Missing entry identifier: entry_id' }); } else { try { const removedEntry: FSEntry = await flowsheet_service.removeTrack(entry_id); @@ -273,7 +275,7 @@ export const updateEntry: RequestHandler, res, next) => { const current_show = await flowsheet_service.getLatestShow(); if (req.body.dj_id === undefined) { - res.status(400).send('Bad Request, Must include a dj_id to join show'); + res.status(400).json({ message: 'Bad Request, Must include a dj_id to join show' }); } else if (current_show?.end_time !== null) { try { const show_session: Show = await flowsheet_service.startShow( @@ -416,7 +418,7 @@ export const getShowInfo: RequestHandler { export type RotationAddRequest = Omit; export const addRotation: RequestHandler = async (req, res, next) => { if (req.body.album_id === undefined || req.body.rotation_bin === undefined) { - res.status(400).send('Missing Parameters: album_id or rotation_bin'); + res.status(400).json({ message: 'Missing Parameters: album_id or rotation_bin' }); } else { try { const rotationRelease: RotationRelease = await libraryService.addToRotation(req.body); @@ -187,16 +184,16 @@ export const killRotation: RequestHandler const { body } = req; if (body.rotation_id === undefined) { - res.status(400).send('Bad Request, Missing Parameter: rotation_id'); + res.status(400).json({ message: 'Bad Request, Missing Parameter: rotation_id' }); } else if (body.kill_date !== undefined && !libraryService.isISODate(body.kill_date)) { - res.status(400).send('Bad Request, Incorrect Date Format: kill_date should be of form YYYY-MM-DD'); + res.status(400).json({ message: 'Bad Request, Incorrect Date Format: kill_date should be of form YYYY-MM-DD' }); } else { try { const updatedRotation: RotationRelease = await libraryService.killRotationInDB(body.rotation_id, body.kill_date); if (updatedRotation !== undefined) { res.status(200).json(updatedRotation); } else { - res.status(400).json({ status: 400, message: 'Rotation entry not found' }); + res.status(400).json({ message: 'Rotation entry not found' }); } } catch (e) { console.error('Failed to update rotation kill_date'); @@ -220,7 +217,7 @@ export const getFormats: RequestHandler = async (req, res, next) => { export const addFormat: RequestHandler = async (req, res, next) => { const { body } = req; if (body.name === undefined) { - res.status(400).send('Bad Request, Missing Parameter: name'); + res.status(400).json({ message: 'Bad Request, Missing Parameter: name' }); } else { try { const newFormat: NewAlbumFormat = { @@ -245,7 +242,7 @@ export const getGenres: RequestHandler = async (req, res) => { export const addGenre: RequestHandler = async (req, res, next) => { const { body } = req; if (body.name === undefined || body.description === undefined) { - res.status(400).send('Bad Request, Parameters name and description are required.'); + res.status(400).json({ message: 'Bad Request, Parameters name and description are required.' }); } else { try { const newGenre: NewGenre = { @@ -271,7 +268,7 @@ export const getAlbum: RequestHandler { + it('returns { message } with correct status for WxycError', () => { + const { res, statusMock, jsonMock } = mockResponse(); + const error = new WxycError('Album not found', 404); + + errorHandler(error, mockReq, res, mockNext); + + expect(statusMock).toHaveBeenCalledWith(404); + expect(jsonMock).toHaveBeenCalledWith({ message: 'Album not found' }); + }); + + it('returns generic message for non-WxycError (does not leak internals)', () => { + const { res, statusMock, jsonMock } = mockResponse(); + const error = new Error('SELECT * FROM users failed: connection refused'); + + errorHandler(error, mockReq, res, mockNext); + + expect(statusMock).toHaveBeenCalledWith(500); + expect(jsonMock).toHaveBeenCalledWith({ message: 'Internal server error' }); + }); + + it('handles non-Error values thrown', () => { + const { res, statusMock, jsonMock } = mockResponse(); + + errorHandler('something broke', mockReq, res, mockNext); + + expect(statusMock).toHaveBeenCalledWith(500); + expect(jsonMock).toHaveBeenCalledWith({ message: 'Internal server error' }); + }); + + it('logs non-WxycError errors to console', () => { + const { res } = mockResponse(); + const error = new Error('db connection lost'); + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + + errorHandler(error, mockReq, res, mockNext); + + expect(consoleSpy).toHaveBeenCalledWith('Unhandled error:', error); + consoleSpy.mockRestore(); + }); +});