diff --git a/src/controllers/RunnerCardController.ts b/src/controllers/RunnerCardController.ts index 2263f07..1366bed 100644 --- a/src/controllers/RunnerCardController.ts +++ b/src/controllers/RunnerCardController.ts @@ -97,7 +97,7 @@ export class RunnerCardController { } const scanController = new ScanController; for (let scan of cardScans) { - scanController.remove(scan.id, force); + await scanController.remove(scan.id, force); } await this.cardRepository.delete(card); diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 9eeb916..4e74330 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -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.
This includes the runner\'s group and distance ran.' }) async getAll() { let responseRunners: ResponseRunner[] = new Array(); - 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.
If no runner with this id exists it will just return 204(no content).' }) + @OpenAPI({ description: 'Delete the runner whose id you provided.
This will also delete all scans and cards associated with the runner.
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); } diff --git a/src/controllers/ScanController.ts b/src/controllers/ScanController.ts index ed7df91..46c7ba1 100644 --- a/src/controllers/ScanController.ts +++ b/src/controllers/ScanController.ts @@ -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; + private trackScanRepository: Repository; /** * 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.
This includes the scan\'s runner\'s distance ran.' }) async getAll() { let responseScans: ResponseScan[] = new Array(); - 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.
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).
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.
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).
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.
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.
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.
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(); diff --git a/src/controllers/ScanStationController.ts b/src/controllers/ScanStationController.ts index 4e32971..df85d0e 100644 --- a/src/controllers/ScanStationController.ts +++ b/src/controllers/ScanStationController.ts @@ -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"] }); diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index 094756c..42feb7c 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -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); diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts index 4b484fc..160a99e 100644 --- a/src/controllers/UserGroupController.ts +++ b/src/controllers/UserGroupController.ts @@ -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); diff --git a/src/models/actions/CreateTrackScan.ts b/src/models/actions/CreateTrackScan.ts index 2303352..51078c4 100644 --- a/src/models/actions/CreateTrackScan.ts +++ b/src/models/actions/CreateTrackScan.ts @@ -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 { 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 { - 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 { - 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; } diff --git a/src/models/actions/UpdateTrackScan.ts b/src/models/actions/UpdateTrackScan.ts new file mode 100644 index 0000000..43c37cd --- /dev/null +++ b/src/models/actions/UpdateTrackScan.ts @@ -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 { + 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 { + 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 { + const station = await getConnection().getRepository(ScanStation).findOne({ id: this.station }); + if (!station) { + throw new ScanStationNotFoundError(); + } + return station; + } +} \ No newline at end of file diff --git a/src/models/entities/TrackScan.ts b/src/models/entities/TrackScan.ts index 8c4143b..cd8049a 100644 --- a/src/models/entities/TrackScan.ts +++ b/src/models/entities/TrackScan.ts @@ -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. diff --git a/src/models/responses/ResponseTrackScan.ts b/src/models/responses/ResponseTrackScan.ts index 724a8fd..d52b4f7 100644 --- a/src/models/responses/ResponseTrackScan.ts +++ b/src/models/responses/ResponseTrackScan.ts @@ -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; } } diff --git a/src/tests/scans/scans_add.spec.ts b/src/tests/scans/scans_add.spec.ts index c08ee01..580ecd8 100644 --- a/src/tests/scans/scans_add.spec.ts +++ b/src/tests/scans/scans_add.spec.ts @@ -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, diff --git a/src/tests/scans/scans_delete.spec.ts b/src/tests/scans/scans_delete.spec.ts index e88e812..0ff0b72 100644 --- a/src/tests/scans/scans_delete.spec.ts +++ b/src/tests/scans/scans_delete.spec.ts @@ -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 () => { diff --git a/src/tests/scans/scans_update.spec.ts b/src/tests/scans/scans_update.spec.ts index b4ff86c..60b7fb1 100644 --- a/src/tests/scans/scans_update.spec.ts +++ b/src/tests/scans/scans_update.spec.ts @@ -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, diff --git a/src/tests/trackscans/trackscans_add.spec.ts b/src/tests/trackscans/trackscans_add.spec.ts new file mode 100644 index 0000000..a4b475d --- /dev/null +++ b/src/tests/trackscans/trackscans_add.spec.ts @@ -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); + }); +}); \ No newline at end of file diff --git a/src/tests/trackscans/trackscans_delete.spec.ts b/src/tests/trackscans/trackscans_delete.spec.ts new file mode 100644 index 0000000..646c189 --- /dev/null +++ b/src/tests/trackscans/trackscans_delete.spec.ts @@ -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); + }); +}); diff --git a/src/tests/trackscans/trackscans_get.spec.ts b/src/tests/trackscans/trackscans_get.spec.ts new file mode 100644 index 0000000..1f0eaed --- /dev/null +++ b/src/tests/trackscans/trackscans_get.spec.ts @@ -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"); + }); +}); \ No newline at end of file diff --git a/src/tests/trackscans/trackscans_update.spec.ts b/src/tests/trackscans/trackscans_update.spec.ts new file mode 100644 index 0000000..131f3b4 --- /dev/null +++ b/src/tests/trackscans/trackscans_update.spec.ts @@ -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") + }); +});