diff --git a/api/__tests__/mock/address.ts b/api/__tests__/mock/address.ts index 91068b87e..71cc0d865 100644 --- a/api/__tests__/mock/address.ts +++ b/api/__tests__/mock/address.ts @@ -28,7 +28,16 @@ export async function initialiseAddress() { longitude: 3.71847, }; + const address4 = { + street: "Krijgslaan", + number: 282, + city: "Ghent", + zip_code: 9000, + latitude: 51.02776, + longitude: 3.71847, + }; + await prisma.address.createMany({ - data: [address1, address2, address3], + data: [address1, address2, address3, address4], }); } diff --git a/api/__tests__/routes/address.test.ts b/api/__tests__/routes/address.test.ts index d9c43aa9e..8cac9dcb1 100644 --- a/api/__tests__/routes/address.test.ts +++ b/api/__tests__/routes/address.test.ts @@ -7,6 +7,12 @@ import { initialiseDatabase, restoreTables, } from "../mock/database"; +import { + badRequestResponse, + forbiddenResponse, + methodNotAllowedResponse, + notFoundResponse, +} from "../utilities/constants"; describe("Address tests", () => { let runner: Testrunner; @@ -83,8 +89,233 @@ describe("Address tests", () => { }); }); - afterAll(() => { - app.close(); + test("DELETE /address/:id", async () => { + await runner.delete({ url: "/address/4" }); + //verify that the address is truly deleted (no getAll method) + await runner.get({ + url: "/address/4", + expectedData: [notFoundResponse], + statusCode: 404, + }); + }); + + test("PATCH /address/:id (Student own address)", async () => { + runner.authLevel(AuthenticationLevel.STUDENT); + const expected = { + street: "Wallaby Way", + number: 42, + city: "Gent", + zip_code: 2000, + latitude: -33.865143, + longitude: 151.2099, + id: 1, + }; + + await runner.patch({ + url: "/address/1", + data: { city: "Gent" }, + expectedResponse: expected, + }); + }); + }); + + describe("Unsuccesful requests", () => { + let runner: Testrunner; + beforeAll(async () => { + const server = request(app); + runner = new Testrunner(server); + + await deleteDatabaseData(); + await initialiseDatabase(); + }); + + describe("Must be correctly authorized to use any path", () => { + const newAddress = { + street: "Krijgslaan", + number: 2, + city: "Ghent", + zip_code: 9000, + latitude: 51.02776, + longitude: 3.71847, + }; + + describe("Cannot reach any path without authorisation", () => { + beforeEach(() => { + runner.authLevel(AuthenticationLevel.UNAUTHORIZED); + }); + + test("Cannot reach GET /address", async () => { + await runner.get({ + url: "/address", + expectedData: [forbiddenResponse], + statusCode: 403, + }); + }); + + test("Cannot reach GET /address/:id", async () => { + await runner.get({ + url: "/address/1", + expectedData: [forbiddenResponse], + statusCode: 403, + }); + }); + + test("Cannot reach POST /address", async () => { + await runner.post({ + url: "/address", + data: newAddress, + expectedResponse: forbiddenResponse, + statusCode: 403, + }); + }); + + test("Cannot reach PATCH /address/:id", async () => { + await runner.patch({ + url: "/address/1", + data: newAddress, + expectedResponse: forbiddenResponse, + statusCode: 403, + }); + }); + + test("Cannot reach DELETE /address/:id", async () => { + await runner.delete({ + url: "/address/1", + statusCode: 403, + }); + }); + }); + describe("Cannot reach any path as a student", () => { + beforeEach(() => { + runner.authLevel(AuthenticationLevel.STUDENT); + }); + + test("Cannot reach GET /address", async () => { + await runner.get({ + url: "/address", + expectedData: [forbiddenResponse], + statusCode: 403, + }); + }); + + test("Cannot reach POST /address", async () => { + await runner.post({ + url: "/address", + data: newAddress, + expectedResponse: forbiddenResponse, + statusCode: 403, + }); + }); + + test("PATCH /address/:id (not own address)", async () => { + const expected = { + street: "Wallaby Way", + number: 42, + city: "Gent", + zip_code: 2000, + latitude: -33.865143, + longitude: 151.2099, + id: 1, + }; + + await runner.patch({ + url: "/address/2", + data: { city: "Gent" }, + expectedResponse: forbiddenResponse, + statusCode: 403, + }); + }); + + test("Cannot reach DELETE /address/:id", async () => { + await runner.delete({ + url: "/address/1", + statusCode: 403, + }); + }); + }); + }); + describe("The requested path must exist", () => { + beforeEach(() => { + runner.authLevel(AuthenticationLevel.ADMINISTRATOR); + }); + + test("Find a nonexistent address", async () => { + await runner.get({ + url: "/address/1000", + expectedData: [notFoundResponse], + statusCode: 404, + }); + }); + + test("Update a nonexistent address", async () => { + const newAddress = { + street: "Krijgslaan", + number: 2, + }; + await runner.patch({ + url: "/address/1000", + data: newAddress, + expectedResponse: notFoundResponse, + statusCode: 404, + }); + }); + test("Delete a nonexistent address", async () => { + await runner.delete({ url: "/address/1000", statusCode: 404 }); + }); + }); + describe("The type of address id must be correct", () => { + beforeEach(() => { + runner.authLevel(AuthenticationLevel.ADMINISTRATOR); + }); + + test("GET request", async () => { + await runner.get({ + url: "/address/wrongtype", + expectedData: [badRequestResponse], + statusCode: 400, + }); + }); + + test("PATCH request", async () => { + const newAddress = { + street: "Krijgslaan", + number: 2, + city: "Ghent", + zip_code: 9000, + latitude: 51.02776, + longitude: 3.71847, + }; + + await runner.patch({ + url: "/address/wrongtype", + data: newAddress, + expectedResponse: badRequestResponse, + statusCode: 400, + }); + }); + + test("DELETE request", async () => { + await runner.delete({ + url: "/address/wrongtype", + statusCode: 400, + }); + }); }); + describe("The type of address id must be correct", () => { + beforeEach(() => { + runner.authLevel(AuthenticationLevel.ADMINISTRATOR); + }); + test("Cannot reach GET /address", async () => { + await runner.get({ + url: "/address", + expectedData: [methodNotAllowedResponse], + statusCode: 405, + }); + }); + }); + }); + + afterAll(() => { + app.close(); }); }); diff --git a/api/__tests__/routes/user.test.ts b/api/__tests__/routes/user.test.ts index 6dfb05f33..c5b5da245 100644 --- a/api/__tests__/routes/user.test.ts +++ b/api/__tests__/routes/user.test.ts @@ -176,13 +176,13 @@ describe("User tests", () => { last_login: "2020-01-01T00:00:00.000Z", date_added: "2020-01-01T00:00:00.000Z", phone: "23457890", - address_id: 4, + address_id: 5, student: false, super_student: true, admin: false, deleted: false, address: { - id: 4, + id: 5, street: "street", number: 1, city: "Gent", diff --git a/api/__tests__/utilities/constants.ts b/api/__tests__/utilities/constants.ts index 003125498..8a83ea395 100644 --- a/api/__tests__/utilities/constants.ts +++ b/api/__tests__/utilities/constants.ts @@ -29,3 +29,7 @@ export const badRequestForeignKey = { message: "Bad Request", detail: "Foreign key constraint failed", }; + +export const methodNotAllowedResponse = { + message: "Method not allowed", +}; diff --git a/api/src/routes/address.ts b/api/src/routes/address.ts index aade19771..feff71f51 100644 --- a/api/src/routes/address.ts +++ b/api/src/routes/address.ts @@ -38,6 +38,11 @@ export class AddressRouting extends Routing { async updateOne(req: CustomRequest, res: express.Response) { const addressIdentifier = Parser.number(req.params["id"]); + // Must be a valid identifier. + if (!addressIdentifier || Number.isNaN(addressIdentifier)) { + throw new APIError(APIErrorCode.BAD_REQUEST); + } + // If the user is a student, they can only update their own address. if ( process.env["DISABLE_AUTH"] !== "true" &&