diff --git a/helpers/functions/apiResponse.ts b/helpers/functions/apiResponse.ts index c216fbd..8509645 100644 --- a/helpers/functions/apiResponse.ts +++ b/helpers/functions/apiResponse.ts @@ -29,7 +29,10 @@ export default class ApiResponse { res.status(422).json({ status: 422, message }); } - static serverError(res: NextApiResponse, message = 'API error, contact the administrator.') { + static serverError( + res: NextApiResponse, + message: string = 'API error, contact the administrator.' + ) { res.status(500).json({ status: 500, message }); } } diff --git a/helpers/functions/fileHelpers.ts b/helpers/functions/fileHelpers.ts index e800f35..15eb0bd 100644 --- a/helpers/functions/fileHelpers.ts +++ b/helpers/functions/fileHelpers.ts @@ -14,7 +14,7 @@ export const getUsersList = async (): Promise => { const fileContent = await fs.readFile(usersFilePath, 'utf8'); return JSON.parse(fileContent) || []; } catch (error) { - console.error('Error reading users.json:', error); + console.log('Error reading users.json:', error); return []; } }; @@ -23,7 +23,7 @@ export const updateUsersList = async (usersList: BorgWarehouseUser[]): Promise => { const fileContent = await fs.readFile(repoFilePath, 'utf8'); return JSON.parse(fileContent) || []; } catch (error) { - console.error('Error reading repo.json:', error); + console.log('Error reading repo.json:', error); return []; } }; @@ -44,6 +44,6 @@ export const updateRepoList = async (repoList: Repository[], history = false): P } await fs.writeFile(repoFilePath, JSON.stringify(repoList, null, 2)); } catch (error) { - console.error('Error writing repo.json:', error); + console.log('Error writing repo.json:', error); } }; diff --git a/helpers/functions/repoHistory.ts b/helpers/functions/repoHistory.ts index ecc5677..1399ef0 100644 --- a/helpers/functions/repoHistory.ts +++ b/helpers/functions/repoHistory.ts @@ -31,6 +31,6 @@ export default async function repoHistory(repoList: Repository[]) { await fs.appendFile(backupFilePath, logData); } catch (error) { - console.error('An error occurred while saving the repo history :', error); + console.log('An error occurred while saving the repo history :', error); } } diff --git a/helpers/functions/shell.utils.ts b/helpers/functions/shell.utils.ts index fd91380..04dc009 100644 --- a/helpers/functions/shell.utils.ts +++ b/helpers/functions/shell.utils.ts @@ -32,10 +32,10 @@ export const updateRepoShell = async ( sshPublicKey: string, storageSize: number, appendOnlyMode: boolean -): Promise<{ stderr?: string }> => { +): Promise<{ stdout?: string; stderr?: string }> => { const shellsDirectory = path.join(process.cwd(), '/helpers'); - const { stderr } = await exec( + const { stdout, stderr } = await exec( `${shellsDirectory}/shells/updateRepo.sh ${repositoryName} "${sshPublicKey}" ${storageSize} ${appendOnlyMode}` ); - return { stderr }; + return { stdout, stderr }; }; diff --git a/pages/api/account/sendTestEmail.ts b/pages/api/account/sendTestEmail.ts index ce5fa71..6e47a2a 100644 --- a/pages/api/account/sendTestEmail.ts +++ b/pages/api/account/sendTestEmail.ts @@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.status(200).json({ message: 'Mail successfully sent' }); } catch (error) { - console.error(error); + console.log(error); return res.status(500).json({ message: `An error occurred while sending the email: ${error}` }); } } diff --git a/pages/api/account/updateAppriseAlert.ts b/pages/api/account/updateAppriseAlert.ts index 3d28d5c..223616c 100644 --- a/pages/api/account/updateAppriseAlert.ts +++ b/pages/api/account/updateAppriseAlert.ts @@ -41,7 +41,7 @@ export default async function handler( await updateUsersList(updatedUsersList); return res.status(200).json({ message: 'Successful API send' }); } catch (error: any) { - console.error(error); + console.log(error); return res.status(500).json({ status: 500, message: diff --git a/pages/api/account/updateAppriseMode.ts b/pages/api/account/updateAppriseMode.ts index 7f49880..b9afdaa 100644 --- a/pages/api/account/updateAppriseMode.ts +++ b/pages/api/account/updateAppriseMode.ts @@ -41,7 +41,7 @@ export default async function handler( await updateUsersList(updatedUsersList); return res.status(200).json({ message: 'Successful API send' }); } catch (error: any) { - console.error(error); + console.log(error); return res.status(500).json({ status: 500, message: diff --git a/pages/api/account/updateAppriseServices.ts b/pages/api/account/updateAppriseServices.ts index 84bf921..d646094 100644 --- a/pages/api/account/updateAppriseServices.ts +++ b/pages/api/account/updateAppriseServices.ts @@ -44,7 +44,7 @@ export default async function handler( await updateUsersList(updatedUsersList); return res.status(200).json({ message: 'Successful API send' }); } catch (error: any) { - console.error(error); + console.log(error); return res.status(500).json({ status: 500, message: diff --git a/pages/api/account/updateEmail.ts b/pages/api/account/updateEmail.ts index 60dd2b3..549e105 100644 --- a/pages/api/account/updateEmail.ts +++ b/pages/api/account/updateEmail.ts @@ -45,7 +45,7 @@ export default async function handler( await updateUsersList(updatedUsersList); return res.status(200).json({ message: 'Successful API send' }); } catch (error: any) { - console.error(error); + console.log(error); return res.status(500).json({ status: 500, message: diff --git a/pages/api/account/updateEmailAlert.ts b/pages/api/account/updateEmailAlert.ts index d8aea2b..a98d0c8 100644 --- a/pages/api/account/updateEmailAlert.ts +++ b/pages/api/account/updateEmailAlert.ts @@ -41,7 +41,7 @@ export default async function handler( await updateUsersList(updatedUsersList); return res.status(200).json({ message: 'Successful API send' }); } catch (error: any) { - console.error(error); + console.log(error); return res.status(500).json({ status: 500, message: diff --git a/pages/api/account/updatePassword.ts b/pages/api/account/updatePassword.ts index c211eab..803c7bd 100644 --- a/pages/api/account/updatePassword.ts +++ b/pages/api/account/updatePassword.ts @@ -49,7 +49,7 @@ export default async function handler( return res.status(200).json({ message: 'Successful API send' }); } catch (error: any) { - console.error(error); + console.log(error); return res.status(500).json({ status: 500, message: diff --git a/pages/api/account/updateUsername.ts b/pages/api/account/updateUsername.ts index 10e062a..ce89953 100644 --- a/pages/api/account/updateUsername.ts +++ b/pages/api/account/updateUsername.ts @@ -53,7 +53,7 @@ export default async function handler( return res.status(200).json({ message: 'Successful API send' }); } catch (error: any) { - console.error(error); + console.log(error); return res.status(500).json({ status: 500, message: diff --git a/pages/api/cronjob/checkStatus.ts b/pages/api/cronjob/checkStatus.ts index cedfefe..fb19958 100644 --- a/pages/api/cronjob/checkStatus.ts +++ b/pages/api/cronjob/checkStatus.ts @@ -95,7 +95,7 @@ export default async function handler( await updateRepoList(updatedRepoList); return ApiResponse.success(res, 'Status cron executed successfully.'); } catch (error) { - console.error(error); + console.log(error); return ApiResponse.serverError(res); } } diff --git a/pages/api/cronjob/getStorageUsed.ts b/pages/api/cronjob/getStorageUsed.ts index fb52419..b34f1cb 100644 --- a/pages/api/cronjob/getStorageUsed.ts +++ b/pages/api/cronjob/getStorageUsed.ts @@ -41,7 +41,7 @@ export default async function handler( await updateRepoList(updatedRepoList); return ApiResponse.success(res, 'Storage cron has been executed.'); } catch (err) { - console.error(err); + console.log(err); return ApiResponse.serverError(res); } } diff --git a/pages/api/repo/id/[slug]/delete.ts b/pages/api/repo/id/[slug]/delete.ts index bff5b04..e433f20 100644 --- a/pages/api/repo/id/[slug]/delete.ts +++ b/pages/api/repo/id/[slug]/delete.ts @@ -64,7 +64,7 @@ export default async function handler( await updateRepoList(updatedRepoList, true); return ApiResponse.success(res, `Repository ${repoList[indexToDelete].repositoryName} deleted`); } catch (error) { - console.error(error); + console.log(error); return ApiResponse.serverError(res); } } diff --git a/pages/api/repo/id/[slug]/edit.ts b/pages/api/repo/id/[slug]/edit.ts index 26da5cb..b503973 100644 --- a/pages/api/repo/id/[slug]/edit.ts +++ b/pages/api/repo/id/[slug]/edit.ts @@ -87,15 +87,16 @@ export default async function handler( ); if (stderr) { console.log('Update repository error: ', stderr); - return ApiResponse.serverError(res); + throw new Error(); } const updatedRepoList = [...filteredRepoList, updatedRepo]; await updateRepoList(updatedRepoList, true); + return res.status(200).json({ message: `Repository ${repo.repositoryName} has been edited` }); } catch (error) { - console.error(error); - return ApiResponse.serverError(res); + console.log(error); + return ApiResponse.serverError(res, error as string); } } diff --git a/tests/supertest/checkStatus.test.ts b/tests/supertest/checkStatus.test.ts index da6a798..be43e61 100644 --- a/tests/supertest/checkStatus.test.ts +++ b/tests/supertest/checkStatus.test.ts @@ -44,6 +44,7 @@ describe('Cronjob API Handler', () => { process.env.CRONJOB_KEY = 'test-key'; jest.clearAllMocks(); jest.resetModules(); + jest.spyOn(console, 'log').mockImplementation(() => {}); }); it('should return 401 if no authorization header', async () => { diff --git a/tests/supertest/delete.test.ts b/tests/supertest/delete.test.ts index 7bde772..cc74baf 100644 --- a/tests/supertest/delete.test.ts +++ b/tests/supertest/delete.test.ts @@ -34,6 +34,7 @@ describe('DELETE /api/repo/id/[slug]/delete', () => { beforeEach(() => { jest.clearAllMocks(); jest.resetModules(); + jest.spyOn(console, 'log').mockImplementation(() => {}); }); it('should return 405 if method is not DELETE', async () => { diff --git a/tests/supertest/edit.test.ts b/tests/supertest/edit.test.ts index 26153bb..10c70a6 100644 --- a/tests/supertest/edit.test.ts +++ b/tests/supertest/edit.test.ts @@ -38,6 +38,8 @@ describe('PATCH /api/repo/id/[slug]/edit', () => { beforeEach(() => { jest.clearAllMocks(); jest.resetModules(); + jest.resetAllMocks(); + jest.spyOn(console, 'log').mockImplementation(() => {}); }); it('should return 405 if method is not PATCH', async () => { diff --git a/tests/supertest/getStorageUsed.test.ts b/tests/supertest/getStorageUsed.test.ts index 22c5c6a..8e55ddc 100644 --- a/tests/supertest/getStorageUsed.test.ts +++ b/tests/supertest/getStorageUsed.test.ts @@ -13,6 +13,13 @@ jest.mock('~/helpers/functions/shell.utils', () => ({ })); describe('GET /api/cronjob/getStorageUsed', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + jest.resetAllMocks(); + jest.spyOn(console, 'log').mockImplementation(() => {}); + }); + const CRONJOB_KEY = 'test-cronjob-key'; process.env.CRONJOB_KEY = CRONJOB_KEY; diff --git a/tests/supertest/repoIndex.test.ts b/tests/supertest/repoIndex.test.ts new file mode 100644 index 0000000..4b0fa9e --- /dev/null +++ b/tests/supertest/repoIndex.test.ts @@ -0,0 +1,123 @@ +import { createMocks } from 'node-mocks-http'; +import handler from '~/pages/api/repo/id/[slug]'; +import { getServerSession } from 'next-auth/next'; +import { getRepoList, tokenController } from '~/helpers/functions'; +import { Repository } from '~/types/domain/config.types'; + +jest.mock('next-auth', () => { + return jest.fn(() => { + return { + auth: { session: {} }, + GET: jest.fn(), + POST: jest.fn(), + }; + }); +}); + +jest.mock('next-auth/next', () => ({ + getServerSession: jest.fn(), +})); + +jest.mock('~/helpers/functions', () => ({ + getRepoList: jest.fn(), + tokenController: jest.fn(), +})); + +const mockRepoList: Repository[] = [ + { + id: 1, + alias: 'repo1', + repositoryName: 'Test Repository 1', + status: true, + lastSave: 1678901234, + alert: 1, + storageSize: 100, + storageUsed: 50, + sshPublicKey: 'ssh-rsa AAAAB3Nza...fakekey1', + comment: 'Test repository 1', + displayDetails: true, + unixUser: 'user1', + lanCommand: false, + appendOnlyMode: false, + lastStatusAlertSend: 1678901234, + }, + { + id: 2, + alias: 'repo2', + repositoryName: 'Test Repository 2', + status: false, + lastSave: 1678905678, + storageSize: 200, + storageUsed: 150, + sshPublicKey: 'ssh-rsa AAAAB3Nza...fakekey2', + comment: 'Test repository 2', + displayDetails: false, + unixUser: 'user2', + }, +]; + +describe('GET /api/repo/id/[slug]', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + jest.spyOn(console, 'log').mockImplementation(() => {}); + }); + + it('should return 405 if method is not GET', async () => { + const { req, res } = createMocks({ method: 'POST' }); + await handler(req, res); + expect(res._getStatusCode()).toBe(405); + }); + + it('should return 401 if no session or authorization header is provided', async () => { + const { req, res } = createMocks({ method: 'GET' }); + await handler(req, res); + expect(res._getStatusCode()).toBe(401); + }); + + it('should return 401 if API key is invalid', async () => { + (getServerSession as jest.Mock).mockResolvedValue(null); + (tokenController as jest.Mock).mockResolvedValue(null); + const { req, res } = createMocks({ + method: 'GET', + headers: { authorization: 'Bearer INVALID_API_KEY' }, + }); + await handler(req, res); + expect(res._getStatusCode()).toBe(401); + }); + + it('should return 403 if API key does not have read permissions', async () => { + (getServerSession as jest.Mock).mockResolvedValue(null); + (tokenController as jest.Mock).mockResolvedValue({ read: false }); + const { req, res } = createMocks({ + method: 'GET', + headers: { authorization: 'Bearer API_KEY' }, + }); + await handler(req, res); + expect(res._getStatusCode()).toBe(403); + }); + + it('should return 400 if slug is missing or malformed', async () => { + (getServerSession as jest.Mock).mockResolvedValue({ user: { name: 'USER' } }); + const { req, res } = createMocks({ method: 'GET', query: { slug: undefined } }); + await handler(req, res); + expect(res._getStatusCode()).toBe(400); + }); + + it('should return 404 if repository is not found', async () => { + (getServerSession as jest.Mock).mockResolvedValue({ user: { name: 'USER' } }); + (getRepoList as jest.Mock).mockResolvedValue([]); + const { req, res } = createMocks({ method: 'GET', query: { slug: '3' } }); + await handler(req, res); + expect(res._getStatusCode()).toBe(404); + }); + + it('should return 200 and the repository data if found', async () => { + (getServerSession as jest.Mock).mockResolvedValue({ user: { name: 'USER' } }); + (getRepoList as jest.Mock).mockResolvedValue(mockRepoList); + const { req, res } = createMocks({ method: 'GET', query: { slug: '1' } }); + await handler(req, res); + expect(res._getStatusCode()).toBe(200); + expect(res._getJSONData()).toEqual({ repo: mockRepoList[0] }); + }); +}); diff --git a/tests/supertest/sendTestEmail.test.ts b/tests/supertest/sendTestEmail.test.ts index ee9550d..38412ab 100644 --- a/tests/supertest/sendTestEmail.test.ts +++ b/tests/supertest/sendTestEmail.test.ts @@ -2,7 +2,19 @@ import { createMocks } from 'node-mocks-http'; import handler from '~/pages/api/account/sendTestEmail'; import { getServerSession } from 'next-auth/next'; -jest.mock('next-auth/next'); +jest.mock('next-auth', () => { + return jest.fn(() => { + return { + auth: { session: {} }, + GET: jest.fn(), + POST: jest.fn(), + }; + }); +}); + +jest.mock('next-auth/next', () => ({ + getServerSession: jest.fn(), +})); jest.mock('~/helpers/functions/nodemailerSMTP', () => ({ __esModule: true, default: jest.fn(() => ({ @@ -11,6 +23,10 @@ jest.mock('~/helpers/functions/nodemailerSMTP', () => ({ })); describe('Email API', () => { + beforeEach(() => { + jest.spyOn(console, 'log').mockImplementation(() => {}); + }); + it('should return 401 if not authenticated', async () => { // Mock unauthenticated session (getServerSession as jest.Mock).mockResolvedValue(null); diff --git a/tests/supertest/updateAppriseAlert.test.ts b/tests/supertest/updateAppriseAlert.test.ts index 5ee13b5..6e0b182 100644 --- a/tests/supertest/updateAppriseAlert.test.ts +++ b/tests/supertest/updateAppriseAlert.test.ts @@ -11,6 +11,13 @@ jest.mock('~/helpers/functions/fileHelpers', () => ({ })); describe('Notifications API', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + jest.resetAllMocks(); + jest.spyOn(console, 'log').mockImplementation(() => {}); + }); + it('should return 405 if the method is not PUT', async () => { const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); diff --git a/tests/supertest/updateAppriseServices.test.ts b/tests/supertest/updateAppriseServices.test.ts index 3afae04..e8a85a0 100644 --- a/tests/supertest/updateAppriseServices.test.ts +++ b/tests/supertest/updateAppriseServices.test.ts @@ -11,6 +11,13 @@ jest.mock('~/helpers/functions/fileHelpers', () => ({ })); describe('PUT /api/account/updateAppriseURLs', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + jest.resetAllMocks(); + jest.spyOn(console, 'log').mockImplementation(() => {}); + }); + it('should return 401 if not authenticated', async () => { // Mock unauthenticated session (getServerSession as jest.Mock).mockResolvedValue(null);