Merge pull request 'New Feature: Donor endpoints feature/65-donor_controllers' (#69) from feature/65-donor_controllers into dev
Reviewed-on: #69 closes #65 Donors go 💲💲💲
This commit is contained in:
commit
39857cf6e6
105
src/controllers/DonorController.ts
Normal file
105
src/controllers/DonorController.ts
Normal file
@ -0,0 +1,105 @@
|
||||
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 { CreateDonor } from '../models/actions/CreateDonor';
|
||||
import { UpdateDonor } from '../models/actions/UpdateDonor';
|
||||
import { Donor } from '../models/entities/Donor';
|
||||
import { ResponseDonor } from '../models/responses/ResponseDonor';
|
||||
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
|
||||
|
||||
@JsonController('/donors')
|
||||
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
|
||||
export class DonorController {
|
||||
private donorRepository: Repository<Donor>;
|
||||
|
||||
/**
|
||||
* Gets the repository of this controller's model/entity.
|
||||
*/
|
||||
constructor() {
|
||||
this.donorRepository = getConnectionManager().get().getRepository(Donor);
|
||||
}
|
||||
|
||||
@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.' })
|
||||
async getAll() {
|
||||
let responseDonors: ResponseDonor[] = new Array<ResponseDonor>();
|
||||
const donors = await this.donorRepository.find();
|
||||
donors.forEach(donor => {
|
||||
responseDonors.push(new ResponseDonor(donor));
|
||||
});
|
||||
return responseDonors;
|
||||
}
|
||||
|
||||
@Get('/:id')
|
||||
@Authorized("DONOR:GET")
|
||||
@ResponseSchema(ResponseDonor)
|
||||
@ResponseSchema(DonorNotFoundError, { statusCode: 404 })
|
||||
@OnUndefined(DonorNotFoundError)
|
||||
@OpenAPI({ description: 'Lists all information about the runner whose id got provided.' })
|
||||
async getOne(@Param('id') id: number) {
|
||||
let donor = await this.donorRepository.findOne({ id: id })
|
||||
if (!donor) { throw new DonorNotFoundError(); }
|
||||
return new ResponseDonor(donor);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@Authorized("DONOR:CREATE")
|
||||
@ResponseSchema(ResponseDonor)
|
||||
@OpenAPI({ description: 'Create a new runner. <br> Please remeber to provide the runner\'s group\'s id.' })
|
||||
async post(@Body({ validate: true }) createRunner: CreateDonor) {
|
||||
let donor;
|
||||
try {
|
||||
donor = await createRunner.toDonor();
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
donor = await this.donorRepository.save(donor)
|
||||
return new ResponseDonor(await this.donorRepository.findOne(donor));
|
||||
}
|
||||
|
||||
@Put('/:id')
|
||||
@Authorized("DONOR:UPDATE")
|
||||
@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." })
|
||||
async put(@Param('id') id: number, @Body({ validate: true }) donor: UpdateDonor) {
|
||||
let oldDonor = await this.donorRepository.findOne({ id: id });
|
||||
|
||||
if (!oldDonor) {
|
||||
throw new DonorNotFoundError();
|
||||
}
|
||||
|
||||
if (oldDonor.id != donor.id) {
|
||||
throw new DonorIdsNotMatchingError();
|
||||
}
|
||||
|
||||
await this.donorRepository.save(await donor.updateDonor(oldDonor));
|
||||
return new ResponseDonor(await this.donorRepository.findOne({ id: id }));
|
||||
}
|
||||
|
||||
@Delete('/:id')
|
||||
@Authorized("DONOR:DELETE")
|
||||
@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).' })
|
||||
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);
|
||||
|
||||
if (!donor) {
|
||||
throw new DonorNotFoundError();
|
||||
}
|
||||
|
||||
//TODO: DELETE DONATIONS AND WARN FOR FORCE (https://git.odit.services/lfk/backend/issues/66)
|
||||
|
||||
await this.donorRepository.delete(donor);
|
||||
return new ResponseDonor(responseDonor);
|
||||
}
|
||||
}
|
36
src/errors/DonorErrors.ts
Normal file
36
src/errors/DonorErrors.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { IsString } from 'class-validator';
|
||||
import { NotAcceptableError, NotFoundError } from 'routing-controllers';
|
||||
|
||||
/**
|
||||
* Error to throw when a donor couldn't be found.
|
||||
*/
|
||||
export class DonorNotFoundError extends NotFoundError {
|
||||
@IsString()
|
||||
name = "DonorNotFoundError"
|
||||
|
||||
@IsString()
|
||||
message = "Donor not found!"
|
||||
}
|
||||
|
||||
/**
|
||||
* Error to throw when two donors' ids don't match.
|
||||
* Usually occurs when a user tries to change a donor's id.
|
||||
*/
|
||||
export class DonorIdsNotMatchingError extends NotAcceptableError {
|
||||
@IsString()
|
||||
name = "DonorIdsNotMatchingError"
|
||||
|
||||
@IsString()
|
||||
message = "The ids don't match! \n And if you wanted to change a donor's id: This isn't allowed!"
|
||||
}
|
||||
|
||||
/**
|
||||
* Error to throw when a donor needs a receipt, but no address is associated with them.
|
||||
*/
|
||||
export class DonorReceiptAddressNeededError extends NotAcceptableError {
|
||||
@IsString()
|
||||
name = "DonorReceiptAddressNeededError"
|
||||
|
||||
@IsString()
|
||||
message = "An address is needed to create a receipt for a donor. \n You didn't provide one."
|
||||
}
|
38
src/models/actions/CreateDonor.ts
Normal file
38
src/models/actions/CreateDonor.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { IsBoolean, IsOptional } from 'class-validator';
|
||||
import { DonorReceiptAddressNeededError } from '../../errors/DonorErrors';
|
||||
import { Donor } from '../entities/Donor';
|
||||
import { CreateParticipant } from './CreateParticipant';
|
||||
|
||||
/**
|
||||
* This classed is used to create a new Donor entity from a json body (post request).
|
||||
*/
|
||||
export class CreateDonor extends CreateParticipant {
|
||||
|
||||
/**
|
||||
* Does this donor need a receipt?
|
||||
*/
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
receiptNeeded?: boolean = false;
|
||||
|
||||
/**
|
||||
* Creates a new Donor entity from this.
|
||||
*/
|
||||
public async toDonor(): Promise<Donor> {
|
||||
let newDonor: Donor = new Donor();
|
||||
|
||||
newDonor.firstname = this.firstname;
|
||||
newDonor.middlename = this.middlename;
|
||||
newDonor.lastname = this.lastname;
|
||||
newDonor.phone = this.phone;
|
||||
newDonor.email = this.email;
|
||||
newDonor.address = await this.getAddress();
|
||||
newDonor.receiptNeeded = this.receiptNeeded;
|
||||
|
||||
if (this.receiptNeeded == true && this.address == null) {
|
||||
throw new DonorReceiptAddressNeededError()
|
||||
}
|
||||
|
||||
return newDonor;
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ export class CreateRunnerOrganisation extends CreateRunnerGroup {
|
||||
|
||||
newRunnerOrganisation.name = this.name;
|
||||
newRunnerOrganisation.contact = await this.getContact();
|
||||
newRunnerOrganisation.address = await this.getAddress();
|
||||
// newRunnerOrganisation.address = await this.getAddress();
|
||||
|
||||
return newRunnerOrganisation;
|
||||
}
|
||||
|
44
src/models/actions/UpdateDonor.ts
Normal file
44
src/models/actions/UpdateDonor.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { IsBoolean, IsInt, IsOptional } from 'class-validator';
|
||||
import { DonorReceiptAddressNeededError } from '../../errors/DonorErrors';
|
||||
import { Donor } from '../entities/Donor';
|
||||
import { CreateParticipant } from './CreateParticipant';
|
||||
|
||||
/**
|
||||
* This class is used to update a Donor entity (via put request).
|
||||
*/
|
||||
export class UpdateDonor extends CreateParticipant {
|
||||
|
||||
/**
|
||||
* The updated donor'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;
|
||||
|
||||
/**
|
||||
* Does the updated donor need a receipt?
|
||||
*/
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
receiptNeeded?: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* Updates a provided Donor entity based on this.
|
||||
*/
|
||||
public async updateDonor(donor: Donor): Promise<Donor> {
|
||||
donor.firstname = this.firstname;
|
||||
donor.middlename = this.middlename;
|
||||
donor.lastname = this.lastname;
|
||||
donor.phone = this.phone;
|
||||
donor.email = this.email;
|
||||
donor.receiptNeeded = this.receiptNeeded;
|
||||
donor.address = await this.getAddress();
|
||||
|
||||
if (this.receiptNeeded == true && this.address == null) {
|
||||
throw new DonorReceiptAddressNeededError()
|
||||
}
|
||||
|
||||
return donor;
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ export class UpdateRunnerOrganisation extends CreateRunnerGroup {
|
||||
|
||||
organisation.name = this.name;
|
||||
organisation.contact = await this.getContact();
|
||||
organisation.address = await this.getAddress();
|
||||
// organisation.address = await this.getAddress();
|
||||
|
||||
return organisation;
|
||||
}
|
||||
|
@ -7,8 +7,7 @@ import {
|
||||
} from "class-validator";
|
||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { config } from '../../config';
|
||||
import { Participant } from "./Participant";
|
||||
import { RunnerOrganisation } from "./RunnerOrganisation";
|
||||
import { IAddressUser } from './IAddressUser';
|
||||
|
||||
/**
|
||||
* Defines the Address entity.
|
||||
@ -79,12 +78,6 @@ export class Address {
|
||||
/**
|
||||
* Used to link the address to participants.
|
||||
*/
|
||||
@OneToMany(() => Participant, participant => participant.address, { nullable: true })
|
||||
participants: Participant[];
|
||||
|
||||
/**
|
||||
* Used to link the address to runner groups.
|
||||
*/
|
||||
@OneToMany(() => RunnerOrganisation, group => group.address, { nullable: true })
|
||||
groups: RunnerOrganisation[];
|
||||
@OneToMany(() => IAddressUser, addressUser => addressUser.address, { nullable: true })
|
||||
addressUsers: IAddressUser[];
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
IsNotEmpty
|
||||
} from "class-validator";
|
||||
import { Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
|
||||
import { Participant } from "./Participant";
|
||||
import { Donor } from './Donor';
|
||||
|
||||
/**
|
||||
* Defines the Donation entity.
|
||||
@ -24,8 +24,8 @@ export abstract class Donation {
|
||||
* The donations's donor.
|
||||
*/
|
||||
@IsNotEmpty()
|
||||
@ManyToOne(() => Participant, donor => donor.donations)
|
||||
donor: Participant;
|
||||
@ManyToOne(() => Donor, donor => donor.donations)
|
||||
donor: Donor;
|
||||
|
||||
/**
|
||||
* The donation's amount in cents (or whatever your currency's smallest unit is.).
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { IsBoolean } from "class-validator";
|
||||
import { ChildEntity, Column } from "typeorm";
|
||||
import { ChildEntity, Column, OneToMany } from "typeorm";
|
||||
import { Donation } from './Donation';
|
||||
import { Participant } from "./Participant";
|
||||
|
||||
/**
|
||||
@ -14,4 +15,11 @@ export class Donor extends Participant {
|
||||
@Column()
|
||||
@IsBoolean()
|
||||
receiptNeeded: boolean = false;
|
||||
|
||||
/**
|
||||
* Used to link the participant as the donor of a donation.
|
||||
* Attention: Only runner's can be associated as a distanceDonations distance source.
|
||||
*/
|
||||
@OneToMany(() => Donation, donation => donation.donor, { nullable: true })
|
||||
donations: Donation[];
|
||||
}
|
@ -10,6 +10,7 @@ import {
|
||||
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { config } from '../../config';
|
||||
import { Address } from "./Address";
|
||||
import { IAddressUser } from './IAddressUser';
|
||||
import { RunnerGroup } from "./RunnerGroup";
|
||||
|
||||
/**
|
||||
@ -17,7 +18,7 @@ import { RunnerGroup } from "./RunnerGroup";
|
||||
* Mainly it's own class to reduce duplicate code and enable contact's to be associated with multiple groups.
|
||||
*/
|
||||
@Entity()
|
||||
export class GroupContact {
|
||||
export class GroupContact implements IAddressUser {
|
||||
/**
|
||||
* Autogenerated unique id (primary key).
|
||||
*/
|
||||
@ -54,7 +55,7 @@ export class GroupContact {
|
||||
* This is a address object to prevent any formatting differences.
|
||||
*/
|
||||
@IsOptional()
|
||||
@ManyToOne(() => Address, address => address.participants, { nullable: true })
|
||||
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
|
||||
address?: Address;
|
||||
|
||||
/**
|
||||
|
15
src/models/entities/IAddressUser.ts
Normal file
15
src/models/entities/IAddressUser.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Entity, ManyToOne, PrimaryColumn } from 'typeorm';
|
||||
import { Address } from './Address';
|
||||
|
||||
/**
|
||||
* The interface(tm) all entities using addresses have to implement.
|
||||
* This is a abstract class, because apparently typeorm can't really work with interfaces :/
|
||||
*/
|
||||
@Entity()
|
||||
export abstract class IAddressUser {
|
||||
@PrimaryColumn()
|
||||
id: number;
|
||||
|
||||
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
|
||||
address?: Address
|
||||
}
|
@ -7,10 +7,10 @@ import {
|
||||
|
||||
IsString
|
||||
} from "class-validator";
|
||||
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
|
||||
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
|
||||
import { config } from '../../config';
|
||||
import { Address } from "./Address";
|
||||
import { Donation } from "./Donation";
|
||||
import { IAddressUser } from './IAddressUser';
|
||||
|
||||
/**
|
||||
* Defines the Participant entity.
|
||||
@ -18,7 +18,7 @@ import { Donation } from "./Donation";
|
||||
*/
|
||||
@Entity()
|
||||
@TableInheritance({ column: { name: "type", type: "varchar" } })
|
||||
export abstract class Participant {
|
||||
export abstract class Participant implements IAddressUser {
|
||||
/**
|
||||
* Autogenerated unique id (primary key).
|
||||
*/
|
||||
@ -54,7 +54,7 @@ export abstract class Participant {
|
||||
* The participant's address.
|
||||
* This is a address object to prevent any formatting differences.
|
||||
*/
|
||||
@ManyToOne(() => Address, address => address.participants, { nullable: true })
|
||||
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
|
||||
address?: Address;
|
||||
|
||||
/**
|
||||
@ -74,11 +74,4 @@ export abstract class Participant {
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
email?: string;
|
||||
|
||||
/**
|
||||
* Used to link the participant as the donor of a donation.
|
||||
* Attention: Only runner's can be associated as a distanceDonations distance source.
|
||||
*/
|
||||
@OneToMany(() => Donation, donation => donation.donor, { nullable: true })
|
||||
donations: Donation[];
|
||||
}
|
@ -18,7 +18,7 @@ export class Runner extends Participant {
|
||||
* Can be a runner team or organisation.
|
||||
*/
|
||||
@IsNotEmpty()
|
||||
@ManyToOne(() => RunnerGroup, group => group.runners, { nullable: false })
|
||||
@ManyToOne(() => RunnerGroup, group => group.runners)
|
||||
group: RunnerGroup;
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { IsInt, IsOptional } from "class-validator";
|
||||
import { ChildEntity, ManyToOne, OneToMany } from "typeorm";
|
||||
import { Address } from "./Address";
|
||||
import { Address } from './Address';
|
||||
import { IAddressUser } from './IAddressUser';
|
||||
import { Runner } from './Runner';
|
||||
import { RunnerGroup } from "./RunnerGroup";
|
||||
import { RunnerTeam } from "./RunnerTeam";
|
||||
@ -10,13 +11,13 @@ import { RunnerTeam } from "./RunnerTeam";
|
||||
* This usually is a school, club or company.
|
||||
*/
|
||||
@ChildEntity()
|
||||
export class RunnerOrganisation extends RunnerGroup {
|
||||
export class RunnerOrganisation extends RunnerGroup implements IAddressUser {
|
||||
|
||||
/**
|
||||
* The organisations's address.
|
||||
*/
|
||||
@IsOptional()
|
||||
@ManyToOne(() => Address, address => address.groups, { nullable: true })
|
||||
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
|
||||
address?: Address;
|
||||
|
||||
/**
|
||||
|
@ -9,5 +9,6 @@ export enum PermissionTarget {
|
||||
USER = 'USER',
|
||||
USERGROUP = 'USERGROUP',
|
||||
PERMISSION = 'PERMISSION',
|
||||
STATSCLIENT = 'STATSCLIENT'
|
||||
STATSCLIENT = 'STATSCLIENT',
|
||||
DONOR = 'DONOR'
|
||||
}
|
26
src/models/responses/ResponseDonor.ts
Normal file
26
src/models/responses/ResponseDonor.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import {
|
||||
IsBoolean
|
||||
} from "class-validator";
|
||||
import { Donor } from '../entities/Donor';
|
||||
import { ResponseParticipant } from './ResponseParticipant';
|
||||
|
||||
/**
|
||||
* Defines the donor response.
|
||||
*/
|
||||
export class ResponseDonor extends ResponseParticipant {
|
||||
|
||||
/**
|
||||
* Does this donor need a receipt?
|
||||
*/
|
||||
@IsBoolean()
|
||||
receiptNeeded: boolean;
|
||||
|
||||
/**
|
||||
* Creates a ResponseRunner object from a runner.
|
||||
* @param runner The user the response shall be build for.
|
||||
*/
|
||||
public constructor(donor: Donor) {
|
||||
super(donor);
|
||||
this.receiptNeeded = donor.receiptNeeded;
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import {
|
||||
IsArray,
|
||||
IsNotEmpty,
|
||||
IsObject
|
||||
|
||||
IsObject,
|
||||
IsOptional
|
||||
} from "class-validator";
|
||||
import { Address } from '../entities/Address';
|
||||
import { RunnerOrganisation } from '../entities/RunnerOrganisation';
|
||||
@ -17,7 +18,7 @@ export class ResponseRunnerOrganisation extends ResponseRunnerGroup {
|
||||
* The runnerOrganisation's address.
|
||||
*/
|
||||
@IsObject()
|
||||
@IsNotEmpty()
|
||||
@IsOptional()
|
||||
address?: Address;
|
||||
|
||||
/**
|
||||
|
94
src/tests/donors/donor_add.spec.ts
Normal file
94
src/tests/donors/donor_add.spec.ts
Normal file
@ -0,0 +1,94 @@
|
||||
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/donors with errors', () => {
|
||||
it('creating a new donor without any parameters should return 400', async () => {
|
||||
const res1 = await axios.post(base + '/api/donors', null, axios_config);
|
||||
expect(res1.status).toEqual(400);
|
||||
expect(res1.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('creating a new donor without a last name should return 400', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"middlename": "middle"
|
||||
}, axios_config);
|
||||
expect(res2.status).toEqual(400);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('creating a new donor with a invalid address should return 404', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"middlename": "middle",
|
||||
"lastname": "last",
|
||||
"address": 0
|
||||
}, axios_config);
|
||||
expect(res2.status).toEqual(404);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('creating a new donor with a invalid phone number should return 400', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"middlename": "middle",
|
||||
"lastname": "last",
|
||||
"phone": "123"
|
||||
}, axios_config);
|
||||
expect(res2.status).toEqual(400);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('creating a new donor with a invalid mail address should return 400', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "string",
|
||||
"middlename": "string",
|
||||
"lastname": "string",
|
||||
"phone": null,
|
||||
"email": "123",
|
||||
}, axios_config);
|
||||
expect(res2.status).toEqual(400);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('creating a new donor without an address but with receiptNeeded=true 406', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "string",
|
||||
"middlename": "string",
|
||||
"lastname": "string",
|
||||
"receiptNeeded": true
|
||||
}, axios_config);
|
||||
expect(res2.status).toEqual(406);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
});
|
||||
// ---------------
|
||||
describe('POST /api/donors working', () => {
|
||||
it('creating a new donor with only needed params should return 200', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"lastname": "last"
|
||||
}, axios_config);
|
||||
expect(res2.status).toEqual(200);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('creating a new donor with all non-relationship optional params should return 200', async () => {
|
||||
const res3 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"middlename": "middle",
|
||||
"lastname": "last",
|
||||
"receiptNeeded": false
|
||||
}, axios_config);
|
||||
expect(res3.status).toEqual(200);
|
||||
expect(res3.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
});
|
47
src/tests/donors/donor_delete.spec.ts
Normal file
47
src/tests/donors/donor_delete.spec.ts
Normal file
@ -0,0 +1,47 @@
|
||||
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 + deletion (non-existant)', () => {
|
||||
it('delete', async () => {
|
||||
const res2 = await axios.delete(base + '/api/donors/0', axios_config);
|
||||
expect(res2.status).toEqual(204);
|
||||
});
|
||||
});
|
||||
// ---------------
|
||||
describe('add+delete', () => {
|
||||
let added_donor;
|
||||
it('creating a new donor with only needed params should return 200', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"lastname": "last"
|
||||
}, axios_config);
|
||||
added_donor = res2.data;
|
||||
expect(res2.status).toEqual(200);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('delete donor', async () => {
|
||||
const res3 = await axios.delete(base + '/api/donors/' + added_donor.id, axios_config);
|
||||
expect(res3.status).toEqual(200);
|
||||
expect(res3.headers['content-type']).toContain("application/json")
|
||||
let deleted_runner = res3.data
|
||||
expect(deleted_runner).toEqual(added_donor);
|
||||
});
|
||||
it('check if donor really was deleted', async () => {
|
||||
const res4 = await axios.get(base + '/api/donors/' + added_donor.id, axios_config);
|
||||
expect(res4.status).toEqual(404);
|
||||
expect(res4.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
});
|
57
src/tests/donors/donor_get.spec.ts
Normal file
57
src/tests/donors/donor_get.spec.ts
Normal file
@ -0,0 +1,57 @@
|
||||
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/donors', () => {
|
||||
it('basic get should return 200', async () => {
|
||||
const res = await axios.get(base + '/api/donors', axios_config);
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
});
|
||||
// ---------------
|
||||
describe('GET /api/donors/0', () => {
|
||||
it('basic get should return 404', async () => {
|
||||
const res = await axios.get(base + '/api/donors/0', axios_config);
|
||||
expect(res.status).toEqual(404);
|
||||
expect(res.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
});
|
||||
// ---------------
|
||||
describe('GET /api/donors after adding', () => {
|
||||
let added_donor;
|
||||
it('creating a new donor with only needed params should return 200', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"lastname": "last"
|
||||
}, axios_config);
|
||||
added_donor = res2.data;
|
||||
expect(res2.status).toEqual(200);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('explicit get should return 200', async () => {
|
||||
const res3 = await axios.get(base + '/api/donors/' + added_donor.id, axios_config);
|
||||
expect(res3.status).toEqual(200);
|
||||
expect(res3.headers['content-type']).toContain("application/json")
|
||||
let gotten_donor = res3.data
|
||||
expect(gotten_donor).toEqual(added_donor);
|
||||
});
|
||||
it('get from all runners should return 200', async () => {
|
||||
const res4 = await axios.get(base + '/api/donors/', axios_config);
|
||||
expect(res4.status).toEqual(200);
|
||||
expect(res4.headers['content-type']).toContain("application/json")
|
||||
let gotten_donors = res4.data
|
||||
expect(gotten_donors).toContainEqual(added_donor);
|
||||
});
|
||||
});
|
75
src/tests/donors/donor_update.spec.ts
Normal file
75
src/tests/donors/donor_update.spec.ts
Normal file
@ -0,0 +1,75 @@
|
||||
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('Update donor name after adding', () => {
|
||||
let added_donor;
|
||||
it('creating a new runner with only needed params should return 200', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"lastname": "last"
|
||||
}, axios_config);
|
||||
added_donor = res2.data;
|
||||
expect(res2.status).toEqual(200);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('valid update should return 200', async () => {
|
||||
let donor_copy = added_donor
|
||||
donor_copy.firstname = "second"
|
||||
const res3 = await axios.put(base + '/api/donors/' + added_donor.id, donor_copy, axios_config);
|
||||
expect(res3.status).toEqual(200);
|
||||
expect(res3.headers['content-type']).toContain("application/json")
|
||||
let updated_donor = res3.data
|
||||
expect(updated_donor).toEqual(donor_copy);
|
||||
});
|
||||
});
|
||||
// ---------------
|
||||
describe('Update donor id after adding(should fail)', () => {
|
||||
let added_donor;
|
||||
it('creating a new donor with only needed params should return 200', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"lastname": "last"
|
||||
}, axios_config);
|
||||
added_donor = res2.data;
|
||||
expect(res2.status).toEqual(200);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('invalid update should return 406', async () => {
|
||||
added_donor.id++;
|
||||
const res3 = await axios.put(base + '/api/donors/' + (added_donor.id - 1), added_donor, axios_config);
|
||||
expect(res3.status).toEqual(406);
|
||||
expect(res3.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
});
|
||||
// ---------------
|
||||
describe('Update donor without address but receiptNeeded=true should fail', () => {
|
||||
let added_donor;
|
||||
it('creating a new donor with only needed params should return 200', async () => {
|
||||
const res2 = await axios.post(base + '/api/donors', {
|
||||
"firstname": "first",
|
||||
"lastname": "last",
|
||||
}, axios_config);
|
||||
added_donor = res2.data;
|
||||
expect(res2.status).toEqual(200);
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
it('invalid update should return 406', async () => {
|
||||
added_donor.receiptNeeded = true;
|
||||
const res3 = await axios.put(base + '/api/donors/' + added_donor.id, added_donor, axios_config);
|
||||
expect(res3.status).toEqual(406);
|
||||
expect(res3.headers['content-type']).toContain("application/json")
|
||||
});
|
||||
});
|
@ -72,30 +72,26 @@ describe('adding + getting tracks', () => {
|
||||
});
|
||||
// ---------------
|
||||
describe('adding + getting + updating', () => {
|
||||
let added_track_id
|
||||
let added_track;
|
||||
it('correct distance input should return 200', async () => {
|
||||
const res = await axios.post(base + '/api/tracks', {
|
||||
"name": "string",
|
||||
"distance": 1500
|
||||
}, axios_config);
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers['content-type']).toContain("application/json")
|
||||
expect(res.headers['content-type']).toContain("application/json");
|
||||
added_track = res.data;
|
||||
});
|
||||
it('get should return 200', async () => {
|
||||
const res1 = await axios.get(base + '/api/tracks', axios_config);
|
||||
const res1 = await axios.get(base + '/api/tracks/' + added_track.id, axios_config);
|
||||
expect(res1.status).toEqual(200);
|
||||
expect(res1.headers['content-type']).toContain("application/json")
|
||||
let added_track = res1.data[res1.data.length - 1]
|
||||
added_track_id = added_track.id
|
||||
delete added_track.id
|
||||
expect(added_track).toEqual({
|
||||
"name": "string",
|
||||
"distance": 1500
|
||||
})
|
||||
const compareTrack = res1.data;
|
||||
expect(compareTrack).toEqual(added_track)
|
||||
})
|
||||
it('get should return 200', async () => {
|
||||
const res2 = await axios.put(base + '/api/tracks/' + added_track_id, {
|
||||
"id": added_track_id,
|
||||
const res2 = await axios.put(base + '/api/tracks/' + added_track.id, {
|
||||
"id": added_track.id,
|
||||
"name": "apitrack",
|
||||
"distance": 5100
|
||||
}, axios_config);
|
||||
@ -103,10 +99,10 @@ describe('adding + getting + updating', () => {
|
||||
expect(res2.headers['content-type']).toContain("application/json")
|
||||
})
|
||||
it('get should return 200', async () => {
|
||||
const res3 = await axios.get(base + '/api/tracks', axios_config);
|
||||
const res3 = await axios.get(base + '/api/tracks/' + added_track.id, axios_config);
|
||||
expect(res3.status).toEqual(200);
|
||||
expect(res3.headers['content-type']).toContain("application/json")
|
||||
let added_track2 = res3.data[res3.data.length - 1]
|
||||
let added_track2 = res3.data;
|
||||
delete added_track2.id
|
||||
expect(added_track2).toEqual({
|
||||
"name": "apitrack",
|
||||
|
Loading…
x
Reference in New Issue
Block a user