Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test #180

Merged
merged 43 commits into from
Jul 11, 2023
Merged

Test #180

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e05130b
commit to initialize branch
ConradStras Jul 4, 2023
c2a4ef2
Merge pull request #167 from COS301-SE-2023/test
JulianPienaar Jul 5, 2023
878986f
Added environment files to alter apiURL
JulianPienaar Jul 5, 2023
8fce3b9
Fixed expiration check on auth token
JulianPienaar Jul 5, 2023
da7f083
Merge pull request #169 from COS301-SE-2023/main
JanSpies82 Jul 6, 2023
1aa1da4
Expanded auth service tests
JanSpies82 Jul 6, 2023
d2a84be
Added asymmetric encryption module
JulianPienaar Jul 6, 2023
53a7201
Removed bcrypt module
JulianPienaar Jul 6, 2023
ea72825
Reduced amount of vulnerabilities
JulianPienaar Jul 6, 2023
bb0963c
Manually update versions of semver
JulianPienaar Jul 6, 2023
a034307
Fixed the vulnerabilities in the frontend
JulianPienaar Jul 6, 2023
2c13133
Changed input validation
JanSpies82 Jul 7, 2023
e5c4f34
Expanded tests
JanSpies82 Jul 7, 2023
dd9f769
Fixed badge specing
JanSpies82 Jul 7, 2023
b127f11
Removed unnecessary check
JanSpies82 Jul 7, 2023
dc4fb93
Removed more unnecessary tests
JanSpies82 Jul 7, 2023
f5d4def
Restructured endpoints
JanSpies82 Jul 8, 2023
06b38df
Expanded tests
JanSpies82 Jul 8, 2023
44f55cb
Added last test
JanSpies82 Jul 8, 2023
831dbdf
Added encryption to all required places
JulianPienaar Jul 8, 2023
523c466
Now decrypts the contents on export
JulianPienaar Jul 8, 2023
68b54f5
Implemented encryption and decryption for export file e2e
jakeweatherhead Jul 8, 2023
7956bb5
Implemented import encryption+decryption
JulianPienaar Jul 8, 2023
9c2455c
Fixed added quotation marks on import
JulianPienaar Jul 9, 2023
db50ced
Test Code Push
JulianPienaar Jul 9, 2023
a89ca3c
Fixed encryption of documents on save and retrieve
JulianPienaar Jul 9, 2023
c4eb567
Refactored SHA cryptojs import
JanSpies82 Jul 9, 2023
36d8a07
Added create tests
JanSpies82 Jul 9, 2023
8ef30f1
Fixed exporting to include encryption
JulianPienaar Jul 10, 2023
53b0125
Removed unnecessary imports
JanSpies82 Jul 10, 2023
ef05b5b
Commented out redundant functions
JanSpies82 Jul 10, 2023
2a254ba
Expanded test coverage
JanSpies82 Jul 10, 2023
ed843f3
Merge branch 'codecov' into dev/encryption
JulianPienaar Jul 10, 2023
850252e
Added SHA256 import
JulianPienaar Jul 10, 2023
1a71bbb
Merge pull request #177 from COS301-SE-2023/dev/encryption
JanSpies82 Jul 10, 2023
8322a9c
Updated test
JanSpies82 Jul 10, 2023
aa4da3c
Edit db connection
JanSpies82 Jul 10, 2023
bea508a
Added missing imports
JanSpies82 Jul 10, 2023
c2c6620
Merge branch 'codecov' into dev/frontend
ConradStras Jul 11, 2023
329dd26
Updated data sent to backend on refresh token
JulianPienaar Jul 11, 2023
7634ecf
Fixed breaking changes
jakeweatherhead Jul 11, 2023
ed46555
Merge pull request #178 from COS301-SE-2023/dev/frontend
JanSpies82 Jul 11, 2023
7ae86b3
Merge pull request #179 from COS301-SE-2023/codecov
JanSpies82 Jul 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
[![Build](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/build.yml)
[![Test Backend](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/test-backend.yml/badge.svg?branch=main)](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/test-backend.yml)
[![Test Integration](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/test-integration.yml/badge.svg?branch=main)](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/test-integration.yml)
[![CodeQL](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/codeql-analysis.yml)
![GitHub issues](https://img.shields.io/github/issues/COS301-SE-2023/WriteToPdf)\
[![CodeQL](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/COS301-SE-2023/WriteToPdf/actions/workflows/codeql-analysis.yml)
![GitHub issues](https://img.shields.io/github/issues/COS301-SE-2023/WriteToPdf)
[![Issues closed](https://img.shields.io/github/issues-closed/COS301-SE-2023/WriteToPdf?color=blue)](https://github.com/COS301-SE-2023/WriteToPdf/issues?q=is%3Aissue+is%3Aclosed)
[![Commits](https://img.shields.io/github/commit-activity/w/COS301-SE-2023/WriteToPdf)](https://github.com/COS301-SE-2023/WriteToPdf/issues)
<!-- [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=COS301-SE-2023_WriteToPdf&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=COS301-SE-2023_WriteToPdf) -->
Expand Down
14 changes: 10 additions & 4 deletions backend/db/data-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ export const testDBOptions: DataSourceOptions = {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.TEST_DB_NAME,
entities: ['dist/**/*.entity.js'],
synchronize: true, // Set this to false in production to prevent automatic schema sync
entities: [
'dist/**/*.entity.{js,ts}',
MarkdownFile,
User,
Folder,
Asset,
],
synchronize: false, // Set this to false in production to prevent automatic schema sync
};

const dataSource = new DataSource(
Expand All @@ -47,6 +53,6 @@ const testDataSource = new DataSource(
testDBOptions,
);

// export { dataSource, testDataSource};
export { dataSource, testDataSource };

export default dataSource;
// export default dataSource;
4 changes: 3 additions & 1 deletion backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ import { MarkdownFile } from './markdown_files/entities/markdown_file.entity';
import { Folder } from './folders/entities/folder.entity';
import { S3Service } from './s3/s3.service';
import { ConversionService } from './conversion/conversion.service';
import { User } from './users/entities/user.entity';

@Module({
imports: [
AuthModule,
EditModule,
UsersModule,
TypeOrmModule.forRoot(dataSourceOptions),
TypeOrmModule.forRoot(testDBOptions),
// TypeOrmModule.forRoot(testDBOptions),
TypeOrmModule.forFeature([MarkdownFile]),
TypeOrmModule.forFeature([Folder]),
TypeOrmModule.forFeature([User]),
MarkdownFilesModule,
AssetsModule,
FoldersModule,
Expand Down
170 changes: 168 additions & 2 deletions backend/src/auth/auth.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import {
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { AuthModule } from './auth.module';
import { RefreshTokenDTO } from './dto/refresh_token.dto';
import {
HttpException,
HttpStatus,
} from '@nestjs/common';
import e from 'express';

describe('AuthController', () => {
let controller: AuthController;
let authService: AuthService;

beforeEach(async () => {
const module: TestingModule =
Expand All @@ -20,9 +27,168 @@ describe('AuthController', () => {
controller = module.get<AuthController>(
AuthController,
);
authService =
module.get<AuthService>(AuthService);
});

it('should be defined', () => {
expect(controller).toBeDefined();
describe('refreshToken', () => {
it('should throw exception if request method is not POST', async () => {
const request = { method: 'GET' };
const refreshTokenDTO =
new RefreshTokenDTO();

try {
await controller.refreshToken(
refreshTokenDTO,
request as any,
);
expect(true).toBe(false);
} catch (error) {
expect(error).toBeInstanceOf(
HttpException,
);
expect(error.message).toBe(
'Method Not Allowed',
);
expect(error.status).toBe(
HttpStatus.METHOD_NOT_ALLOWED,
);
}
});

it('should throw an exception if UserID is undefined', async () => {
const request = { method: 'POST' };
const refreshTokenDTO =
new RefreshTokenDTO();
refreshTokenDTO.Email = 'test';
refreshTokenDTO.Token = 'test';
refreshTokenDTO.ExpiresAt = new Date();

try {
await controller.refreshToken(
refreshTokenDTO,
request as any,
);
expect(true).toBe(false);
} catch (error) {
expect(error).toBeInstanceOf(
HttpException,
);
expect(error.message).toBe(
'Invalid request body',
);
expect(error.status).toBe(
HttpStatus.BAD_REQUEST,
);
}
});

it('should throw an exception if Email is undefined', async () => {
const request = { method: 'POST' };
const refreshTokenDTO =
new RefreshTokenDTO();
refreshTokenDTO.UserID = 1;
refreshTokenDTO.Token = 'test';
refreshTokenDTO.ExpiresAt = new Date();

try {
await controller.refreshToken(
refreshTokenDTO,
request as any,
);
expect(true).toBe(false);
} catch (error) {
expect(error).toBeInstanceOf(
HttpException,
);
expect(error.message).toBe(
'Invalid request body',
);
expect(error.status).toBe(
HttpStatus.BAD_REQUEST,
);
}
});

it('should throw an exception if Token is undefined', async () => {
const request = { method: 'POST' };
const refreshTokenDTO =
new RefreshTokenDTO();
refreshTokenDTO.UserID = 1;
refreshTokenDTO.Email = 'test';
refreshTokenDTO.ExpiresAt = new Date();

try {
await controller.refreshToken(
refreshTokenDTO,
request as any,
);
expect(true).toBe(false);
} catch (error) {
expect(error).toBeInstanceOf(
HttpException,
);
expect(error.message).toBe(
'Invalid request body',
);
expect(error.status).toBe(
HttpStatus.BAD_REQUEST,
);
}
});

it('should throw an exception if ExpiresAt is undefined', async () => {
const request = { method: 'POST' };
const refreshTokenDTO =
new RefreshTokenDTO();
refreshTokenDTO.UserID = 1;
refreshTokenDTO.Email = 'test';
refreshTokenDTO.Token = 'test';

try {
await controller.refreshToken(
refreshTokenDTO,
request as any,
);
expect(true).toBe(false);
} catch (error) {
expect(error).toBeInstanceOf(
HttpException,
);
expect(error.message).toBe(
'Invalid request body',
);
expect(error.status).toBe(
HttpStatus.BAD_REQUEST,
);
}
});

it('should return a new access token', async () => {
const request = { method: 'POST' };
const refreshTokenDTO =
new RefreshTokenDTO();
refreshTokenDTO.UserID = 1;
refreshTokenDTO.Email = 'test';
refreshTokenDTO.Token = 'test';
refreshTokenDTO.ExpiresAt = new Date();

jest
.spyOn(authService, 'refreshToken')
.mockResolvedValue(new RefreshTokenDTO());

const result =
await controller.refreshToken(
refreshTokenDTO,
request as any,
);

expect(result).toBeInstanceOf(
RefreshTokenDTO,
);
expect(
authService.refreshToken,
).toBeCalledWith(refreshTokenDTO);
});
});
});
20 changes: 7 additions & 13 deletions backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,19 @@ export class AuthController {
HttpStatus.METHOD_NOT_ALLOWED,
);
}
const receivedDTO = plainToClass(
RefreshTokenDTO,
refreshTokenDTO,
);
const errors = validateSync(receivedDTO);

if (errors.length > 0) {
if (
!refreshTokenDTO.Email ||
!refreshTokenDTO.Token ||
!refreshTokenDTO.ExpiresAt ||
!refreshTokenDTO.UserID
) {
throw new HttpException(
'Invalid request data',
'Invalid request body',
HttpStatus.BAD_REQUEST,
);
}

if (refreshTokenDTO.UserID === undefined)
throw new HttpException(
'UserID cannot be undefined',
HttpStatus.BAD_REQUEST,
);

return this.authService.refreshToken(
refreshTokenDTO,
);
Expand Down
78 changes: 78 additions & 0 deletions backend/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from '@nestjs/testing';
import { AuthService } from './auth.service';
import { JwtService } from '@nestjs/jwt';
import { RefreshTokenDTO } from './dto/refresh_token.dto';

describe('AuthService', () => {
let service: AuthService;
Expand All @@ -20,6 +21,7 @@ describe('AuthService', () => {
signAsync: jest.fn(
() => 'mockToken',
),
verifyAsync: jest.fn(() => ({})),
},
},
],
Expand All @@ -44,6 +46,10 @@ describe('AuthService', () => {
expires_at: expect.any(Date),
};

jest
.spyOn(jwtService, 'signAsync')
.mockResolvedValue('mockToken');

const actualToken =
await service.generateToken(
username,
Expand All @@ -58,4 +64,76 @@ describe('AuthService', () => {
).toHaveBeenCalledWith(expectedPayload);
});
});

describe('refreshToken', () => {
it('should return a new refresh token', async () => {
const refreshTokenDTO =
new RefreshTokenDTO();
refreshTokenDTO.UserID = 1;
refreshTokenDTO.Email = 'testuser';
refreshTokenDTO.Token = 'testtoken';

const expectedPayload =
new RefreshTokenDTO();
expectedPayload.UserID =
refreshTokenDTO.UserID;
expectedPayload.Email =
refreshTokenDTO.Email;
expectedPayload.Token = 'newtoken';
expectedPayload.ExpiresAt =
expect.any(Date);

jest
.spyOn(jwtService, 'signAsync')
.mockResolvedValue('newtoken');

jest
.spyOn(jwtService, 'verifyAsync')
.mockResolvedValue({});

const actualToken =
await service.refreshToken(
refreshTokenDTO,
);

expect(actualToken).toStrictEqual(
expectedPayload,
);
expect(
jwtService.signAsync,
).toHaveBeenCalled();

expect(
jwtService.verifyAsync,
).toHaveBeenCalledWith(
refreshTokenDTO.Token,
);
});

it('should throw an error if the token is invalid', async () => {
const refreshTokenDTO =
new RefreshTokenDTO();
refreshTokenDTO.UserID = 1;
refreshTokenDTO.Email = 'testuser';
refreshTokenDTO.Token = 'invalidtoken';

jest
.spyOn(jwtService, 'verifyAsync')
.mockRejectedValue(new Error());

await expect(
service.refreshToken(refreshTokenDTO),
).rejects.toThrow();

expect(
jwtService.verifyAsync,
).toHaveBeenCalledWith(
refreshTokenDTO.Token,
);

expect(
jwtService.signAsync,
).not.toHaveBeenCalled();
});
});
});
2 changes: 1 addition & 1 deletion backend/src/conversion/conversion.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class ConversionService {

convertToTxt(markdownDTO: ExportDTO) {
const html = convertDeltaToHtml(
markdownDTO.Content,
JSON.parse(markdownDTO.Content),
);

const text = html.replace(
Expand Down
Loading