Skip to content

Commit

Permalink
Merge branch 'dev' into stop-sending-full-defences-on-low-leevels
Browse files Browse the repository at this point in the history
  • Loading branch information
pmarsh-scottlogic committed Mar 12, 2024
2 parents 360c30c + 80d4480 commit 600dc65
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 11 deletions.
8 changes: 7 additions & 1 deletion backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"memorystore": "^1.6.7",
"openai": "^4.19.0",
"openai-chat-tokens": "^0.2.8",
"pdf-parse": "^1.1.1"
"pdf-parse": "^1.1.1",
"query-types": "^0.1.4"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
Expand Down
2 changes: 2 additions & 0 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import cors from 'cors';
import express from 'express';
import queryTypes from 'query-types';

import nonSessionRoutes from './nonSessionRoutes';
import sessionRoutes from './sessionRoutes';

export default express()
.use(express.json())
.use(queryTypes.middleware())
.use(
cors({
origin: process.env.CORS_ALLOW_ORIGIN,
Expand Down
7 changes: 5 additions & 2 deletions backend/src/controller/startController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Response } from 'express';

import { StartGetRequest } from '@src/models/api/StartGetRequest';
import {
StartGetRequest,
StartResponse,
} from '@src/models/api/StartGetRequest';
import { LEVEL_NAMES, isValidLevel } from '@src/models/level';
import { getValidOpenAIModels } from '@src/openai';
import {
Expand Down Expand Up @@ -36,7 +39,7 @@ function handleStart(req: StartGetRequest, res: Response) {
defences: req.session.levelState[level].defences,
availableModels: getValidOpenAIModels(),
systemRoles,
});
} as StartResponse);
}

export { handleStart };
19 changes: 12 additions & 7 deletions backend/src/models/api/StartGetRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import { Defence } from '@src/models/defence';
import { EmailInfo } from '@src/models/email';
import { LEVEL_NAMES } from '@src/models/level';

export type StartResponse = {
emails: EmailInfo[];
chatHistory: ChatMessage[];
defences: Defence[];
availableModels: string[];
systemRoles: {
level: LEVEL_NAMES;
systemRole: string;
}[];
};

export type StartGetRequest = Request<
never,
{
emails: EmailInfo[];
chatHistory: ChatMessage[];
defences: Defence[];
availableModels: string[];
systemRoles: string[];
},
StartResponse,
never,
{
level?: LEVEL_NAMES;
Expand Down
54 changes: 54 additions & 0 deletions backend/test/api/start.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { beforeAll, describe, expect, it, jest } from '@jest/globals';
import { OpenAI } from 'openai';
import request from 'supertest';

import app from '@src/app';
import { StartResponse } from '@src/models/api/StartGetRequest';
import { LEVEL_NAMES } from '@src/models/level';

jest.mock('openai');

const PATH = '/start';

describe('/start endpoints', () => {
const mockListFn = jest.fn<OpenAI.Models['list']>();
jest.mocked(OpenAI).mockImplementation(
() =>
({
models: {
list: mockListFn,
},
} as unknown as jest.MockedObject<OpenAI>)
);

beforeAll(() => {
mockListFn.mockResolvedValue({
data: [{ id: 'gpt-3.5-turbo' }],
} as OpenAI.ModelsPage);
});

it.each(Object.values(LEVEL_NAMES))(
'WHEN given valid level [%s] THEN it responds with 200',
async (level) =>
request(app)
.get(`${PATH}?level=${level}`)
.expect(200)
.expect('Content-Type', /application\/json/)
.then((response) => {
const { chatHistory, emails } = response.body as StartResponse;
expect(chatHistory).toEqual([]);
expect(emails).toEqual([]);
})
);

it.each([-1, 4, 'SANDBOX'])(
'WHEN given invalid level [%s] THEN it responds with 400',
async (level) =>
request(app)
.get(`${PATH}?level=${level}`)
.expect(400)
.then((response) => {
expect(response.text).toEqual('Invalid level');
})
);
});
1 change: 1 addition & 0 deletions backend/test/setupEnvVars.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// set the environment variables
process.env.OPENAI_API_KEY = 'sk-12345';
process.env.SESSION_SECRET = "shhh! Don't tell anyone...";
5 changes: 5 additions & 0 deletions backend/typings/query-types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare module 'query-types' {
import type { NextHandleFunction } from 'connect';

function middleware(): NextHandleFunction;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ describe('LevelMissionInfoBanner component tests', () => {
<LevelMissionInfoBanner
currentLevel={currentLevel}
openOverlay={() => {}}
openLevelsCompleteOverlay={() => {}}
numCompletedLevels={currentLevel}
/>
);

Expand All @@ -41,6 +43,8 @@ describe('LevelMissionInfoBanner component tests', () => {
<LevelMissionInfoBanner
currentLevel={currentLevel}
openOverlay={openOverlayMock}
openLevelsCompleteOverlay={() => {}}
numCompletedLevels={currentLevel}
/>
);

Expand All @@ -49,4 +53,23 @@ describe('LevelMissionInfoBanner component tests', () => {

expect(openOverlayMock).toHaveBeenCalled();
});

test('fires the openLevelsCompleteOverlay callback on button click', () => {
const currentLevel = LEVEL_NAMES.LEVEL_3;

const openOverlayMock = vi.fn();
render(
<LevelMissionInfoBanner
currentLevel={currentLevel}
openOverlay={() => {}}
openLevelsCompleteOverlay={openOverlayMock}
numCompletedLevels={4}
/>
);

const button = screen.getByText('Congratulations!');
fireEvent.click(button);

expect(openOverlayMock).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@ import './LevelMissionInfoBanner.css';
function LevelMissionInfoBanner({
currentLevel,
openOverlay,
openLevelsCompleteOverlay,
numCompletedLevels,
}: {
currentLevel: LEVEL_NAMES;
openOverlay: () => void;
openLevelsCompleteOverlay: () => void;
numCompletedLevels: number;
}) {
const isLevelComplete = currentLevel < numCompletedLevels;
const isLevel3 = currentLevel === LEVEL_NAMES.LEVEL_3;

return (
<span className="level-mission-info-banner">
<h2 className="level-title-area">{`Level ${currentLevel + 1}`}</h2>
Expand All @@ -25,6 +32,11 @@ function LevelMissionInfoBanner({
</span>
Mission Info
</ThemedButton>
{isLevelComplete && isLevel3 && (
<ThemedButton onClick={openLevelsCompleteOverlay}>
Congratulations!
</ThemedButton>
)}
</span>
);
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/MainComponent/MainComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ function MainComponent({
<LevelMissionInfoBanner
currentLevel={currentLevel}
openOverlay={openInformationOverlay}
openLevelsCompleteOverlay={openLevelsCompleteOverlay}
numCompletedLevels={numCompletedLevels}
/>
)}
<MainBody
Expand Down

0 comments on commit 600dc65

Please sign in to comment.