Merge branch 'dev' into feature/93-user_endpoints
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/pr Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/pr Build is passing
				
			This commit is contained in:
		
							
								
								
									
										21
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								.drone.yml
									
									
									
									
									
								
							| @@ -1,24 +1,3 @@ | ||||
| --- | ||||
| kind: pipeline | ||||
| name: tests:14.15.1-alpine3.12 | ||||
| clone: | ||||
|   disable: true | ||||
| steps: | ||||
|   - name: checkout pr | ||||
|     image: alpine/git | ||||
|     commands: | ||||
|       - git clone $DRONE_REMOTE_URL . | ||||
|       - git checkout $DRONE_SOURCE_BRANCH | ||||
|       - mv .env.ci .env | ||||
|   - name: run tests | ||||
|     image: node:14.15.1-alpine3.12 | ||||
|     commands: | ||||
|       - yarn | ||||
|       - yarn test:ci | ||||
| trigger: | ||||
|   event: | ||||
|     - pull_request | ||||
|  | ||||
| --- | ||||
| kind: pipeline | ||||
| name: tests:node_latest | ||||
|   | ||||
							
								
								
									
										145
									
								
								src/controllers/DonationController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/controllers/DonationController.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| 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 { DonationIdsNotMatchingError, DonationNotFoundError } from '../errors/DonationErrors'; | ||||
| import { DonorNotFoundError } from '../errors/DonorErrors'; | ||||
| import { RunnerNotFoundError } from '../errors/RunnerErrors'; | ||||
| import { CreateDistanceDonation } from '../models/actions/create/CreateDistanceDonation'; | ||||
| import { CreateFixedDonation } from '../models/actions/create/CreateFixedDonation'; | ||||
| import { UpdateDistanceDonation } from '../models/actions/update/UpdateDistanceDonation'; | ||||
| import { UpdateFixedDonation } from '../models/actions/update/UpdateFixedDonation'; | ||||
| import { DistanceDonation } from '../models/entities/DistanceDonation'; | ||||
| import { Donation } from '../models/entities/Donation'; | ||||
| import { FixedDonation } from '../models/entities/FixedDonation'; | ||||
| import { ResponseDistanceDonation } from '../models/responses/ResponseDistanceDonation'; | ||||
| import { ResponseDonation } from '../models/responses/ResponseDonation'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
|  | ||||
| @JsonController('/donations') | ||||
| @OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) | ||||
| export class DonationController { | ||||
| 	private donationRepository: Repository<Donation>; | ||||
| 	private distanceDonationRepository: Repository<DistanceDonation>; | ||||
| 	private fixedDonationRepository: Repository<FixedDonation>; | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the repository of this controller's model/entity. | ||||
| 	 */ | ||||
| 	constructor() { | ||||
| 		this.donationRepository = getConnectionManager().get().getRepository(Donation); | ||||
| 		this.distanceDonationRepository = getConnectionManager().get().getRepository(DistanceDonation); | ||||
| 		this.fixedDonationRepository = getConnectionManager().get().getRepository(FixedDonation); | ||||
| 	} | ||||
|  | ||||
| 	@Get() | ||||
| 	@Authorized("DONATION:GET") | ||||
| 	@ResponseSchema(ResponseDonation, { isArray: true }) | ||||
| 	@ResponseSchema(ResponseDistanceDonation, { isArray: true }) | ||||
| 	@OpenAPI({ description: 'Lists all donations (fixed or distance based) from all donors. <br> This includes the donations\'s runner\'s distance ran(if distance donation).' }) | ||||
| 	async getAll() { | ||||
| 		let responseDonations: ResponseDonation[] = new Array<ResponseDonation>(); | ||||
| 		const donations = await this.donationRepository.find({ relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] }); | ||||
| 		donations.forEach(donation => { | ||||
| 			responseDonations.push(donation.toResponse()); | ||||
| 		}); | ||||
| 		return responseDonations; | ||||
| 	} | ||||
|  | ||||
| 	@Get('/:id') | ||||
| 	@Authorized("DONATION:GET") | ||||
| 	@ResponseSchema(ResponseDonation) | ||||
| 	@ResponseSchema(ResponseDistanceDonation) | ||||
| 	@ResponseSchema(DonationNotFoundError, { statusCode: 404 }) | ||||
| 	@OnUndefined(DonationNotFoundError) | ||||
| 	@OpenAPI({ description: 'Lists all information about the donation whose id got provided. This includes the donation\'s runner\'s distance ran (if distance donation).' }) | ||||
| 	async getOne(@Param('id') id: number) { | ||||
| 		let donation = await this.donationRepository.findOne({ id: id }, { relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] }) | ||||
| 		if (!donation) { throw new DonationNotFoundError(); } | ||||
| 		return donation.toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Post('/fixed') | ||||
| 	@Authorized("DONATION:CREATE") | ||||
| 	@ResponseSchema(ResponseDonation) | ||||
| 	@ResponseSchema(DonorNotFoundError, { statusCode: 404 }) | ||||
| 	@OpenAPI({ description: 'Create a fixed donation (not distance donation - use /donations/distance instead). <br> Please rmemember to provide the donation\'s donors\'s id and amount.' }) | ||||
| 	async postFixed(@Body({ validate: true }) createDonation: CreateFixedDonation) { | ||||
| 		let donation = await createDonation.toEntity(); | ||||
| 		donation = await this.fixedDonationRepository.save(donation); | ||||
| 		return (await this.donationRepository.findOne({ id: donation.id }, { relations: ['donor'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Post('/distance') | ||||
| 	@Authorized("DONATION:CREATE") | ||||
| 	@ResponseSchema(ResponseDistanceDonation) | ||||
| 	@ResponseSchema(DonorNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) | ||||
| 	@OpenAPI({ description: 'Create a distance donation (not fixed donation - use /donations/fixed instead). <br> Please rmemember to provide the donation\'s donors\'s and runner\s ids and amount per distance (kilometer).' }) | ||||
| 	async postDistance(@Body({ validate: true }) createDonation: CreateDistanceDonation) { | ||||
| 		let donation = await createDonation.toEntity(); | ||||
| 		donation = await this.distanceDonationRepository.save(donation); | ||||
| 		return (await this.donationRepository.findOne({ id: donation.id }, { relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Put('/fixed/:id') | ||||
| 	@Authorized("DONATION:UPDATE") | ||||
| 	@ResponseSchema(ResponseDonation) | ||||
| 	@ResponseSchema(DonationNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(DonorNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(DonationIdsNotMatchingError, { statusCode: 406 }) | ||||
| 	@OpenAPI({ description: "Update the fixed donation (not distance donation - use /donations/distance instead) whose id you provided. <br> Please remember that ids can't be changed and amounts must be positive." }) | ||||
| 	async putFixed(@Param('id') id: number, @Body({ validate: true }) donation: UpdateFixedDonation) { | ||||
| 		let oldDonation = await this.fixedDonationRepository.findOne({ id: id }); | ||||
|  | ||||
| 		if (!oldDonation) { | ||||
| 			throw new DonationNotFoundError(); | ||||
| 		} | ||||
|  | ||||
| 		if (oldDonation.id != donation.id) { | ||||
| 			throw new DonationIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.fixedDonationRepository.save(await donation.update(oldDonation)); | ||||
| 		return (await this.donationRepository.findOne({ id: donation.id }, { relations: ['donor'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Put('/distance/:id') | ||||
| 	@Authorized("DONATION:UPDATE") | ||||
| 	@ResponseSchema(ResponseDonation) | ||||
| 	@ResponseSchema(DonationNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(DonorNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(DonationIdsNotMatchingError, { statusCode: 406 }) | ||||
| 	@OpenAPI({ description: "Update the distance donation (not fixed donation - use /donations/fixed instead) whose id you provided. <br> Please remember that ids can't be changed and amountPerDistance must be positive." }) | ||||
| 	async putDistance(@Param('id') id: number, @Body({ validate: true }) donation: UpdateDistanceDonation) { | ||||
| 		let oldDonation = await this.distanceDonationRepository.findOne({ id: id }); | ||||
|  | ||||
| 		if (!oldDonation) { | ||||
| 			throw new DonationNotFoundError(); | ||||
| 		} | ||||
|  | ||||
| 		if (oldDonation.id != donation.id) { | ||||
| 			throw new DonationIdsNotMatchingError(); | ||||
| 		} | ||||
|  | ||||
| 		await this.distanceDonationRepository.save(await donation.update(oldDonation)); | ||||
| 		return (await this.donationRepository.findOne({ id: donation.id }, { relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] })).toResponse(); | ||||
| 	} | ||||
|  | ||||
| 	@Delete('/:id') | ||||
| 	@Authorized("DONATION:DELETE") | ||||
| 	@ResponseSchema(ResponseDonation) | ||||
| 	@ResponseSchema(ResponseDistanceDonation) | ||||
| 	@ResponseSchema(ResponseEmpty, { statusCode: 204 }) | ||||
| 	@OnUndefined(204) | ||||
| 	@OpenAPI({ description: 'Delete the donation whose id you provided. <br> If no donation with this id exists it will just return 204(no content).' }) | ||||
| 	async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { | ||||
| 		let donation = await this.donationRepository.findOne({ id: id }); | ||||
| 		if (!donation) { return null; } | ||||
| 		const responseScan = await this.donationRepository.findOne({ id: donation.id }, { relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] }); | ||||
|  | ||||
| 		await this.donationRepository.delete(donation); | ||||
| 		return responseScan.toResponse(); | ||||
| 	} | ||||
| } | ||||
| @@ -1,12 +1,13 @@ | ||||
| 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 { DonorIdsNotMatchingError, DonorNotFoundError } from '../errors/DonorErrors'; | ||||
| import { DonorHasDonationsError, DonorIdsNotMatchingError, DonorNotFoundError } from '../errors/DonorErrors'; | ||||
| import { CreateDonor } from '../models/actions/create/CreateDonor'; | ||||
| import { UpdateDonor } from '../models/actions/update/UpdateDonor'; | ||||
| import { Donor } from '../models/entities/Donor'; | ||||
| import { ResponseDonor } from '../models/responses/ResponseDonor'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { DonationController } from './DonationController'; | ||||
|  | ||||
| @JsonController('/donors') | ||||
| @OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) | ||||
| @@ -23,10 +24,10 @@ export class DonorController { | ||||
| 	@Get() | ||||
| 	@Authorized("DONOR:GET") | ||||
| 	@ResponseSchema(ResponseDonor, { isArray: true }) | ||||
| 	@OpenAPI({ description: 'Lists all runners from all teams/orgs. <br> This includes the runner\'s group and distance ran.' }) | ||||
| 	@OpenAPI({ description: 'Lists all donor. <br> This includes the donor\'s current donation amount.' }) | ||||
| 	async getAll() { | ||||
| 		let responseDonors: ResponseDonor[] = new Array<ResponseDonor>(); | ||||
| 		const donors = await this.donorRepository.find(); | ||||
| 		const donors = await this.donorRepository.find({ relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }); | ||||
| 		donors.forEach(donor => { | ||||
| 			responseDonors.push(new ResponseDonor(donor)); | ||||
| 		}); | ||||
| @@ -38,9 +39,9 @@ export class DonorController { | ||||
| 	@ResponseSchema(ResponseDonor) | ||||
| 	@ResponseSchema(DonorNotFoundError, { statusCode: 404 }) | ||||
| 	@OnUndefined(DonorNotFoundError) | ||||
| 	@OpenAPI({ description: 'Lists all information about the runner whose id got provided.' }) | ||||
| 	@OpenAPI({ description: 'Lists all information about the donor whose id got provided. <br> This includes the donor\'s current donation amount.' }) | ||||
| 	async getOne(@Param('id') id: number) { | ||||
| 		let donor = await this.donorRepository.findOne({ id: id }) | ||||
| 		let donor = await this.donorRepository.findOne({ id: id }, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }) | ||||
| 		if (!donor) { throw new DonorNotFoundError(); } | ||||
| 		return new ResponseDonor(donor); | ||||
| 	} | ||||
| @@ -48,7 +49,7 @@ export class DonorController { | ||||
| 	@Post() | ||||
| 	@Authorized("DONOR:CREATE") | ||||
| 	@ResponseSchema(ResponseDonor) | ||||
| 	@OpenAPI({ description: 'Create a new runner. <br> Please remeber to provide the runner\'s group\'s id.' }) | ||||
| 	@OpenAPI({ description: 'Create a new donor.' }) | ||||
| 	async post(@Body({ validate: true }) createRunner: CreateDonor) { | ||||
| 		let donor; | ||||
| 		try { | ||||
| @@ -58,7 +59,7 @@ export class DonorController { | ||||
| 		} | ||||
|  | ||||
| 		donor = await this.donorRepository.save(donor) | ||||
| 		return new ResponseDonor(await this.donorRepository.findOne(donor)); | ||||
| 		return new ResponseDonor(await this.donorRepository.findOne(donor, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] })); | ||||
| 	} | ||||
|  | ||||
| 	@Put('/:id') | ||||
| @@ -66,7 +67,7 @@ export class DonorController { | ||||
| 	@ResponseSchema(ResponseDonor) | ||||
| 	@ResponseSchema(DonorNotFoundError, { statusCode: 404 }) | ||||
| 	@ResponseSchema(DonorIdsNotMatchingError, { statusCode: 406 }) | ||||
| 	@OpenAPI({ description: "Update the runner whose id you provided. <br> Please remember that ids can't be changed." }) | ||||
| 	@OpenAPI({ description: "Update the donor whose id you provided. <br> Please remember that ids can't be changed." }) | ||||
| 	async put(@Param('id') id: number, @Body({ validate: true }) donor: UpdateDonor) { | ||||
| 		let oldDonor = await this.donorRepository.findOne({ id: id }); | ||||
|  | ||||
| @@ -79,7 +80,7 @@ export class DonorController { | ||||
| 		} | ||||
|  | ||||
| 		await this.donorRepository.save(await donor.update(oldDonor)); | ||||
| 		return new ResponseDonor(await this.donorRepository.findOne({ id: id })); | ||||
| 		return new ResponseDonor(await this.donorRepository.findOne({ id: id }, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] })); | ||||
| 	} | ||||
|  | ||||
| 	@Delete('/:id') | ||||
| @@ -87,17 +88,24 @@ export class DonorController { | ||||
| 	@ResponseSchema(ResponseDonor) | ||||
| 	@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 donor whose id you provided. <br> If no donor with this id exists it will just return 204(no content). <br> If the donor still has donations associated this will fail, please provide the query param ?force=true to delete the donor with all associated donations.' }) | ||||
| 	async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { | ||||
| 		let donor = await this.donorRepository.findOne({ id: id }); | ||||
| 		if (!donor) { return null; } | ||||
| 		const responseDonor = await this.donorRepository.findOne(donor); | ||||
| 		const responseDonor = await this.donorRepository.findOne(donor, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }); | ||||
|  | ||||
| 		if (!donor) { | ||||
| 			throw new DonorNotFoundError(); | ||||
| 		} | ||||
|  | ||||
| 		//TODO: DELETE DONATIONS AND WARN FOR FORCE (https://git.odit.services/lfk/backend/issues/66) | ||||
| 		const donorDonations = (await this.donorRepository.findOne({ id: donor.id }, { relations: ["donations"] })).donations; | ||||
| 		if (donorDonations.length > 0 && !force) { | ||||
| 			throw new DonorHasDonationsError(); | ||||
| 		} | ||||
| 		const donationController = new DonationController(); | ||||
| 		for (let donation of donorDonations) { | ||||
| 			await donationController.remove(donation.id, force); | ||||
| 		} | ||||
|  | ||||
| 		await this.donorRepository.delete(donor); | ||||
| 		return new ResponseDonor(responseDonor); | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| 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 { RunnerGroupNeededError, RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors'; | ||||
| import { RunnerGroupNeededError, RunnerHasDistanceDonationsError, RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors'; | ||||
| import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; | ||||
| import { CreateRunner } from '../models/actions/create/CreateRunner'; | ||||
| import { UpdateRunner } from '../models/actions/update/UpdateRunner'; | ||||
| import { Runner } from '../models/entities/Runner'; | ||||
| import { ResponseEmpty } from '../models/responses/ResponseEmpty'; | ||||
| import { ResponseRunner } from '../models/responses/ResponseRunner'; | ||||
| import { DonationController } from './DonationController'; | ||||
| import { RunnerCardController } from './RunnerCardController'; | ||||
| import { ScanController } from './ScanController'; | ||||
|  | ||||
| @@ -91,6 +92,7 @@ export class RunnerController { | ||||
| 	@Authorized("RUNNER:DELETE") | ||||
| 	@ResponseSchema(ResponseRunner) | ||||
| 	@ResponseSchema(ResponseEmpty, { statusCode: 204 }) | ||||
| 	@ResponseSchema(RunnerHasDistanceDonationsError, { statusCode: 406 }) | ||||
| 	@OnUndefined(204) | ||||
| 	@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) { | ||||
| @@ -102,10 +104,19 @@ export class RunnerController { | ||||
| 			throw new RunnerNotFoundError(); | ||||
| 		} | ||||
|  | ||||
| 		const runnerDonations = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["distanceDonations"] })).distanceDonations; | ||||
| 		if (runnerDonations.length > 0 && !force) { | ||||
| 			throw new RunnerHasDistanceDonationsError(); | ||||
| 		} | ||||
| 		const donationController = new DonationController(); | ||||
| 		for (let donation of runnerDonations) { | ||||
| 			await donationController.remove(donation.id, force); | ||||
| 		} | ||||
|  | ||||
| 		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); | ||||
| 		for (let card of runnerCards) { | ||||
| 			await cardController.remove(card.id, force); | ||||
| 		} | ||||
|  | ||||
| 		const runnerScans = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["scans"] })).scans; | ||||
|   | ||||
							
								
								
									
										25
									
								
								src/errors/DonationErrors.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/errors/DonationErrors.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| import { IsString } from 'class-validator'; | ||||
| import { NotAcceptableError, NotFoundError } from 'routing-controllers'; | ||||
|  | ||||
| /** | ||||
|  * Error to throw when a Donation couldn't be found. | ||||
|  */ | ||||
| export class DonationNotFoundError extends NotFoundError { | ||||
| 	@IsString() | ||||
| 	name = "DonationNotFoundError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "Donation not found!" | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Error to throw when two Donations' ids don't match. | ||||
|  * Usually occurs when a user tries to change a Donation's id. | ||||
|  */ | ||||
| export class DonationIdsNotMatchingError extends NotAcceptableError { | ||||
| 	@IsString() | ||||
| 	name = "DonationIdsNotMatchingError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "The ids don't match! \n And if you wanted to change a Donation's id: This isn't allowed!" | ||||
| } | ||||
| @@ -33,4 +33,15 @@ export class DonorReceiptAddressNeededError extends NotAcceptableError { | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "An address is needed to create a receipt for a donor. \n You didn't provide one." | ||||
| } | ||||
|  | ||||
| /** | ||||
| * Error to throw when a donor still has donations associated. | ||||
| */ | ||||
| export class DonorHasDonationsError extends NotAcceptableError { | ||||
| 	@IsString() | ||||
| 	name = "DonorHasDonationsError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "This donor still has donations associated with it. \n If you want to delete this donor with all it's donations and teams add `?force` to your query." | ||||
| } | ||||
| @@ -33,4 +33,15 @@ export class RunnerGroupNeededError extends NotAcceptableError { | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "Runner's need to be part of one group (team or organisation)! \n You provided neither." | ||||
| } | ||||
|  | ||||
| /** | ||||
| * Error to throw when a runner still has distance donations associated. | ||||
| */ | ||||
| export class RunnerHasDistanceDonationsError extends NotAcceptableError { | ||||
| 	@IsString() | ||||
| 	name = "RunnerHasDistanceDonationsError" | ||||
|  | ||||
| 	@IsString() | ||||
| 	message = "This runner still has distance donations associated with it. \n If you want to delete this runner with all it's donations and teams add `?force` to your query." | ||||
| } | ||||
							
								
								
									
										52
									
								
								src/models/actions/create/CreateDistanceDonation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/models/actions/create/CreateDistanceDonation.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| import { IsInt, IsPositive } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { RunnerNotFoundError } from '../../../errors/RunnerErrors'; | ||||
| import { DistanceDonation } from '../../entities/DistanceDonation'; | ||||
| import { Runner } from '../../entities/Runner'; | ||||
| import { CreateDonation } from './CreateDonation'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to create a new FixedDonation entity from a json body (post request). | ||||
|  */ | ||||
| export class CreateDistanceDonation extends CreateDonation { | ||||
|  | ||||
|     /** | ||||
|      * The donation's associated runner. | ||||
|      * This is important to link the runner's distance ran to the donation. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     runner: number; | ||||
|  | ||||
|     /** | ||||
|      * The donation's amount per distance (full kilometer aka 1000 meters). | ||||
|      * The unit is your currency's smallest unit (default: euro cent). | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     amountPerDistance: number; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new FixedDonation entity from this. | ||||
|      */ | ||||
|     public async toEntity(): Promise<DistanceDonation> { | ||||
|         let newDonation = new DistanceDonation; | ||||
|  | ||||
|         newDonation.amountPerDistance = this.amountPerDistance; | ||||
|         newDonation.donor = await this.getDonor(); | ||||
|         newDonation.runner = await this.getRunner(); | ||||
|  | ||||
|         return newDonation; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								src/models/actions/create/CreateDonation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/models/actions/create/CreateDonation.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| import { IsInt, IsPositive } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { DonorNotFoundError } from '../../../errors/DonorErrors'; | ||||
| import { Donation } from '../../entities/Donation'; | ||||
| import { Donor } from '../../entities/Donor'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to create a new Donation entity from a json body (post request). | ||||
|  */ | ||||
| export abstract class CreateDonation { | ||||
|     /** | ||||
|      * The donation's associated donor. | ||||
|      * This is important to link donations to donors. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     donor: number; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new Donation entity from this. | ||||
|      */ | ||||
|     public abstract toEntity(): Promise<Donation>; | ||||
|  | ||||
|     /** | ||||
|      * Gets a donor based on the donor id provided via this.donor. | ||||
|      */ | ||||
|     public async getDonor(): Promise<Donor> { | ||||
|         const donor = await getConnection().getRepository(Donor).findOne({ id: this.donor }); | ||||
|         if (!donor) { | ||||
|             throw new DonorNotFoundError(); | ||||
|         } | ||||
|         return donor; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/models/actions/create/CreateFixedDonation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/models/actions/create/CreateFixedDonation.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| import { IsInt, IsPositive } from 'class-validator'; | ||||
| import { FixedDonation } from '../../entities/FixedDonation'; | ||||
| import { CreateDonation } from './CreateDonation'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to create a new FixedDonation entity from a json body (post request). | ||||
|  */ | ||||
| export class CreateFixedDonation extends CreateDonation { | ||||
|     /** | ||||
|      * The donation's amount. | ||||
|      * The unit is your currency's smallest unit (default: euro cent). | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     amount: number; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new FixedDonation entity from this. | ||||
|      */ | ||||
|     public async toEntity(): Promise<FixedDonation> { | ||||
|         let newDonation = new FixedDonation; | ||||
|  | ||||
|         newDonation.amount = this.amount; | ||||
|         newDonation.donor = await this.getDonor(); | ||||
|  | ||||
|         return newDonation; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										51
									
								
								src/models/actions/update/UpdateDistanceDonation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/models/actions/update/UpdateDistanceDonation.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| import { IsInt, IsPositive } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { RunnerNotFoundError } from '../../../errors/RunnerErrors'; | ||||
| import { DistanceDonation } from '../../entities/DistanceDonation'; | ||||
| import { Runner } from '../../entities/Runner'; | ||||
| import { UpdateDonation } from './UpdateDonation'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to update a DistanceDonation entity (via put request). | ||||
|  */ | ||||
| export class UpdateDistanceDonation extends UpdateDonation { | ||||
|  | ||||
|     /** | ||||
|      * The donation's associated runner. | ||||
|      * This is important to link the runner's distance ran to the donation. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     runner: number; | ||||
|  | ||||
|     /** | ||||
|      * The donation's amount per distance (full kilometer aka 1000 meters). | ||||
|      * The unit is your currency's smallest unit (default: euro cent). | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     amountPerDistance: number; | ||||
|  | ||||
|     /** | ||||
|      * Update a DistanceDonation entity based on this. | ||||
|      * @param donation The donation that shall be updated. | ||||
|      */ | ||||
|     public async update(donation: DistanceDonation): Promise<DistanceDonation> { | ||||
|         donation.amountPerDistance = this.amountPerDistance; | ||||
|         donation.donor = await this.getDonor(); | ||||
|         donation.runner = await this.getRunner(); | ||||
|  | ||||
|         return donation; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										41
									
								
								src/models/actions/update/UpdateDonation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/models/actions/update/UpdateDonation.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| import { IsInt, IsPositive } from 'class-validator'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { DonorNotFoundError } from '../../../errors/DonorErrors'; | ||||
| import { Donation } from '../../entities/Donation'; | ||||
| import { Donor } from '../../entities/Donor'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to update a Donation entity (via put request). | ||||
|  */ | ||||
| export abstract class UpdateDonation { | ||||
|     /** | ||||
|      * The updated donation'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 donation's associated donor. | ||||
|      * This is important to link donations to donors. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     donor: number; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new Donation entity from this. | ||||
|      */ | ||||
|     public abstract update(donation: Donation): Promise<Donation>; | ||||
|  | ||||
|     /** | ||||
|      * Gets a donor based on the donor id provided via this.donor. | ||||
|      */ | ||||
|     public async getDonor(): Promise<Donor> { | ||||
|         const donor = await getConnection().getRepository(Donor).findOne({ id: this.donor }); | ||||
|         if (!donor) { | ||||
|             throw new DonorNotFoundError(); | ||||
|         } | ||||
|         return donor; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/models/actions/update/UpdateFixedDonation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/models/actions/update/UpdateFixedDonation.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| import { IsInt, IsPositive } from 'class-validator'; | ||||
| import { FixedDonation } from '../../entities/FixedDonation'; | ||||
| import { UpdateDonation } from './UpdateDonation'; | ||||
|  | ||||
| /** | ||||
|  * This class is used to update a FixedDonation entity (via put request). | ||||
|  */ | ||||
| export class UpdateFixedDonation extends UpdateDonation { | ||||
|     /** | ||||
|      * The updated donation's amount. | ||||
|      * The unit is your currency's smallest unit (default: euro cent). | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     amount: number; | ||||
|  | ||||
|     /** | ||||
|      * Update a FixedDonation entity based on this. | ||||
|      * @param donation The donation that shall be updated. | ||||
|      */ | ||||
|     public async update(donation: FixedDonation): Promise<FixedDonation> { | ||||
|         donation.amount = this.amount; | ||||
|         donation.donor = await this.getDonor(); | ||||
|  | ||||
|         return donation; | ||||
|     } | ||||
| } | ||||
| @@ -1,5 +1,6 @@ | ||||
| import { IsInt, IsNotEmpty, IsPositive } from "class-validator"; | ||||
| import { ChildEntity, Column, ManyToOne } from "typeorm"; | ||||
| import { ResponseDistanceDonation } from '../responses/ResponseDistanceDonation'; | ||||
| import { Donation } from "./Donation"; | ||||
| import { Runner } from "./Runner"; | ||||
|  | ||||
| @@ -31,7 +32,7 @@ export class DistanceDonation extends Donation { | ||||
|    * Get's calculated from the runner's distance ran and the amount donated per kilometer. | ||||
|    */ | ||||
|   public get amount(): number { | ||||
|     let calculatedAmount = -1; | ||||
|     let calculatedAmount = 0; | ||||
|     try { | ||||
|       calculatedAmount = this.amountPerDistance * (this.runner.distance / 1000); | ||||
|     } catch (error) { | ||||
| @@ -43,7 +44,7 @@ export class DistanceDonation extends Donation { | ||||
|   /** | ||||
|    * Turns this entity into it's response class. | ||||
|    */ | ||||
|   public toResponse() { | ||||
|     return new Error("NotImplemented"); | ||||
|   public toResponse(): ResponseDistanceDonation { | ||||
|     return new ResponseDistanceDonation(this); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import { | ||||
|   IsNotEmpty | ||||
| } from "class-validator"; | ||||
| import { Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm"; | ||||
| import { ResponseDonation } from '../responses/ResponseDonation'; | ||||
| import { Donor } from './Donor'; | ||||
|  | ||||
| /** | ||||
| @@ -31,12 +32,13 @@ export abstract class Donation { | ||||
|    * The donation's amount in cents (or whatever your currency's smallest unit is.). | ||||
|    * The exact implementation may differ for each type of donation. | ||||
|    */ | ||||
|   abstract amount: number; | ||||
|   public abstract get amount(): number; | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Turns this entity into it's response class. | ||||
|    */ | ||||
|   public toResponse() { | ||||
|     return new Error("NotImplemented"); | ||||
|   public toResponse(): ResponseDonation { | ||||
|     return new ResponseDonation(this); | ||||
|   } | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { IsBoolean } from "class-validator"; | ||||
| import { IsBoolean, IsInt } from "class-validator"; | ||||
| import { ChildEntity, Column, OneToMany } from "typeorm"; | ||||
| import { ResponseDonor } from '../responses/ResponseDonor'; | ||||
| import { Donation } from './Donation'; | ||||
| @@ -24,6 +24,15 @@ export class Donor extends Participant { | ||||
|   @OneToMany(() => Donation, donation => donation.donor, { nullable: true }) | ||||
|   donations: Donation[]; | ||||
|  | ||||
|   /** | ||||
|    * Returns the total donations of a donor based on his linked donations. | ||||
|   */ | ||||
|   @IsInt() | ||||
|   public get donationAmount(): number { | ||||
|     if (!this.donations) { return 0; } | ||||
|     return this.donations.reduce((sum, current) => sum + current.amount, 0); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Turns this entity into it's response class. | ||||
|    */ | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import { IsInt, IsPositive } from "class-validator"; | ||||
| import { ChildEntity, Column } from "typeorm"; | ||||
| import { ResponseDonation } from '../responses/ResponseDonation'; | ||||
| import { Donation } from "./Donation"; | ||||
|  | ||||
| /** | ||||
| @@ -11,16 +12,33 @@ export class FixedDonation extends Donation { | ||||
|  | ||||
|   /** | ||||
|    * The donation's amount in cents (or whatever your currency's smallest unit is.). | ||||
|    * This is the "real" value used by fixed donations. | ||||
|    */ | ||||
|   @Column() | ||||
|   @IsInt() | ||||
|   @IsPositive() | ||||
|   amount: number; | ||||
|   private _amount: number; | ||||
|  | ||||
|   /** | ||||
|    * The donation's amount in cents (or whatever your currency's smallest unit is.). | ||||
|    */ | ||||
|   @IsInt() | ||||
|   @IsPositive() | ||||
|   public get amount(): number { | ||||
|     return this._amount; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * The donation's amount in cents (or whatever your currency's smallest unit is.). | ||||
|    */ | ||||
|   public set amount(value: number) { | ||||
|     this._amount = value; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Turns this entity into it's response class. | ||||
|    */ | ||||
|   public toResponse() { | ||||
|     return new Error("NotImplemented"); | ||||
|   public toResponse(): ResponseDonation { | ||||
|     return new ResponseDonation(this); | ||||
|   } | ||||
| } | ||||
| @@ -13,5 +13,6 @@ export enum PermissionTarget { | ||||
|     DONOR = 'DONOR', | ||||
|     SCAN = 'SCAN', | ||||
|     STATION = 'STATION', | ||||
|     CARD = 'CARD' | ||||
|     CARD = 'CARD', | ||||
|     DONATION = 'DONATION' | ||||
| } | ||||
							
								
								
									
										35
									
								
								src/models/responses/ResponseDistanceDonation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/models/responses/ResponseDistanceDonation.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import { IsInt, IsObject, IsPositive } from 'class-validator'; | ||||
| import { DistanceDonation } from '../entities/DistanceDonation'; | ||||
| import { ResponseDonation } from './ResponseDonation'; | ||||
| import { ResponseRunner } from './ResponseRunner'; | ||||
|  | ||||
| /** | ||||
|  * Defines the distance donation response. | ||||
| */ | ||||
| export class ResponseDistanceDonation extends ResponseDonation { | ||||
|  | ||||
|     /** | ||||
|      * The donation's associated runner. | ||||
|      * Used as the source of the donation's distance. | ||||
|      */ | ||||
|     @IsObject() | ||||
|     runner: ResponseRunner; | ||||
|  | ||||
|     /** | ||||
|      * The donation's amount donated per distance. | ||||
|      * The amount the donor set to be donated per kilometer that the runner ran. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     amountPerDistance: number; | ||||
|  | ||||
|     /** | ||||
|      * Creates a ResponseDistanceDonation object from a scan. | ||||
|      * @param donation The distance donation the response shall be build for. | ||||
|      */ | ||||
|     public constructor(donation: DistanceDonation) { | ||||
|         super(donation); | ||||
|         this.runner = donation.runner.toResponse(); | ||||
|         this.amountPerDistance = donation.amountPerDistance; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										37
									
								
								src/models/responses/ResponseDonation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/models/responses/ResponseDonation.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| import { IsInt, IsNotEmpty, IsPositive } from "class-validator"; | ||||
| import { Donation } from '../entities/Donation'; | ||||
| import { ResponseDonor } from './ResponseDonor'; | ||||
|  | ||||
| /** | ||||
|  * Defines the donation response. | ||||
| */ | ||||
| export class ResponseDonation { | ||||
|     /** | ||||
|      * The donation's id. | ||||
|      */ | ||||
|     @IsInt() | ||||
|     @IsPositive() | ||||
|     id: number; | ||||
|  | ||||
|     /** | ||||
|      * The donation's donor. | ||||
|      */ | ||||
|     @IsNotEmpty() | ||||
|     donor: ResponseDonor; | ||||
|  | ||||
|     /** | ||||
|      * The donation's amount in the smalles unit of your currency (default: euro cent). | ||||
|      */ | ||||
|     @IsInt() | ||||
|     amount: number; | ||||
|  | ||||
|     /** | ||||
|      * Creates a ResponseDonation object from a scan. | ||||
|      * @param donation The donation the response shall be build for. | ||||
|      */ | ||||
|     public constructor(donation: Donation) { | ||||
|         this.id = donation.id; | ||||
|         this.donor = donation.donor.toResponse(); | ||||
|         this.amount = donation.amount; | ||||
|     } | ||||
| } | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { | ||||
|     IsBoolean | ||||
|     IsBoolean, IsInt | ||||
| } from "class-validator"; | ||||
| import { Donor } from '../entities/Donor'; | ||||
| import { ResponseParticipant } from './ResponseParticipant'; | ||||
| @@ -15,6 +15,12 @@ export class ResponseDonor extends ResponseParticipant { | ||||
|     @IsBoolean() | ||||
|     receiptNeeded: boolean; | ||||
|  | ||||
|     /** | ||||
|     * Returns the total donations of a donor based on his linked donations. | ||||
|     */ | ||||
|     @IsInt() | ||||
|     donationAmount: number; | ||||
|  | ||||
|     /** | ||||
|      * Creates a ResponseRunner object from a runner. | ||||
|      * @param runner The user the response shall be build for. | ||||
| @@ -22,5 +28,6 @@ export class ResponseDonor extends ResponseParticipant { | ||||
|     public constructor(donor: Donor) { | ||||
|         super(donor); | ||||
|         this.receiptNeeded = donor.receiptNeeded; | ||||
|         this.donationAmount = donor.donationAmount; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										236
									
								
								src/tests/donations/donations_add.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								src/tests/donations/donations_add.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,236 @@ | ||||
| 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/donations illegally', () => { | ||||
| 	it('posting to a non-existant endpoint should return 4040', async () => { | ||||
| 		const res1 = await axios.post(base + '/api/donations', null, axios_config); | ||||
| 		expect(res1.status).toEqual(404); | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('POST /api/donations/fixed illegally', () => { | ||||
| 	let added_donor; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = 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/donations/fixed', null, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('no donor should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/fixed', { | ||||
| 			"amount": 100 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('no amount should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/fixed', { | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('illegal amount input should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/fixed', { | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": -1 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('invalid donor input should return 404', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/fixed', { | ||||
| 			"donor": 999999999999999999999999, | ||||
| 			"amount": 100 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('POST /api/donations/distance illegally', () => { | ||||
| 	let added_donor; | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = 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/donations/distance', null, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('no donor should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('no amountPerDistance should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('no runner should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('illegal amountPerDistance input should return 400', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": -1, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('invalid donor input should return 404', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"donor": 999999999999999999999999, | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('invalid runner input should return 404', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"donor": added_donor.id, | ||||
| 			"runner": 999999999999999999999999, | ||||
| 			"amountPerDistance": 100, | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('POST /api/donations/fixed successfully', () => { | ||||
| 	let added_donor; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new fixed donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/fixed', { | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": 1000 | ||||
| 		}, axios_config); | ||||
| 		delete res.data.id; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"donor": added_donor, | ||||
| 			"amount": 1000 | ||||
| 		}); | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('POST /api/donations/distance successfully', () => { | ||||
| 	let added_donor; | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_donation; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		delete res.data.group; | ||||
| 		added_runner = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new fixed donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		delete res.data.id; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data).toEqual({ | ||||
| 			"donor": added_donor, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"runner": added_runner, | ||||
| 			"amount": 0 | ||||
| 		}) | ||||
| 	}); | ||||
| }); | ||||
							
								
								
									
										113
									
								
								src/tests/donations/donations_delete.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/tests/donations/donations_delete.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| 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 donation (non-existant)', () => { | ||||
| 	it('delete', async () => { | ||||
| 		const res = await axios.delete(base + '/api/donations/0', axios_config); | ||||
| 		expect(res.status).toEqual(204); | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('DELETE fixed donation', () => { | ||||
| 	let added_donor; | ||||
| 	let added_donation; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new fixed donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/fixed', { | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": 1000 | ||||
| 		}, axios_config); | ||||
| 		added_donation = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('delete donation', async () => { | ||||
| 		const res = await axios.delete(base + '/api/donations/' + added_donation.id, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 		expect(res.data).toEqual(added_donation); | ||||
| 	}); | ||||
| 	it('check if donation really was deleted', async () => { | ||||
| 		const res = await axios.get(base + '/api/donations/' + added_donation.id, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('DELETE distance donation', () => { | ||||
| 	let added_donor; | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_donation; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new distance donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		delete res.data.runner.distance; | ||||
| 		added_donation = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('delete donation', async () => { | ||||
| 		const res = await axios.delete(base + '/api/donations/' + added_donation.id, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 		delete res.data.runner.distance; | ||||
| 		expect(res.data).toEqual(added_donation); | ||||
| 	}); | ||||
| 	it('check if donation really was deleted', async () => { | ||||
| 		const res = await axios.get(base + '/api/donations/' + added_donation.id, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
							
								
								
									
										108
									
								
								src/tests/donations/donations_get.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/tests/donations/donations_get.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| 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/donations sucessfully', () => { | ||||
| 	it('basic get should return 200', async () => { | ||||
| 		const res = await axios.get(base + '/api/donations', axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('GET /api/donations illegally', () => { | ||||
| 	it('get for non-existant track should return 404', async () => { | ||||
| 		const res = await axios.get(base + '/api/donations/-1', axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('adding + getting fixed donation', () => { | ||||
| 	let added_donor; | ||||
| 	let added_donation; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new fixed donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/fixed', { | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": 1000 | ||||
| 		}, axios_config); | ||||
| 		added_donation = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('check if donation was added (no parameter validation)', async () => { | ||||
| 		const res = await axios.get(base + '/api/donations/' + added_donation.id, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('adding + getting distance donation', () => { | ||||
| 	let added_donor; | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_donation; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new fixed donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		added_donation = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('check if donation was added (no parameter validation)', async () => { | ||||
| 		const res = await axios.get(base + '/api/donations/' + added_donation.id, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 	}); | ||||
| }); | ||||
							
								
								
									
										343
									
								
								src/tests/donations/donations_update.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								src/tests/donations/donations_update.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,343 @@ | ||||
| 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 fixed donation illegally', () => { | ||||
| 	let added_donor; | ||||
| 	let added_donation; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new fixed donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/fixed', { | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": 1000 | ||||
| 		}, axios_config); | ||||
| 		added_donation = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating empty should return 400', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/fixed/' + added_donation.id, null, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with wrong id should return 406', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/fixed/' + added_donation.id, { | ||||
| 			"id": added_donation.id + 1, | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": 100 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(406); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with negative amount should return 400', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/fixed/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": -1 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with invalid donor should return 404', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/fixed/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"donor": 9999999999999999999, | ||||
| 			"amount": 100 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('adding + updating distance donation illegally', () => { | ||||
| 	let added_donor; | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_donation; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new distance donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		added_donation = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating empty should return 400', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, null, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with wrong id should return 406', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { | ||||
| 			"id": added_donation.id + 1, | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(406); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with negative amountPerDistance should return 400', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": -1, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(400); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with invalid donor should return 404', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": 9999999999999999999 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating with invalid runner should return 404', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"runner": 9999999999999999999, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(404); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('adding + updating fixed donation valid', () => { | ||||
| 	let added_donor; | ||||
| 	let added_donor2; | ||||
| 	let added_donation; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donationAmount; | ||||
| 		added_donor2 = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new fixed donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/fixed', { | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": 1000 | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donor.donationAmount; | ||||
| 		added_donation = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating nothing should return 200', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/fixed/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": 1000 | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donor.donationAmount; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data).toEqual(added_donation); | ||||
| 	}); | ||||
| 	it('updating amount should return 200', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/fixed/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"donor": added_donor.id, | ||||
| 			"amount": 42 | ||||
| 		}, axios_config); | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data.amount).toEqual(42); | ||||
| 	}); | ||||
| 	it('updating donor should return 200', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/fixed/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"donor": added_donor2.id, | ||||
| 			"amount": 42 | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donor.donationAmount; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data.donor).toEqual(added_donor2); | ||||
| 	}); | ||||
| }); | ||||
| // --------------- | ||||
| describe('adding + updating distance donation valid', () => { | ||||
| 	let added_donor; | ||||
| 	let added_donor2; | ||||
| 	let added_org; | ||||
| 	let added_runner; | ||||
| 	let added_runner2; | ||||
| 	let added_donation; | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donationAmount; | ||||
| 		added_donor = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new donor with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donors', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last" | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donationAmount; | ||||
| 		added_donor2 = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new org with just a name should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/organisations', { | ||||
| 			"name": "test123" | ||||
| 		}, axios_config); | ||||
| 		added_org = res.data | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		added_runner = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new runner with only needed params should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/runners', { | ||||
| 			"firstname": "first", | ||||
| 			"lastname": "last", | ||||
| 			"group": added_org.id | ||||
| 		}, axios_config); | ||||
| 		delete res.data.group; | ||||
| 		added_runner2 = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('creating a new distance donation should return 200', async () => { | ||||
| 		const res = await axios.post(base + '/api/donations/distance', { | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donor.donationAmount; | ||||
| 		added_donation = res.data; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json") | ||||
| 	}); | ||||
| 	it('updating nothing should return 200', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 100, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donor.donationAmount; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data).toEqual(added_donation); | ||||
| 	}); | ||||
| 	it('updating amount should return 200', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"runner": added_runner.id, | ||||
| 			"amountPerDistance": 69, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donor.donationAmount; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data.amountPerDistance).toEqual(69); | ||||
| 	}); | ||||
| 	it('updating runner should return 200', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"runner": added_runner2.id, | ||||
| 			"amountPerDistance": 69, | ||||
| 			"donor": added_donor.id | ||||
| 		}, axios_config); | ||||
| 		delete res.data.runner.group; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data.runner).toEqual(added_runner2); | ||||
| 	}); | ||||
| 	it('updating donor should return 200', async () => { | ||||
| 		const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { | ||||
| 			"id": added_donation.id, | ||||
| 			"runner": added_runner2.id, | ||||
| 			"amountPerDistance": 69, | ||||
| 			"donor": added_donor2.id | ||||
| 		}, axios_config); | ||||
| 		delete res.data.donor.donationAmount; | ||||
| 		expect(res.status).toEqual(200); | ||||
| 		expect(res.headers['content-type']).toContain("application/json"); | ||||
| 		expect(res.data.donor).toEqual(added_donor2); | ||||
| 	}); | ||||
| }); | ||||
| @@ -44,4 +44,114 @@ describe('add+delete', () => { | ||||
|         expect(res4.status).toEqual(404); | ||||
|         expect(res4.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
| }); | ||||
| // --------------- | ||||
| describe('DELETE donor with donations invalid', () => { | ||||
|     let added_donor; | ||||
|     let added_org; | ||||
|     let added_runner; | ||||
|     it('creating a new donor with only needed params should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donors', { | ||||
|             "firstname": "first", | ||||
|             "lastname": "last" | ||||
|         }, axios_config); | ||||
|         added_donor = res.data | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new org with just a name should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/organisations', { | ||||
|             "name": "test123" | ||||
|         }, axios_config); | ||||
|         added_org = res.data | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new runner with only needed params should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/runners', { | ||||
|             "firstname": "first", | ||||
|             "lastname": "last", | ||||
|             "group": added_org.id | ||||
|         }, axios_config); | ||||
|         added_runner = res.data; | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new fixed donation should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donations/fixed', { | ||||
|             "donor": added_donor.id, | ||||
|             "amount": 1000 | ||||
|         }, axios_config); | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new distance donation should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donations/distance', { | ||||
|             "runner": added_runner.id, | ||||
|             "amountPerDistance": 100, | ||||
|             "donor": added_donor.id | ||||
|         }, axios_config); | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('delete donor w/o force', async () => { | ||||
|         const res = await axios.delete(base + '/api/donors/' + added_donor.id, axios_config); | ||||
|         expect(res.status).toEqual(406); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
| }); | ||||
| // --------------- | ||||
| describe('DELETE donor with donations valid', () => { | ||||
|     let added_donor; | ||||
|     let added_org; | ||||
|     let added_runner; | ||||
|     it('creating a new donor with only needed params should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donors', { | ||||
|             "firstname": "first", | ||||
|             "lastname": "last" | ||||
|         }, axios_config); | ||||
|         added_donor = res.data | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new org with just a name should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/organisations', { | ||||
|             "name": "test123" | ||||
|         }, axios_config); | ||||
|         added_org = res.data | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new runner with only needed params should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/runners', { | ||||
|             "firstname": "first", | ||||
|             "lastname": "last", | ||||
|             "group": added_org.id | ||||
|         }, axios_config); | ||||
|         added_runner = res.data; | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new fixed donation should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donations/fixed', { | ||||
|             "donor": added_donor.id, | ||||
|             "amount": 1000 | ||||
|         }, axios_config); | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new distance donation should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donations/distance', { | ||||
|             "runner": added_runner.id, | ||||
|             "amountPerDistance": 100, | ||||
|             "donor": added_donor.id | ||||
|         }, axios_config); | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('delete donor w/ force ', async () => { | ||||
|         const res = await axios.delete(base + '/api/donors/' + added_donor.id + "?force=true", axios_config); | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
| }); | ||||
| @@ -55,4 +55,98 @@ describe('add+delete', () => { | ||||
|         expect(res4.status).toEqual(404); | ||||
|         expect(res4.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
| }); | ||||
| // --------------- | ||||
| describe('DELETE donor with donations invalid', () => { | ||||
|     let added_donor; | ||||
|     let added_org; | ||||
|     let added_runner; | ||||
|     it('creating a new donor with only needed params should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donors', { | ||||
|             "firstname": "first", | ||||
|             "lastname": "last" | ||||
|         }, axios_config); | ||||
|         added_donor = res.data | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new org with just a name should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/organisations', { | ||||
|             "name": "test123" | ||||
|         }, axios_config); | ||||
|         added_org = res.data | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new runner with only needed params should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/runners', { | ||||
|             "firstname": "first", | ||||
|             "lastname": "last", | ||||
|             "group": added_org.id | ||||
|         }, axios_config); | ||||
|         added_runner = res.data; | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new distance donation should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donations/distance', { | ||||
|             "runner": added_runner.id, | ||||
|             "amountPerDistance": 100, | ||||
|             "donor": added_donor.id | ||||
|         }, axios_config); | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('delete runner w/o force', async () => { | ||||
|         const res = await axios.delete(base + '/api/runners/' + added_runner.id, axios_config); | ||||
|         expect(res.status).toEqual(406); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
| }); | ||||
| // --------------- | ||||
| describe('DELETE donor with donations valid', () => { | ||||
|     let added_donor; | ||||
|     let added_org; | ||||
|     let added_runner; | ||||
|     it('creating a new donor with only needed params should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donors', { | ||||
|             "firstname": "first", | ||||
|             "lastname": "last" | ||||
|         }, axios_config); | ||||
|         added_donor = res.data | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new org with just a name should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/organisations', { | ||||
|             "name": "test123" | ||||
|         }, axios_config); | ||||
|         added_org = res.data | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new runner with only needed params should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/runners', { | ||||
|             "firstname": "first", | ||||
|             "lastname": "last", | ||||
|             "group": added_org.id | ||||
|         }, axios_config); | ||||
|         added_runner = res.data; | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('creating a new distance donation should return 200', async () => { | ||||
|         const res = await axios.post(base + '/api/donations/distance', { | ||||
|             "runner": added_runner.id, | ||||
|             "amountPerDistance": 100, | ||||
|             "donor": added_donor.id | ||||
|         }, axios_config); | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
|     it('delete donor w/ force ', async () => { | ||||
|         const res = await axios.delete(base + '/api/runners/' + added_runner.id + "?force=true", axios_config); | ||||
|         expect(res.status).toEqual(200); | ||||
|         expect(res.headers['content-type']).toContain("application/json") | ||||
|     }); | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user