Compare commits

...

37 Commits

Author SHA1 Message Date
f2c50e929e Merge branch 'dev' of git.odit.services:lfk/backend into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-09 19:05:00 +01:00
02e3239848 Reverted temporary logging 2021-01-09 19:04:07 +01:00
8a54b027d0 Reverted temporary logging
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-09 18:55:16 +01:00
3b11e896d4 Merge branch 'dev' of git.odit.services:lfk/backend into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-09 18:50:50 +01:00
89926b2c31 Temporary: extended live logging 2021-01-09 18:50:48 +01:00
7b4e89555e Temporary: extended live logging
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-09 18:47:11 +01:00
1e37186247 Revert "Temporary: extended live logging"
This reverts commit 154c763719.
2021-01-09 18:45:44 +01:00
154c763719 Temporary: extended live logging
All checks were successful
continuous-integration/drone/push Build is passing
ref lfk/frontend#28
2021-01-09 18:08:36 +01:00
80197d5834 Merge pull request 'feature/78-trackscan' (#85) from feature/78-trackscan into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #85
2021-01-09 16:33:09 +00:00
7e95103a2d added trackscan update tests
All checks were successful
continuous-integration/drone/pr Build is passing
ref #78
2021-01-09 17:18:33 +01:00
efe1a1f543 added trackscan delete tests
ref #78
2021-01-09 16:56:57 +01:00
4fea690670 Added missing parameter fro negative-test
ref #78
2021-01-09 16:54:19 +01:00
f1dee1061d added trackscan get tests
ref #78
2021-01-09 16:49:17 +01:00
61cf0fc08d Implemented proper scan invalidation
ref #78
2021-01-09 16:47:54 +01:00
0c86e5dae1 added trackscan add tests
ref #78
2021-01-09 16:44:52 +01:00
638898fa28 Implemented trackscan updateing
ref #78
2021-01-09 16:17:50 +01:00
e7cd68e1c8 removed distance checks from tests
ref #78
2021-01-09 15:59:36 +01:00
e40e6faebd Merge branch 'feature/78-trackscan' of git.odit.services:lfk/backend into feature/78-trackscan
# Conflicts:
#	src/controllers/RunnerController.ts
2021-01-09 15:45:35 +01:00
3d07aac944 Implemented cascading scan, track and card deletion
ref #78
2021-01-09 15:45:17 +01:00
1a5493facf Implemented cascading scan, track and card deletion
ref #78
2021-01-09 15:43:52 +01:00
9013b9492c Fixed runner distance resolution
ref #78
2021-01-09 15:25:11 +01:00
188f26ad65 Fixed manual trackscan creation
ref #78
2021-01-09 14:52:08 +01:00
3ceb5a0c0f Removed total distance from tests
ref #78
2021-01-09 14:24:16 +01:00
e1ce052d3c Fixed runner total distance not getting resolved
ref #78
2021-01-09 14:23:47 +01:00
70a379edef Merge pull request 'New feature: runner cards (feature/77-runner_cards)' (#84) from feature/77-runner_cards into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #84
2021-01-09 13:01:50 +00:00
35ea3154d1 Added card update tests
All checks were successful
continuous-integration/drone/pr Build is passing
ref #77
2021-01-09 12:42:41 +01:00
ebf66821a2 Added card delete tests
ref #77
2021-01-09 12:41:59 +01:00
8463bee253 added card add tests
ref #77
2021-01-09 12:32:47 +01:00
860680d001 Implmented the EAN generation
ref #77
2021-01-09 12:24:05 +01:00
df39166279 Added card get tests
ref #77
2021-01-09 11:59:20 +01:00
32fda46f0a Implemented runner updateing
ref #77
2021-01-09 11:55:32 +01:00
36ecae7e6e Added card creation
#17
2021-01-09 11:48:13 +01:00
a5bfe4e3d5 Added card deletion + errors
ref #77
2021-01-09 11:28:59 +01:00
4faeddc3f3 Added runner card get endpoints
ref #77
2021-01-09 11:23:12 +01:00
98f7bf366f Added card permission target
ref #77
2021-01-09 11:21:52 +01:00
af3a9e5ce2 Added basic response calss for runner cards
ref #77
2021-01-09 11:15:29 +01:00
52eb7b1afe Added a barebones runnercard controller
ref #77
2021-01-09 11:10:05 +01:00
27 changed files with 1616 additions and 72 deletions

View File

@@ -0,0 +1,106 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm';
import { RunnerCardHasScansError, RunnerCardIdsNotMatchingError, RunnerCardNotFoundError } from '../errors/RunnerCardErrors';
import { RunnerNotFoundError } from '../errors/RunnerErrors';
import { CreateRunnerCard } from '../models/actions/CreateRunnerCard';
import { UpdateRunnerCard } from '../models/actions/UpdateRunnerCard';
import { RunnerCard } from '../models/entities/RunnerCard';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseRunnerCard } from '../models/responses/ResponseRunnerCard';
import { ScanController } from './ScanController';
@JsonController('/cards')
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
export class RunnerCardController {
private cardRepository: Repository<RunnerCard>;
/**
* Gets the repository of this controller's model/entity.
*/
constructor() {
this.cardRepository = getConnectionManager().get().getRepository(RunnerCard);
}
@Get()
@Authorized("CARD:GET")
@ResponseSchema(ResponseRunnerCard, { isArray: true })
@OpenAPI({ description: 'Lists all card.' })
async getAll() {
let responseCards: ResponseRunnerCard[] = new Array<ResponseRunnerCard>();
const cards = await this.cardRepository.find({ relations: ['runner'] });
cards.forEach(card => {
responseCards.push(new ResponseRunnerCard(card));
});
return responseCards;
}
@Get('/:id')
@Authorized("CARD:GET")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerCardNotFoundError, { statusCode: 404 })
@OnUndefined(RunnerCardNotFoundError)
@OpenAPI({ description: "Lists all information about the card whose id got provided." })
async getOne(@Param('id') id: number) {
let card = await this.cardRepository.findOne({ id: id }, { relations: ['runner'] });
if (!card) { throw new RunnerCardNotFoundError(); }
return card.toResponse();
}
@Post()
@Authorized("CARD:CREATE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OpenAPI({ description: "Create a new card. <br> You can provide a associated runner by id but you don't have to." })
async post(@Body({ validate: true }) createCard: CreateRunnerCard) {
let card = await createCard.toEntity();
card = await this.cardRepository.save(card);
return (await this.cardRepository.findOne({ id: card.id }, { relations: ['runner'] })).toResponse();
}
@Put('/:id')
@Authorized("CARD:UPDATE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerCardNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerCardIdsNotMatchingError, { statusCode: 406 })
@OpenAPI({ description: "Update the card whose id you provided. <br> Scans created via this card will still be associated with the old runner. <br> Please remember that ids can't be changed." })
async put(@Param('id') id: number, @Body({ validate: true }) card: UpdateRunnerCard) {
let oldCard = await this.cardRepository.findOne({ id: id });
if (!oldCard) {
throw new RunnerCardNotFoundError();
}
if (oldCard.id != card.id) {
throw new RunnerCardIdsNotMatchingError();
}
await this.cardRepository.save(await card.update(oldCard));
return (await this.cardRepository.findOne({ id: id }, { relations: ['runner'] })).toResponse();
}
@Delete('/:id')
@Authorized("CARD:DELETE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(ResponseEmpty, { statusCode: 204 })
@ResponseSchema(RunnerCardHasScansError, { statusCode: 406 })
@OnUndefined(204)
@OpenAPI({ description: "Delete the card whose id you provided. <br> If no card with this id exists it will just return 204(no content). <br> If the card still has scans associated you have to provide the force=true query param (warning: this deletes all scans associated with by this card - please disable it instead or just remove the runner association)." })
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
let card = await this.cardRepository.findOne({ id: id });
if (!card) { return null; }
const cardScans = (await this.cardRepository.findOne({ id: id }, { relations: ["scans"] })).scans;
if (cardScans.length != 0 && !force) {
throw new RunnerCardHasScansError();
}
const scanController = new ScanController;
for (let scan of cardScans) {
await scanController.remove(scan.id, force);
}
await this.cardRepository.delete(card);
return card.toResponse();
}
}

View File

@@ -8,6 +8,8 @@ import { UpdateRunner } from '../models/actions/UpdateRunner';
import { Runner } from '../models/entities/Runner';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseRunner } from '../models/responses/ResponseRunner';
import { RunnerCardController } from './RunnerCardController';
import { ScanController } from './ScanController';
@JsonController('/runners')
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
@@ -27,7 +29,7 @@ export class RunnerController {
@OpenAPI({ description: 'Lists all runners from all teams/orgs. <br> This includes the runner\'s group and distance ran.' })
async getAll() {
let responseRunners: ResponseRunner[] = new Array<ResponseRunner>();
const runners = await this.runnerRepository.find({ relations: ['scans', 'group'] });
const runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'scans.track', 'cards'] });
runners.forEach(runner => {
responseRunners.push(new ResponseRunner(runner));
});
@@ -41,7 +43,7 @@ export class RunnerController {
@OnUndefined(RunnerNotFoundError)
@OpenAPI({ description: 'Lists all information about the runner whose id got provided.' })
async getOne(@Param('id') id: number) {
let runner = await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group'] })
let runner = await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group', 'scans.track', 'cards'] })
if (!runner) { throw new RunnerNotFoundError(); }
return new ResponseRunner(runner);
}
@@ -61,7 +63,7 @@ export class RunnerController {
}
runner = await this.runnerRepository.save(runner)
return new ResponseRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group'] }));
return new ResponseRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'scans.track', 'cards'] }));
}
@Put('/:id')
@@ -82,7 +84,7 @@ export class RunnerController {
}
await this.runnerRepository.save(await runner.updateRunner(oldRunner));
return new ResponseRunner(await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group'] }));
return new ResponseRunner(await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group', 'scans.track', 'cards'] }));
}
@Delete('/:id')
@@ -90,16 +92,28 @@ export class RunnerController {
@ResponseSchema(ResponseRunner)
@ResponseSchema(ResponseEmpty, { statusCode: 204 })
@OnUndefined(204)
@OpenAPI({ description: 'Delete the runner whose id you provided. <br> If no runner with this id exists it will just return 204(no content).' })
@OpenAPI({ description: 'Delete the runner whose id you provided. <br> This will also delete all scans and cards associated with the runner. <br> If no runner with this id exists it will just return 204(no content).' })
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
let runner = await this.runnerRepository.findOne({ id: id });
if (!runner) { return null; }
const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group'] });
const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'scans.track', 'cards'] });
if (!runner) {
throw new RunnerNotFoundError();
}
const runnerCards = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["cards"] })).cards;
const cardController = new RunnerCardController;
for (let scan of runnerCards) {
await cardController.remove(scan.id, force);
}
const runnerScans = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["scans"] })).scans;
const scanController = new ScanController;
for (let scan of runnerScans) {
await scanController.remove(scan.id, force);
}
await this.runnerRepository.delete(runner);
return new ResponseRunner(responseRunner);
}

View File

@@ -3,11 +3,14 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm';
import { RunnerNotFoundError } from '../errors/RunnerErrors';
import { ScanIdsNotMatchingError, ScanNotFoundError } from '../errors/ScanErrors';
import { ScanStationNotFoundError } from '../errors/ScanStationErrors';
import ScanAuth from '../middlewares/ScanAuth';
import { CreateScan } from '../models/actions/CreateScan';
import { CreateTrackScan } from '../models/actions/CreateTrackScan';
import { UpdateScan } from '../models/actions/UpdateScan';
import { UpdateTrackScan } from '../models/actions/UpdateTrackScan';
import { Scan } from '../models/entities/Scan';
import { TrackScan } from '../models/entities/TrackScan';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseScan } from '../models/responses/ResponseScan';
import { ResponseTrackScan } from '../models/responses/ResponseTrackScan';
@@ -16,12 +19,14 @@ import { ResponseTrackScan } from '../models/responses/ResponseTrackScan';
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
export class ScanController {
private scanRepository: Repository<Scan>;
private trackScanRepository: Repository<TrackScan>;
/**
* Gets the repository of this controller's model/entity.
*/
constructor() {
this.scanRepository = getConnectionManager().get().getRepository(Scan);
this.trackScanRepository = getConnectionManager().get().getRepository(TrackScan);
}
@Get()
@@ -31,7 +36,7 @@ export class ScanController {
@OpenAPI({ description: 'Lists all scans (normal or track) from all runners. <br> This includes the scan\'s runner\'s distance ran.' })
async getAll() {
let responseScans: ResponseScan[] = new Array<ResponseScan>();
const scans = await this.scanRepository.find({ relations: ['runner', 'runner.scans', 'runner.scans.track'] });
const scans = await this.scanRepository.find({ relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] });
scans.forEach(scan => {
responseScans.push(scan.toResponse());
});
@@ -46,7 +51,7 @@ export class ScanController {
@OnUndefined(ScanNotFoundError)
@OpenAPI({ description: 'Lists all information about the scan whose id got provided. This includes the scan\'s runner\'s distance ran.' })
async getOne(@Param('id') id: number) {
let scan = await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'runner.scans', 'runner.scans.track'] })
let scan = await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })
if (!scan) { throw new ScanNotFoundError(); }
return scan.toResponse();
}
@@ -55,20 +60,22 @@ export class ScanController {
@UseBefore(ScanAuth)
@ResponseSchema(ResponseScan)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OpenAPI({ description: 'Create a new scan. <br> Please remeber to provide the scan\'s runner\'s id and distance for normal scans.', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
@OpenAPI({ description: 'Create a new scan (not track scan - use /scans/trackscans instead). <br> Please rmemember to provide the scan\'s runner\'s id and distance.', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async post(@Body({ validate: true }) createScan: CreateScan) {
let scan = await createScan.toScan();
scan = await this.scanRepository.save(scan);
return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner'] })).toResponse();
return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse();
}
@Post("/trackscans")
@UseBefore(ScanAuth)
@ResponseSchema(ResponseScan)
@ResponseSchema(ResponseTrackScan)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OpenAPI({ description: 'Create a new track scan. <br> This is just a alias for posting /scans', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
@OpenAPI({ description: 'Create a new track scan (for "normal" scans use /scans instead). <br> Please remember that to provide the scan\'s card\'s station\'s id.', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async postTrackScans(@Body({ validate: true }) createScan: CreateTrackScan) {
return this.post(createScan);
let scan = await createScan.toScan();
scan = await this.trackScanRepository.save(scan);
return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse();
}
@Put('/:id')
@@ -77,7 +84,7 @@ export class ScanController {
@ResponseSchema(ScanNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@ResponseSchema(ScanIdsNotMatchingError, { statusCode: 406 })
@OpenAPI({ description: "Update the scan whose id you provided. <br> Please remember that ids can't be changed and distances must be positive." })
@OpenAPI({ description: "Update the scan (not track scan use /scans/trackscans/:id instead) whose id you provided. <br> Please remember that ids can't be changed and distances must be positive." })
async put(@Param('id') id: number, @Body({ validate: true }) scan: UpdateScan) {
let oldScan = await this.scanRepository.findOne({ id: id });
@@ -90,7 +97,30 @@ export class ScanController {
}
await this.scanRepository.save(await scan.updateScan(oldScan));
return (await this.scanRepository.findOne({ id: id }, { relations: ['runner'] })).toResponse();
return (await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse();
}
@Put('/trackscans/:id')
@Authorized("SCAN:UPDATE")
@ResponseSchema(ResponseTrackScan)
@ResponseSchema(ScanNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@ResponseSchema(ScanStationNotFoundError, { statusCode: 404 })
@ResponseSchema(ScanIdsNotMatchingError, { statusCode: 406 })
@OpenAPI({ description: 'Update the track scan (not "normal" scan use /scans/trackscans/:id instead) whose id you provided. <br> Please remember that only the validity, runner and track can be changed.' })
async putTrackScan(@Param('id') id: number, @Body({ validate: true }) scan: UpdateTrackScan) {
let oldScan = await this.trackScanRepository.findOne({ id: id });
if (!oldScan) {
throw new ScanNotFoundError();
}
if (oldScan.id != scan.id) {
throw new ScanIdsNotMatchingError();
}
await this.trackScanRepository.save(await scan.updateScan(oldScan));
return (await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse();
}
@Delete('/:id')
@@ -102,7 +132,7 @@ export class ScanController {
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
let scan = await this.scanRepository.findOne({ id: id });
if (!scan) { return null; }
const responseScan = await this.scanRepository.findOne({ id: scan.id }, { relations: ["runner"] });
const responseScan = await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] });
await this.scanRepository.delete(scan);
return responseScan.toResponse();

View File

@@ -98,7 +98,7 @@ export class ScanStationController {
}
const scanController = new ScanController;
for (let scan of stationScans) {
scanController.remove(scan.id, force);
await scanController.remove(scan.id, force);
}
const responseStation = await this.stationRepository.findOne({ id: station.id }, { relations: ["track"] });

View File

@@ -94,9 +94,9 @@ export class TrackController {
if (trackStations.length != 0 && !force) {
throw new TrackHasScanStationsError();
}
const scanController = new ScanStationController;
const stationController = new ScanStationController;
for (let station of trackStations) {
scanController.remove(station.id, force);
await stationController.remove(station.id, force);
}
await this.trackRepository.delete(track);

View File

@@ -88,9 +88,9 @@ export class UserGroupController {
if (!group) { return null; }
const responseGroup = await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions'] });
const permissionControler = new PermissionController();
const permissionController = new PermissionController();
for (let permission of responseGroup.permissions) {
await permissionControler.remove(permission.id, true);
await permissionController.remove(permission.id, true);
}
await this.userGroupsRepository.delete(group);

View File

@@ -0,0 +1,48 @@
import { IsString } from 'class-validator';
import { NotAcceptableError, NotFoundError } from 'routing-controllers';
/**
* Error to throw when a card couldn't be found.
*/
export class RunnerCardNotFoundError extends NotFoundError {
@IsString()
name = "RunnerCardNotFoundError"
@IsString()
message = "Card not found!"
}
/**
* Error to throw when two cards' ids don't match.
* Usually occurs when a user tries to change a card's id.
*/
export class RunnerCardIdsNotMatchingError extends NotAcceptableError {
@IsString()
name = "RunnerCardIdsNotMatchingError"
@IsString()
message = "The ids don't match! \n And if you wanted to change a cards's id: This isn't allowed"
}
/**
* Error to throw when a card still has scans associated.
*/
export class RunnerCardHasScansError extends NotAcceptableError {
@IsString()
name = "RunnerCardHasScansError"
@IsString()
message = "This card still has scans associated with it. \n If you want to delete this card with all it's scans add `?force` to your query. \n Otherwise please consider just diableing it."
}
/**
* Error to throw when a card's id is too big to generate a ean-13 barcode for it.
* This error should never reach a enduser.
*/
export class RunnerCardIdOutOfRangeError extends Error {
@IsString()
name = "RunnerCardIdOutOfRangeError"
@IsString()
message = "The card's id is too big to fit into a ean-13 barcode. \n This has a very low probability of happening but means that you might want to switch your barcode format for something that can accept numbers over 9999999999."
}

View File

@@ -0,0 +1,45 @@
import { IsBoolean, IsInt, IsOptional } from 'class-validator';
import { getConnection } from 'typeorm';
import { RunnerNotFoundError } from '../../errors/RunnerErrors';
import { Runner } from '../entities/Runner';
import { RunnerCard } from '../entities/RunnerCard';
/**
* This classed is used to create a new RunnerCard entity from a json body (post request).
*/
export class CreateRunnerCard {
/**
* The card's associated runner.
*/
@IsInt()
@IsOptional()
runner?: number;
/**
* Is the new card enabled (for fraud reasons)?
* Default: true
*/
@IsBoolean()
enabled: boolean = true;
/**
* Creates a new RunnerCard entity from this.
*/
public async toEntity(): Promise<RunnerCard> {
let newCard: RunnerCard = new RunnerCard();
newCard.enabled = this.enabled;
newCard.runner = await this.getRunner();
return newCard;
}
public async getRunner(): Promise<Runner> {
if (!this.runner) { return null; }
const runner = await getConnection().getRepository(Runner).findOne({ id: this.runner });
if (!runner) {
throw new RunnerNotFoundError();
}
return runner;
}
}

View File

@@ -1,35 +1,30 @@
import { IsNotEmpty } from 'class-validator';
import { IsInt, IsPositive } from 'class-validator';
import { getConnection } from 'typeorm';
import { RunnerCardNotFoundError } from '../../errors/RunnerCardErrors';
import { RunnerNotFoundError } from '../../errors/RunnerErrors';
import { ScanStationNotFoundError } from '../../errors/ScanStationErrors';
import { RunnerCard } from '../entities/RunnerCard';
import { ScanStation } from '../entities/ScanStation';
import { TrackScan } from '../entities/TrackScan';
import { CreateScan } from './CreateScan';
/**
* This classed is used to create a new Scan entity from a json body (post request).
*/
export class CreateTrackScan extends CreateScan {
/**
* The scan's associated track.
* This is used to determine the scan's distance.
*/
@IsNotEmpty()
track: number;
export class CreateTrackScan {
/**
* The runnerCard associated with the scan.
* This get's saved for documentation and management purposes.
*/
@IsNotEmpty()
@IsInt()
@IsPositive()
card: number;
/**
* The scanning station that created the scan.
* Mainly used for logging and traceing back scans (or errors)
*/
@IsNotEmpty()
@IsInt()
@IsPositive()
station: number;
/**
@@ -48,7 +43,7 @@ export class CreateTrackScan extends CreateScan {
throw new RunnerNotFoundError();
}
newScan.timestamp = new Date(Date.now()).toString();
newScan.timestamp = Math.round(new Date().getTime() / 1000);
newScan.valid = await this.validateScan(newScan);
return newScan;
@@ -57,25 +52,25 @@ export class CreateTrackScan extends CreateScan {
public async getCard(): Promise<RunnerCard> {
const track = await getConnection().getRepository(RunnerCard).findOne({ id: this.card }, { relations: ["runner"] });
if (!track) {
throw new Error();
throw new RunnerCardNotFoundError();
}
return track;
}
public async getStation(): Promise<ScanStation> {
const track = await getConnection().getRepository(ScanStation).findOne({ id: this.card }, { relations: ["track"] });
if (!track) {
throw new Error();
const station = await getConnection().getRepository(ScanStation).findOne({ id: this.station }, { relations: ["track"] });
if (!station) {
throw new ScanStationNotFoundError();
}
return track;
return station;
}
public async validateScan(scan: TrackScan): Promise<boolean> {
const scans = await getConnection().getRepository(TrackScan).find({ where: { runner: scan.runner }, relations: ["track"] });
const scans = await getConnection().getRepository(TrackScan).find({ where: { runner: scan.runner, valid: true }, relations: ["track"] });
if (scans.length == 0) { return true; }
const newestScan = scans[0];
if ((new Date(scan.timestamp).getTime() - new Date(newestScan.timestamp).getTime()) > scan.track.minimumLapTime) {
const newestScan = scans[scans.length - 1];
if ((scan.timestamp - newestScan.timestamp) > scan.track.minimumLapTime) {
return true;
}

View File

@@ -0,0 +1,51 @@
import { IsBoolean, IsInt, IsOptional, IsPositive } from 'class-validator';
import { getConnection } from 'typeorm';
import { RunnerNotFoundError } from '../../errors/RunnerErrors';
import { Runner } from '../entities/Runner';
import { RunnerCard } from '../entities/RunnerCard';
/**
* This class is used to update a RunnerCard entity (via put request).
*/
export class UpdateRunnerCard {
/**
* The updated card's id.
* This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to).
*/
@IsInt()
@IsPositive()
id?: number;
/**
* The updated card's associated runner.
*/
@IsInt()
@IsOptional()
runner?: number;
/**
* Is the updated card enabled (for fraud reasons)?
* Default: true
*/
@IsBoolean()
enabled: boolean = true;
/**
* Creates a new RunnerCard entity from this.
*/
public async update(card: RunnerCard): Promise<RunnerCard> {
card.enabled = this.enabled;
card.runner = await this.getRunner();
return card;
}
public async getRunner(): Promise<Runner> {
if (!this.runner) { return null; }
const runner = await getConnection().getRepository(Runner).findOne({ id: this.runner });
if (!runner) {
throw new RunnerNotFoundError();
}
return runner;
}
}

View File

@@ -0,0 +1,81 @@
import { IsBoolean, IsInt, IsOptional } from 'class-validator';
import { getConnection } from 'typeorm';
import { RunnerNotFoundError } from '../../errors/RunnerErrors';
import { ScanStationNotFoundError } from '../../errors/ScanStationErrors';
import { Runner } from '../entities/Runner';
import { ScanStation } from '../entities/ScanStation';
import { TrackScan } from '../entities/TrackScan';
/**
* This class is used to update a TrackScan entity (via put request)
*/
export abstract class UpdateTrackScan {
/**
* The updated scan's id.
* This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to).
*/
@IsInt()
id: number;
/**
* The updated scan's associated runner.
* This is important to link ran distances to runners.
*/
@IsInt()
@IsOptional()
runner?: number;
/**
* Is the updated scan valid (for fraud reasons).
*/
@IsBoolean()
@IsOptional()
valid?: boolean = true;
/**
* The updated scan's associated station.
* This is important to link ran distances to runners.
*/
@IsInt()
@IsOptional()
public station?: number;
/**
* Update a TrackScan entity based on this.
* @param scan The scan that shall be updated.
*/
public async updateScan(scan: TrackScan): Promise<TrackScan> {
scan.valid = this.valid;
if (this.runner) {
scan.runner = await this.getRunner();
}
if (this.station) {
scan.station = await this.getStation();
}
scan.track = scan.station.track;
return scan;
}
/**
* Gets a runner based on the runner id provided via this.runner.
*/
public async getRunner(): Promise<Runner> {
const runner = await getConnection().getRepository(Runner).findOne({ id: this.runner });
if (!runner) {
throw new RunnerNotFoundError();
}
return runner;
}
/**
* Gets a runner based on the runner id provided via this.runner.
*/
public async getStation(): Promise<ScanStation> {
const station = await getConnection().getRepository(ScanStation).findOne({ id: this.station });
if (!station) {
throw new ScanStationNotFoundError();
}
return station;
}
}

View File

@@ -1,12 +1,13 @@
import {
IsBoolean,
IsEAN,
IsInt,
IsNotEmpty,
IsOptional,
IsString
IsOptional
} from "class-validator";
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { RunnerCardIdOutOfRangeError } from '../../errors/RunnerCardErrors';
import { ResponseRunnerCard } from '../responses/ResponseRunnerCard';
import { Runner } from "./Runner";
import { TrackScan } from "./TrackScan";
@@ -32,17 +33,6 @@ export class RunnerCard {
@ManyToOne(() => Runner, runner => runner.cards, { nullable: true })
runner: Runner;
/**
* The card's code.
* This has to be able to being converted to something barcode compatible.
* Will get automaticlly generated (not implemented yet).
*/
@Column()
@IsEAN()
@IsString()
@IsNotEmpty()
code: string;
/**
* Is the card enabled (for fraud reasons)?
* Default: true
@@ -58,10 +48,37 @@ export class RunnerCard {
@OneToMany(() => TrackScan, scan => scan.track, { nullable: true })
scans: TrackScan[];
/**
* Generates a ean-13 compliant string for barcode generation.
*/
public get code(): string {
const multiply = [1, 3];
let total = 0;
this.paddedId.split('').forEach((letter, index) => {
total += parseInt(letter, 10) * multiply[index % 2];
});
const checkSum = (Math.ceil(total / 10) * 10) - total;
return this.paddedId + checkSum.toString();
}
/**
* Returns this card's id as a string padded to the length of 12 characters with leading zeros.
*/
private get paddedId(): string {
let id: string = this.id.toString();
if (id.length > 12) {
throw new RunnerCardIdOutOfRangeError();
}
while (id.length < 12) { id = '0' + id; }
return id;
}
/**
* Turns this entity into it's response class.
*/
public toResponse() {
return new Error("NotImplemented");
return new ResponseRunnerCard(this);
}
}

View File

@@ -1,5 +1,4 @@
import {
IsDateString,
IsInt,
IsNotEmpty,
@@ -57,9 +56,8 @@ export class TrackScan extends Scan {
* Will be used to implement fraud detection.
*/
@Column()
@IsDateString()
@IsNotEmpty()
timestamp: string;
@IsInt()
timestamp: number;
/**
* Turns this entity into it's response class.

View File

@@ -12,5 +12,6 @@ export enum PermissionTarget {
STATSCLIENT = 'STATSCLIENT',
DONOR = 'DONOR',
SCAN = 'SCAN',
STATION = 'STATION'
STATION = 'STATION',
CARD = 'CARD'
}

View File

@@ -0,0 +1,53 @@
import { IsBoolean, IsEAN, IsInt, IsNotEmpty, IsObject, IsString } from "class-validator";
import { RunnerCard } from '../entities/RunnerCard';
import { ResponseRunner } from './ResponseRunner';
/**
* Defines the runner card response.
*/
export class ResponseRunnerCard {
/**
* The card's id.
*/
@IsInt()
id: number;;
/**
* The card's associated runner.
* This is important to link scans to runners.
*/
@IsObject()
runner: ResponseRunner | null;
/**
* The card's code.
*/
@IsEAN()
@IsString()
@IsNotEmpty()
code: string;
/**
* Is the enabled valid (for fraud reasons).
* The determination of validity will work differently for every child class.
*/
@IsBoolean()
enabled: boolean = true;
/**
* Creates a ResponseRunnerCard object from a runner card.
* @param card The card the response shall be build for.
*/
public constructor(card: RunnerCard) {
this.id = card.id;
if (!card.runner) { this.runner = null }
else { this.runner = card.runner.toResponse(); }
try {
this.code = card.code;
} catch (error) {
this.code = "0000000000000"
}
this.enabled = card.enabled;
}
}

View File

@@ -1,8 +1,8 @@
import { IsDateString, IsNotEmpty } from "class-validator";
import { RunnerCard } from '../entities/RunnerCard';
import { ScanStation } from '../entities/ScanStation';
import { TrackScan } from '../entities/TrackScan';
import { ResponseRunnerCard } from './ResponseRunnerCard';
import { ResponseScan } from './ResponseScan';
import { ResponseScanStation } from './ResponseScanStation';
import { ResponseTrack } from './ResponseTrack';
/**
@@ -19,20 +19,20 @@ export class ResponseTrackScan extends ResponseScan {
* The runnerCard associated with the scan.
*/
@IsNotEmpty()
card: RunnerCard;
card: ResponseRunnerCard;
/**
* The scanning station that created the scan.
*/
@IsNotEmpty()
station: ScanStation;
station: ResponseScanStation;
/**
* The scan's creation timestamp.
*/
@IsDateString()
@IsNotEmpty()
timestamp: string;
timestamp: number;
/**
* Creates a ResponseTrackScan object from a scan.
@@ -41,8 +41,9 @@ export class ResponseTrackScan extends ResponseScan {
public constructor(scan: TrackScan) {
super(scan);
this.track = new ResponseTrack(scan.track);
this.card = scan.card;
this.station = scan.station;
this.card = scan.card.toResponse();
this.station = scan.station.toResponse();
this.timestamp = scan.timestamp;
this.distance = scan.distance;
}
}

View File

@@ -0,0 +1,144 @@
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
let axios_config;
beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
describe('POST /api/cards illegally', () => {
it('non-existant runner input should return 404', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": 999999999999999999999999
}, axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('POST /api/cards successfully (without runner)', () => {
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', null, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": true
});
});
it('creating a disabled card should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"enabled": false
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": false
});
});
it('creating a enabled card should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"enabled": true
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": true
});
});
});
// ---------------
describe('POST /api/cards successfully (with runner)', () => {
let added_org;
let added_runner;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
delete res2.data.group;
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true
});
});
it('creating a card with runner (no optional params) should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true
});
});
it('creating a enabled card with runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id,
"enabled": true
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true
});
});
it('creating a disabled card with runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id,
"enabled": false
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": false
});
});
});

View File

@@ -0,0 +1,45 @@
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
let axios_config;
beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
// ---------------
describe('DELETE card', () => {
let added_card;
it('creating card without runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', null, axios_config);
added_card = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('delete card', async () => {
const res2 = await axios.delete(base + '/api/cards/' + added_card.id, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
expect(res2.data).toEqual(added_card);
});
it('check if card really was deleted', async () => {
const res3 = await axios.get(base + '/api/cards/' + added_card.id, axios_config);
expect(res3.status).toEqual(404);
expect(res3.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('DELETE card (non-existant)', () => {
it('delete', async () => {
const res2 = await axios.delete(base + '/api/cards/0', axios_config);
expect(res2.status).toEqual(204);
});
});

View File

@@ -0,0 +1,46 @@
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
let axios_config;
beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
describe('GET /api/cards sucessfully', () => {
it('basic get should return 200', async () => {
const res = await axios.get(base + '/api/cards', axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('GET /api/cards illegally', () => {
it('get for non-existant track should return 404', async () => {
const res = await axios.get(base + '/api/cards/-1', axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('adding + getting cards (no runner)', () => {
let added_card;
it('correct distance and runner input should return 200', async () => {
const res = await axios.post(base + '/api/cards', null, axios_config);
added_card = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('check if scans was added (no parameter validation)', async () => {
const res = await axios.get(base + '/api/cards/' + added_card.id, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
});

View File

@@ -0,0 +1,163 @@
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
let axios_config;
beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
describe('adding + updating illegally', () => {
let added_card;
it('creating card without runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', null, axios_config);
added_card = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('updating empty should return 400', async () => {
const res2 = await axios.put(base + '/api/cards/' + added_card.id, null, axios_config);
expect(res2.status).toEqual(400);
expect(res2.headers['content-type']).toContain("application/json")
});
it('updating with wrong id should return 406', async () => {
const res2 = await axios.put(base + '/api/cards/' + added_card.id, {
"id": added_card.id + 1
}, axios_config);
expect(res2.status).toEqual(406);
expect(res2.headers['content-type']).toContain("application/json")
});
it('update with invalid runner id should return 404', async () => {
const res2 = await axios.put(base + '/api/cards/' + added_card.id, {
"id": added_card.id,
"runner": 9999999999999999999999999
}, axios_config);
expect(res2.status).toEqual(404);
expect(res2.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('adding + updating card.runner successfully', () => {
let added_org;
let added_runner;
let added_runner2;
let added_card;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
delete res2.data.group;
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
delete res2.data.group;
added_runner2 = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating card without runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', null, axios_config);
added_card = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('valid runner update (add runner) should return 200', async () => {
const res2 = await axios.put(base + '/api/cards/' + added_card.id, {
"id": added_card.id,
"runner": added_runner.id
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json");
expect(res2.data).toEqual({
"id": added_card.id,
"runner": added_runner,
"enabled": true,
"code": added_card.code
});
});
it('valid runner update (change runner) should return 200', async () => {
const res2 = await axios.put(base + '/api/cards/' + added_card.id, {
"id": added_card.id,
"runner": added_runner2.id
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json");
expect(res2.data).toEqual({
"id": added_card.id,
"runner": added_runner2,
"enabled": true,
"code": added_card.code
});
});
});
// ---------------
describe('adding + updating other values successfully', () => {
let added_card;
it('creating card without runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', null, axios_config);
added_card = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('valid update changeing nothing should return 200', async () => {
const res2 = await axios.put(base + '/api/cards/' + added_card.id, {
"id": added_card.id
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json");
expect(res2.data).toEqual(added_card);
});
it('valid disable update should return 200', async () => {
const res2 = await axios.put(base + '/api/cards/' + added_card.id, {
"id": added_card.id,
"enabled": false
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json");
expect(res2.data).toEqual({
"id": added_card.id,
"runner": null,
"enabled": false,
"code": added_card.code
});
});
it('valid enable update should return 200', async () => {
const res2 = await axios.put(base + '/api/cards/' + added_card.id, {
"id": added_card.id,
"enabled": true
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json");
expect(res2.data).toEqual({
"id": added_card.id,
"runner": null,
"enabled": true,
"code": added_card.code
});
});
});

View File

@@ -84,6 +84,7 @@ describe('POST /api/scans successfully', () => {
"group": added_org.id
}, axios_config);
delete res2.data.group;
delete res2.data.distance;
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
@@ -96,6 +97,7 @@ describe('POST /api/scans successfully', () => {
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.runner.distance;
expect(res.data).toEqual({
"runner": added_runner,
"distance": 200,
@@ -111,6 +113,7 @@ describe('POST /api/scans successfully', () => {
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.runner.distance;
expect(res.data).toEqual({
"runner": added_runner,
"distance": 200,
@@ -126,6 +129,7 @@ describe('POST /api/scans successfully', () => {
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.runner.distance;
expect(res.data).toEqual({
"runner": added_runner,
"distance": 200,
@@ -154,6 +158,7 @@ describe('POST /api/scans successfully via scan station', () => {
"group": added_org.id
}, axios_config);
delete res2.data.group;
delete res2.data.distance;
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
@@ -186,6 +191,7 @@ describe('POST /api/scans successfully via scan station', () => {
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.runner.distance;
expect(res.data).toEqual({
"runner": added_runner,
"distance": 200,
@@ -204,6 +210,7 @@ describe('POST /api/scans successfully via scan station', () => {
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.runner.distance;
expect(res.data).toEqual({
"runner": added_runner,
"distance": 200,
@@ -222,6 +229,7 @@ describe('POST /api/scans successfully via scan station', () => {
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.runner.distance;
expect(res.data).toEqual({
"runner": added_runner,
"distance": 200,

View File

@@ -44,6 +44,7 @@ describe('DELETE scan', () => {
"distance": 1000
}, axios_config);
added_scan = res.data;
delete res.data.runner.distance;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
@@ -51,6 +52,7 @@ describe('DELETE scan', () => {
const res2 = await axios.delete(base + '/api/scans/' + added_scan.id, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
delete res2.data.runner.distance;
expect(res2.data).toEqual(added_scan);
});
it('check if scan really was deleted', async () => {

View File

@@ -100,6 +100,7 @@ describe('adding + updating successfilly', () => {
"group": added_org.id
}, axios_config);
delete res2.data.group;
delete res2.data.distance;
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
@@ -121,6 +122,7 @@ describe('adding + updating successfilly', () => {
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
delete res2.data.runner.distance;
expect(res2.data).toEqual({
"id": added_scan.id,
"runner": added_runner,
@@ -137,7 +139,8 @@ describe('adding + updating successfilly', () => {
"valid": false
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
expect(res2.headers['content-type']).toContain("application/json");
delete res2.data.runner.distance;
expect(res2.data).toEqual({
"id": added_scan.id,
"runner": added_runner,
@@ -152,6 +155,7 @@ describe('adding + updating successfilly', () => {
"group": added_org.id
}, axios_config);
delete res2.data.group;
delete res2.data.distance;
added_runner2 = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
@@ -164,6 +168,7 @@ describe('adding + updating successfilly', () => {
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json");
delete res2.data.runner.distance;
expect(res2.data).toEqual({
"id": added_scan.id,
"runner": added_runner2,

View File

@@ -0,0 +1,249 @@
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
let axios_config;
beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
describe('POST /api/scans illegally', () => {
let added_org;
let added_runner;
let added_card;
let added_track;
let added_station;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
added_card = res.data;
});
it('creating a track should return 200', async () => {
const res1 = await axios.post(base + '/api/tracks', {
"name": "test123",
"distance": 123
}, axios_config);
added_track = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('correct description and track input for station creation return 200', async () => {
const res = await axios.post(base + '/api/stations', {
"track": added_track.id,
"description": "I am but a simple test."
}, axios_config);
added_station = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('no input should return 400', async () => {
const res = await axios.post(base + '/api/scans/trackscans', null, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json")
});
it('no station should return 400', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json")
});
it('no card should return 400', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"station": added_station.id,
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json")
});
it('invalid card input should return 404', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": 999999999999999999999999,
"station": added_station.id
}, axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json")
});
it('invalid station input should return 404', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id,
"station": 999999999999999999999999
}, axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('POST /api/scans successfully', () => {
let added_org;
let added_runner;
let added_card;
let added_track;
let added_station;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
added_card = res.data;
});
it('creating a track should return 200', async () => {
const res1 = await axios.post(base + '/api/tracks', {
"name": "test123",
"distance": 123,
"minimumLapTime": 30
}, axios_config);
added_track = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('correct description and track input for station creation return 200', async () => {
const res = await axios.post(base + '/api/stations', {
"track": added_track.id,
"description": "I am but a simple test."
}, axios_config);
added_station = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('creating a scan should return 200', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id,
"station": added_station.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('creating a invalid scan should return 200', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id,
"station": added_station.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
expect(res.data.valid).toEqual(false);
});
});
// ---------------
describe('POST /api/scans successfully via scan station', () => {
let added_org;
let added_runner;
let added_card;
let added_track;
let added_station;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
added_card = res.data;
});
it('creating a track should return 200', async () => {
const res1 = await axios.post(base + '/api/tracks', {
"name": "test123",
"distance": 123,
"minimumLapTime": 30
}, axios_config);
added_track = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('correct description and track input for station creation return 200', async () => {
const res = await axios.post(base + '/api/stations', {
"track": added_track.id,
"description": "I am but a simple test."
}, axios_config);
added_station = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('creating a scan should return 200', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id,
"station": added_station.id
}, {
headers: { "authorization": "Bearer " + added_station.key },
validateStatus: undefined
});
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('creating a invalid scan should return 200', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id,
"station": added_station.id
}, {
headers: { "authorization": "Bearer " + added_station.key },
validateStatus: undefined
});
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
expect(res.data.valid).toEqual(false);
});
});

View File

@@ -0,0 +1,101 @@
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
let axios_config;
beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
// ---------------
describe('DELETE scan', () => {
let added_org;
let added_runner;
let added_card;
let added_track;
let added_station;
let added_scan;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
added_card = res.data;
});
it('creating a track should return 200', async () => {
const res1 = await axios.post(base + '/api/tracks', {
"name": "test123",
"distance": 123,
"minimumLapTime": 30
}, axios_config);
added_track = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('correct description and track input for station creation return 200', async () => {
const res = await axios.post(base + '/api/stations', {
"track": added_track.id,
"description": "I am but a simple test."
}, axios_config);
added_station = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('creating a scan should return 200', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id,
"station": added_station.id
}, {
headers: { "authorization": "Bearer " + added_station.key },
validateStatus: undefined
});
added_scan = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('delete scan', async () => {
const res2 = await axios.delete(base + '/api/scans/' + added_scan.id, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
expect(res2.data).toEqual(added_scan);
});
it('check if scan really was deleted', async () => {
const res3 = await axios.get(base + '/api/scans/' + added_scan.id, axios_config);
expect(res3.status).toEqual(404);
expect(res3.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('DELETE scan (non-existant)', () => {
it('delete', async () => {
const res2 = await axios.delete(base + '/api/scans/0', axios_config);
expect(res2.status).toEqual(204);
});
});

View File

@@ -0,0 +1,102 @@
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
let axios_config;
beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
describe('GET /api/scans sucessfully', () => {
it('basic get should return 200', async () => {
const res = await axios.get(base + '/api/scans', axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('GET /api/scans illegally', () => {
it('get for non-existant track should return 404', async () => {
const res = await axios.get(base + '/api/scans/-1', axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('adding + getting scans', () => {
let added_org;
let added_runner;
let added_card;
let added_track;
let added_station;
let added_scan;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
added_card = res.data;
});
it('creating a track should return 200', async () => {
const res1 = await axios.post(base + '/api/tracks', {
"name": "test123",
"distance": 123,
"minimumLapTime": 30
}, axios_config);
added_track = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('correct description and track input for station creation return 200', async () => {
const res = await axios.post(base + '/api/stations', {
"track": added_track.id,
"description": "I am but a simple test."
}, axios_config);
added_station = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('creating a scan should return 200', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id,
"station": added_station.id
}, {
headers: { "authorization": "Bearer " + added_station.key },
validateStatus: undefined
});
added_scan = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('check if scans was added (no parameter validation)', async () => {
const res = await axios.get(base + '/api/scans/' + added_scan.id, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
});

View File

@@ -0,0 +1,239 @@
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
let axios_config;
beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
describe('adding + updating illegally', () => {
let added_org;
let added_runner;
let added_card;
let added_track;
let added_station;
let added_scan;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
added_card = res.data;
});
it('creating a track should return 200', async () => {
const res1 = await axios.post(base + '/api/tracks', {
"name": "test123",
"distance": 123,
"minimumLapTime": 30
}, axios_config);
added_track = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('correct description and track input for station creation return 200', async () => {
const res = await axios.post(base + '/api/stations', {
"track": added_track.id,
"description": "I am but a simple test."
}, axios_config);
added_station = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('creating a scan should return 200', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id,
"station": added_station.id
}, {
headers: { "authorization": "Bearer " + added_station.key },
validateStatus: undefined
});
added_scan = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('updating empty should return 400', async () => {
const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, null, axios_config);
expect(res2.status).toEqual(400);
expect(res2.headers['content-type']).toContain("application/json")
});
it('updating with wrong id should return 406', async () => {
const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, {
"id": added_scan.id + 1,
"station": added_station.id,
"runner": added_runner.id
}, axios_config);
expect(res2.status).toEqual(406);
expect(res2.headers['content-type']).toContain("application/json")
});
it('updating with invalid station should return 404', async () => {
const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, {
"id": added_scan.id,
"station": 9999999999999999,
"runner": added_runner.id
}, axios_config);
expect(res2.status).toEqual(404);
expect(res2.headers['content-type']).toContain("application/json")
});
it('updating with invalid runner should return 404', async () => {
const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, {
"id": added_scan.id,
"station": added_station.id,
"runner": 9999999999999999999
}, axios_config);
expect(res2.status).toEqual(404);
expect(res2.headers['content-type']).toContain("application/json")
});
});
//---------------
describe('adding + updating successfilly', () => {
let added_org;
let added_runner;
let added_runner2;
let added_card;
let added_track;
let added_track2;
let added_station;
let added_station2;
let added_scan;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
added_runner2 = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
added_card = res.data;
});
it('creating a track should return 200', async () => {
const res1 = await axios.post(base + '/api/tracks', {
"name": "test123",
"distance": 123,
"minimumLapTime": 30
}, axios_config);
added_track = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a track should return 200', async () => {
const res1 = await axios.post(base + '/api/tracks', {
"name": "test123",
"distance": 123,
"minimumLapTime": 30
}, axios_config);
added_track2 = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('correct description and track input for station creation return 200', async () => {
const res = await axios.post(base + '/api/stations', {
"track": added_track.id,
"description": "I am but a simple test."
}, axios_config);
added_station = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('correct description and track input for station creation return 200', async () => {
const res = await axios.post(base + '/api/stations', {
"track": added_track.id,
"description": "I am but a simple test."
}, axios_config);
added_station2 = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
});
it('creating a scan should return 200', async () => {
const res = await axios.post(base + '/api/scans/trackscans', {
"card": added_card.id,
"station": added_station.id
}, {
headers: { "authorization": "Bearer " + added_station.key },
validateStatus: undefined
});
added_scan = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('updating with new runner should return 200', async () => {
const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, {
"id": added_scan.id,
"station": added_station.id,
"runner": added_runner2.id
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('updating with new station should return 200', async () => {
const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, {
"id": added_scan.id,
"station": added_station2.id,
"runner": added_runner.id
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('updating with valid=false should return 200', async () => {
const res2 = await axios.put(base + '/api/scans/trackscans/' + added_scan.id, {
"id": added_scan.id,
"station": added_station2.id,
"runner": added_runner.id,
"valid": false
}, axios_config);
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
});