diff --git a/src/config/index.ts b/src/config/index.ts
index 06c1248..82606ec 100644
--- a/src/config/index.ts
+++ b/src/config/index.ts
@@ -23,6 +23,7 @@ export const ADD_TEAM = 'add-team';
export const TEAM_REQUEST = 'team-request';
export const ADD_TEAM_MEMBER = 'add-team-member';
export const ADD_REPO = 'add-repo';
+export const REPO_REQUEST = 'repo-request';
// Routing paths
@@ -36,6 +37,7 @@ export const ADD_TEAM_URL = '/add-team';
export const TEAM_REQUEST_URL = '/team-request';
export const ADD_TEAM_MEMBER_URL = '/add-team-member';
export const ADD_REPO_URL = '/add-repo';
+export const REPO_REQUEST_URL = '/repo-request';
export const SERVICE_URL = `${BASE_URL}${CONFIRMATION_URL}`;
diff --git a/src/controller/repo-request.controller.ts b/src/controller/repo-request.controller.ts
new file mode 100644
index 0000000..9703880
--- /dev/null
+++ b/src/controller/repo-request.controller.ts
@@ -0,0 +1,18 @@
+import { Request, Response } from 'express';
+import { log } from '../utils/logger';
+import * as config from '../config';
+
+export const get = (_req: Request, res: Response) => {
+ return res.render(config.REPO_REQUEST);
+};
+
+export const post = (req: Request, res: Response) => {
+
+ const repoName = req.body.repo_name;
+
+ // validation middleware and data assignment to be implemented
+
+ log.info(`Repository Name: ${repoName}`);
+
+ return res.redirect(config.LANDING);
+};
diff --git a/src/routes/index.ts b/src/routes/index.ts
index 8cccbff..6117779 100644
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -12,6 +12,7 @@ import removeMemberRouter from './remove-member';
import teamRequestRouter from './team-request';
import addTeamMemberRouter from './add-team-member';
import memberRequestRouter from './member-request';
+import repoRequestRouter from './repo-request';
const router = Router();
@@ -29,5 +30,6 @@ router.use(removeMemberRouter);
router.use(teamRequestRouter);
router.use(addTeamMemberRouter);
router.use(memberRequestRouter);
+router.use(repoRequestRouter);
export default router;
diff --git a/src/routes/repo-request.ts b/src/routes/repo-request.ts
new file mode 100644
index 0000000..22ad656
--- /dev/null
+++ b/src/routes/repo-request.ts
@@ -0,0 +1,13 @@
+import { Router } from 'express';
+
+import { authentication } from '../middleware/authentication.middleware';
+
+import { get, post } from '../controller/repo-request.controller';
+import * as config from '../config';
+
+const addRepoRouter = Router();
+
+addRepoRouter.get(config.REPO_REQUEST_URL, authentication, get);
+addRepoRouter.post(config.REPO_REQUEST_URL, authentication, post);
+
+export default addRepoRouter;
diff --git a/src/views/landing-page.html b/src/views/landing-page.html
index dc9aed8..595e0f7 100644
--- a/src/views/landing-page.html
+++ b/src/views/landing-page.html
@@ -45,7 +45,7 @@
Repo requests
Add Repo
- Additional Repo Requests (e.g permission changes)
+ Additional Repo Requests (e.g permission changes)
diff --git a/src/views/repo-request.html b/src/views/repo-request.html
new file mode 100644
index 0000000..1b3e183
--- /dev/null
+++ b/src/views/repo-request.html
@@ -0,0 +1,43 @@
+{% extends "layout.html" %}
+
+{% block beforeContent %}
+ {% include "include/back-link.html" %}
+{% endblock %}
+
+{% block content %}
+
+
+
Additional Repository Requests
+
+
+ To request a more specific repository request that is not covered by the other options, such as permission changes.
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/test/integration/routes/repo-request.spec.ts b/test/integration/routes/repo-request.spec.ts
new file mode 100644
index 0000000..67d38ec
--- /dev/null
+++ b/test/integration/routes/repo-request.spec.ts
@@ -0,0 +1,58 @@
+jest.mock('../../../src/middleware/logger.middleware');
+jest.mock('../../../src/middleware/authentication.middleware');
+jest.mock('../../../src/utils/logger');
+
+import { jest, beforeEach, describe, expect, test } from '@jest/globals';
+import { Request, Response, NextFunction } from 'express';
+import request from 'supertest';
+
+import app from '../../../src/app';
+import * as config from '../../../src/config';
+import { logger } from '../../../src/middleware/logger.middleware';
+import { log } from '../../../src/utils/logger';
+import { authentication } from '../../../src/middleware/authentication.middleware';
+
+import { MOCK_REDIRECT_MESSAGE, MOCK_GET_REPO_REQUEST_RESPONSE, MOCK_POST_REPO_REQUEST_RESPONSE } from '../../mock/text.mock';
+import { MOCK_POST_REPO_REQUEST } from '../../mock/data';
+
+const mockedLogger = logger as jest.Mock;
+mockedLogger.mockImplementation((_req: Request, _res: Response, next: NextFunction) => next());
+const mockedAuth = authentication as jest.Mock;
+mockedAuth.mockImplementation((_req: Request, _res: Response, next: NextFunction) => next());
+
+describe('repo-request endpoint integration tests', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('GET tests', () => {
+ test('renders the repo-request page', async () => {
+ const res = await request(app).get(config.REPO_REQUEST_URL);
+
+ expect(res.status).toEqual(200);
+ expect(res.text).toContain(MOCK_GET_REPO_REQUEST_RESPONSE);
+ expect(mockedLogger).toHaveBeenCalledTimes(1);
+ expect(mockedAuth).toHaveBeenCalledTimes(1);
+ });
+ });
+ describe('POST tests', () => {
+ test('Should redirect to landing page after POST request', async () => {
+ const res = await request(app).post(config.REPO_REQUEST_URL).send(MOCK_POST_REPO_REQUEST);
+
+ expect(res.status).toEqual(302);
+ expect(res.text).toContain(MOCK_REDIRECT_MESSAGE);
+ expect(mockedLogger).toHaveBeenCalledTimes(1);
+ expect(mockedAuth).toHaveBeenCalledTimes(1);
+ });
+ test('Should log the repository name and description on POST request', async () => {
+ const res = await request(app).post(config.REPO_REQUEST_URL).send(MOCK_POST_REPO_REQUEST);
+
+ const mockLog = log.info as jest.Mock;
+
+ expect(mockLog).toBeCalledWith(MOCK_POST_REPO_REQUEST_RESPONSE);
+ expect(res.text).toContain(MOCK_REDIRECT_MESSAGE);
+ expect(mockedLogger).toHaveBeenCalledTimes(1);
+ expect(mockedAuth).toHaveBeenCalledTimes(1);
+ });
+ });
+});
diff --git a/test/mock/data.ts b/test/mock/data.ts
index 383d015..6b279fd 100644
--- a/test/mock/data.ts
+++ b/test/mock/data.ts
@@ -10,6 +10,7 @@ export const MOCK_POST_TEAM_REQUEST = { team_name: 'team1' };
export const MOCK_POST_MEMBER_REQUEST = { github_handle: 'example' };
export const MOCK_POST_ADD_TEAM_MEMBER = { team_name: 'team1', team_member_github_handle: 'joe' };
+export const MOCK_POST_REPO_REQUEST = { repo_name: 'repo1' };
export const MOCK_CORS_VALUE = {
origin: [config.CDN_HOST, config.BASE_URL],
diff --git a/test/mock/text.mock.ts b/test/mock/text.mock.ts
index 43f0d9e..dd2b313 100644
--- a/test/mock/text.mock.ts
+++ b/test/mock/text.mock.ts
@@ -23,6 +23,9 @@ export const MOCK_POST_MEMBER_REQUEST_RESPONSE = 'GitHub Handle: example';
export const MOCK_GET_ADD_TEAM_MEMBER_RESPONSE = 'Add a GitHub member to a team';
export const MOCK_POST_ADD_TEAM_MEMBER_RESPONSE = 'Team Name: team1, Team Member GitHub Handle: joe';
+export const MOCK_GET_REPO_REQUEST_RESPONSE = 'Additional Repository Requests';
+export const MOCK_POST_REPO_REQUEST_RESPONSE = 'Repository Name: repo1';
+
export const MOCK_NOT_FOUND_RESPONSE = 'Page not found';
export const MOCK_SERVICE_UNAVAILABLE = 'Sorry, there is a problem with the service';
export const MOCK_SERVER_ERROR = 'Pipe 3000 requires elevated privileges';
diff --git a/test/unit/controller/repo-request.controller.spec.ts b/test/unit/controller/repo-request.controller.spec.ts
new file mode 100644
index 0000000..b39320d
--- /dev/null
+++ b/test/unit/controller/repo-request.controller.spec.ts
@@ -0,0 +1,58 @@
+jest.mock('../../../src/utils/logger');
+import { describe, expect, afterEach, test, jest } from '@jest/globals';
+import { Request, Response } from 'express';
+
+import { get, post } from '../../../src/controller/repo-request.controller';
+import * as config from '../../../src/config';
+import { log } from '../../../src/utils/logger';
+
+import { MOCK_POST_REPO_REQUEST } from '../../mock/data';
+import { MOCK_POST_REPO_REQUEST_RESPONSE } from '../../mock/text.mock';
+
+const req = {
+ body: MOCK_POST_REPO_REQUEST
+} as Request;
+
+const mockResponse = () => {
+ const res = {} as Response;
+ res.render = jest.fn().mockReturnValue(res) as any;
+ res.redirect = jest.fn().mockReturnValue(res) as any;
+ return res;
+};
+
+describe('repo-request controller test suites', () => {
+ afterEach(() => {
+ jest.resetAllMocks();
+ });
+
+ describe('repo-request GET tests', () => {
+
+ test('should render repo request page', () => {
+ const res = mockResponse();
+
+ get(req, res);
+
+ expect(res.render).toHaveBeenCalledWith(config.REPO_REQUEST);
+ });
+ });
+
+ describe('repo-request POST tests', () => {
+
+ test('should redirect to landing-page on POST request', () => {
+ const res = mockResponse();
+
+ post(req, res);
+
+ expect(res.redirect).toBeCalledWith(config.LANDING);
+ });
+ test('should log Repository Name on POST request', () => {
+ const res = mockResponse();
+
+ const mockLogInfo = log.info as jest.Mock;
+
+ post(req, res);
+
+ expect(mockLogInfo).toHaveBeenCalledWith(MOCK_POST_REPO_REQUEST_RESPONSE);
+ });
+ });
+});