Skip to content

Commit

Permalink
Merge pull request #179 from COS301-SE-2023/codecov
Browse files Browse the repository at this point in the history
Codecov -> Test
  • Loading branch information
JanSpies82 authored Jul 11, 2023
2 parents da7f083 + ed46555 commit 7ae86b3
Show file tree
Hide file tree
Showing 65 changed files with 6,790 additions and 5,300 deletions.
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

0 comments on commit 7ae86b3

Please sign in to comment.