mirror of
https://github.com/Ravinou/borgwarehouse
synced 2026-03-14 14:25:46 +01:00
test: ✅ edit API
This commit is contained in:
parent
b7d3aec3b1
commit
1ae96c8f9a
5 changed files with 223 additions and 27 deletions
|
|
@ -1,8 +1,5 @@
|
|||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import { authOptions } from '../../../auth/[...nextauth]';
|
||||
import { getServerSession } from 'next-auth/next';
|
||||
import { alertOptions } from '~/types/domain/constants';
|
||||
import {
|
||||
getRepoList,
|
||||
updateRepoList,
|
||||
|
|
@ -13,8 +10,6 @@ import { NextApiRequest, NextApiResponse } from 'next';
|
|||
import ApiResponse from '~/helpers/functions/apiResponse';
|
||||
import { Repository } from '~/types/domain/config.types';
|
||||
import { updateRepoShell } from '~/helpers/functions/shell.utils';
|
||||
const util = require('node:util');
|
||||
const exec = util.promisify(require('node:child_process').exec);
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest & { body: Partial<Repository> },
|
||||
|
|
@ -45,7 +40,14 @@ export default async function handler(
|
|||
return ApiResponse.serverError(res);
|
||||
}
|
||||
|
||||
dataHandler(req, res);
|
||||
try {
|
||||
validateRequestBody(req);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return ApiResponse.badRequest(res, error.message);
|
||||
}
|
||||
return ApiResponse.badRequest(res, 'Invalid request data');
|
||||
}
|
||||
|
||||
try {
|
||||
const { alias, sshPublicKey, storageSize, comment, alert, lanCommand, appendOnlyMode } =
|
||||
|
|
@ -97,31 +99,30 @@ export default async function handler(
|
|||
}
|
||||
}
|
||||
|
||||
const dataHandler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const validateRequestBody = (req: NextApiRequest) => {
|
||||
const slug = req.query.slug;
|
||||
if (!slug || Array.isArray(slug)) {
|
||||
return ApiResponse.badRequest(res, 'Missing slug or slug is malformed');
|
||||
throw new Error('Missing slug or slug is malformed');
|
||||
}
|
||||
const { alias, sshPublicKey, storageSize, comment, alert, lanCommand, appendOnlyMode } = req.body;
|
||||
if (alias !== undefined && typeof alias !== 'string') {
|
||||
return ApiResponse.badRequest(res, 'Alias must be a string');
|
||||
if (req.body.alias !== undefined && typeof req.body.alias !== 'string') {
|
||||
throw new Error('Alias must be a string');
|
||||
}
|
||||
if (sshPublicKey !== undefined && typeof sshPublicKey !== 'string') {
|
||||
return ApiResponse.badRequest(res, 'SSH Public Key must be a string');
|
||||
if (req.body.sshPublicKey !== undefined && typeof req.body.sshPublicKey !== 'string') {
|
||||
throw new Error('SSH Public Key must be a string');
|
||||
}
|
||||
if (storageSize !== undefined && typeof storageSize !== 'number') {
|
||||
return ApiResponse.badRequest(res, 'Storage Size must be a number');
|
||||
if (req.body.storageSize !== undefined && typeof req.body.storageSize !== 'number') {
|
||||
throw new Error('Storage Size must be a number');
|
||||
}
|
||||
if (comment !== undefined && typeof comment !== 'string') {
|
||||
return ApiResponse.badRequest(res, 'Comment must be a string');
|
||||
if (req.body.comment !== undefined && typeof req.body.comment !== 'string') {
|
||||
throw new Error('Comment must be a string');
|
||||
}
|
||||
if (alert !== undefined && typeof alert !== 'number') {
|
||||
return ApiResponse.badRequest(res, 'Alert must be a number');
|
||||
if (req.body.alert !== undefined && typeof req.body.alert !== 'number') {
|
||||
throw new Error('Alert must be a number');
|
||||
}
|
||||
if (lanCommand !== undefined && typeof lanCommand !== 'boolean') {
|
||||
return ApiResponse.badRequest(res, 'Lan Command must be a boolean');
|
||||
if (req.body.lanCommand !== undefined && typeof req.body.lanCommand !== 'boolean') {
|
||||
throw new Error('Lan Command must be a boolean');
|
||||
}
|
||||
if (appendOnlyMode !== undefined && typeof appendOnlyMode !== 'boolean') {
|
||||
return ApiResponse.badRequest(res, 'Append Only Mode must be a boolean');
|
||||
if (req.body.appendOnlyMode !== undefined && typeof req.body.appendOnlyMode !== 'boolean') {
|
||||
throw new Error('Append Only Mode must be a boolean');
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ jest.mock('~/helpers/functions/fileHelpers', () => ({
|
|||
}));
|
||||
|
||||
jest.mock('~/helpers/functions/shell.utils', () => ({
|
||||
getLastSaveList: jest.fn(),
|
||||
getLastSaveListShell: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('~/helpers/functions/nodemailerSMTP', () => ({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import request from 'supertest';
|
||||
import { createMocks } from 'node-mocks-http';
|
||||
import handler from '~/pages/api/repo/id/[slug]/delete';
|
||||
import { getServerSession } from 'next-auth/next';
|
||||
|
|
@ -27,7 +26,7 @@ jest.mock('~/helpers/functions', () => ({
|
|||
|
||||
jest.mock('~/helpers/functions/shell.utils', () => {
|
||||
return {
|
||||
deleteRepo: jest.fn(),
|
||||
deleteRepoShell: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
196
tests/supertest/edit.test.ts
Normal file
196
tests/supertest/edit.test.ts
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import { createMocks } from 'node-mocks-http';
|
||||
import handler from '~/pages/api/repo/id/[slug]/edit';
|
||||
import { getServerSession } from 'next-auth/next';
|
||||
import { updateRepoShell } from '~/helpers/functions/shell.utils';
|
||||
import {
|
||||
getRepoList,
|
||||
updateRepoList,
|
||||
tokenController,
|
||||
isSshPubKeyDuplicate,
|
||||
} from '~/helpers/functions';
|
||||
|
||||
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(),
|
||||
updateRepoList: jest.fn(),
|
||||
tokenController: jest.fn(),
|
||||
isSshPubKeyDuplicate: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('~/helpers/functions/shell.utils', () => ({
|
||||
updateRepoShell: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('PATCH /api/repo/id/[slug]/edit', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.resetModules();
|
||||
});
|
||||
|
||||
it('should return 405 if method is not PATCH', async () => {
|
||||
const { req, res } = createMocks({ method: 'GET' });
|
||||
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: 'PATCH' });
|
||||
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: 'PATCH',
|
||||
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 update permissions', async () => {
|
||||
(getServerSession as jest.Mock).mockResolvedValue(null);
|
||||
(tokenController as jest.Mock).mockResolvedValue({ update: false });
|
||||
const { req, res } = createMocks({
|
||||
method: 'PATCH',
|
||||
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: 'PATCH', 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: 'PATCH', query: { slug: '123' } });
|
||||
await handler(req, res);
|
||||
expect(res._getStatusCode()).toBe(404);
|
||||
});
|
||||
|
||||
it('should return 409 if SSH key is duplicated', async () => {
|
||||
(getServerSession as jest.Mock).mockResolvedValue({ user: { name: 'USER' } });
|
||||
(getRepoList as jest.Mock).mockResolvedValue([{ id: 123, repositoryName: 'test-repo' }]);
|
||||
(isSshPubKeyDuplicate as jest.Mock).mockReturnValue(true);
|
||||
const { req, res } = createMocks({
|
||||
method: 'PATCH',
|
||||
query: { slug: '123' },
|
||||
body: { sshPublicKey: 'duplicate-key' },
|
||||
});
|
||||
await handler(req, res);
|
||||
expect(res._getStatusCode()).toBe(409);
|
||||
});
|
||||
|
||||
it('should return 500 if updateRepoShell fails', async () => {
|
||||
(getServerSession as jest.Mock).mockResolvedValue({ user: { name: 'USER' } });
|
||||
(getRepoList as jest.Mock).mockResolvedValue([{ id: 123, repositoryName: 'test-repo' }]);
|
||||
(updateRepoShell as jest.Mock).mockResolvedValue({ stderr: 'Error' });
|
||||
const { req, res } = createMocks({
|
||||
method: 'PATCH',
|
||||
query: { slug: '123' },
|
||||
body: { alias: 'new-alias' },
|
||||
});
|
||||
await handler(req, res);
|
||||
expect(res._getStatusCode()).toBe(500);
|
||||
});
|
||||
|
||||
it('should successfully update repository with a session', async () => {
|
||||
(getServerSession as jest.Mock).mockResolvedValue({ user: { name: 'USER' } });
|
||||
(getRepoList as jest.Mock).mockResolvedValue([{ id: 123, repositoryName: 'test-repo' }]);
|
||||
(updateRepoShell as jest.Mock).mockResolvedValue({ stderr: null });
|
||||
(updateRepoList as jest.Mock).mockResolvedValue(true);
|
||||
const { req, res } = createMocks({
|
||||
method: 'PATCH',
|
||||
query: { slug: '123' },
|
||||
body: { alias: 'new-alias' },
|
||||
});
|
||||
await handler(req, res);
|
||||
expect(res._getStatusCode()).toBe(200);
|
||||
expect(res._getJSONData()).toEqual({ message: 'Repository test-repo has been edited' });
|
||||
});
|
||||
|
||||
it('should successfully update repository with API key', async () => {
|
||||
(getServerSession as jest.Mock).mockResolvedValue(null);
|
||||
(tokenController as jest.Mock).mockResolvedValue({ update: true });
|
||||
(getRepoList as jest.Mock).mockResolvedValue([{ id: 456, repositoryName: 'repo-key' }]);
|
||||
(updateRepoShell as jest.Mock).mockResolvedValue({ stderr: null });
|
||||
(updateRepoList as jest.Mock).mockResolvedValue(true);
|
||||
const { req, res } = createMocks({
|
||||
method: 'PATCH',
|
||||
query: { slug: '456' },
|
||||
headers: { authorization: 'Bearer API_KEY' },
|
||||
body: { alias: 'updated-repo' },
|
||||
});
|
||||
await handler(req, res);
|
||||
expect(res._getStatusCode()).toBe(200);
|
||||
expect(res._getJSONData()).toEqual({ message: 'Repository repo-key has been edited' });
|
||||
});
|
||||
|
||||
it('should only update the provided fields, keep the rest unchanged and history the modification.', async () => {
|
||||
(getServerSession as jest.Mock).mockResolvedValue({ user: { name: 'USER' } });
|
||||
(getRepoList as jest.Mock).mockResolvedValue([
|
||||
{
|
||||
id: 123,
|
||||
repositoryName: 'test-repo',
|
||||
alias: 'old-alias',
|
||||
sshPublicKey: 'old-key',
|
||||
storageSize: 100,
|
||||
lanCommand: false,
|
||||
},
|
||||
]);
|
||||
(updateRepoShell as jest.Mock).mockResolvedValue({ stderr: null });
|
||||
(updateRepoList as jest.Mock).mockResolvedValue(true);
|
||||
const { req, res } = createMocks({
|
||||
method: 'PATCH',
|
||||
query: { slug: '123' },
|
||||
body: {
|
||||
alias: 'new-alias',
|
||||
sshPublicKey: 'new-key',
|
||||
comment: 'new-comment',
|
||||
alert: 0,
|
||||
appendOnlyMode: true,
|
||||
},
|
||||
});
|
||||
await handler(req, res);
|
||||
expect(updateRepoList).toHaveBeenCalledWith(
|
||||
[
|
||||
{
|
||||
id: 123,
|
||||
repositoryName: 'test-repo',
|
||||
alias: 'new-alias',
|
||||
sshPublicKey: 'new-key',
|
||||
comment: 'new-comment',
|
||||
alert: 0,
|
||||
appendOnlyMode: true,
|
||||
storageSize: 100,
|
||||
lanCommand: false,
|
||||
},
|
||||
],
|
||||
true
|
||||
);
|
||||
expect(updateRepoShell).toHaveBeenCalledWith('test-repo', 'new-key', 100, true);
|
||||
expect(res._getStatusCode()).toBe(200);
|
||||
expect(res._getJSONData()).toEqual({ message: 'Repository test-repo has been edited' });
|
||||
});
|
||||
});
|
||||
|
|
@ -9,7 +9,7 @@ jest.mock('~/helpers/functions', () => ({
|
|||
}));
|
||||
|
||||
jest.mock('~/helpers/functions/shell.utils', () => ({
|
||||
getStorageUsed: jest.fn(),
|
||||
getStorageUsedShell: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('GET /api/cronjob/getStorageUsed', () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue