Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event Tests #122

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/database/event-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,6 @@ export class EventAttendance {
},
})
public attendees: string[];

public isStaff = true;
}
244 changes: 244 additions & 0 deletions src/services/event/event-router.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import { describe, expect, it, beforeEach } from "@jest/globals";
import { StatusCode } from "status-code-enum";
import Models from "../../database/models.js";
import {
delAsAdmin,
delAsAttendee,
delAsStaff,
getAsAttendee,
getAsStaff,
putAsAdmin,
putAsAttendee,
putAsStaff,
} from "../../testTools.js";

const EXTERNAL_PUBLIC_EVENT = {
eventId: "11111c072182654f163f5f0f9a621d72",
name: "Example Pub Event 10",
description: "This is a description",
startTime: 1532202702,
endTime: 1532212702,
locations: [
{
description: "Example Location",
tags: ["SIEBEL0", "ECEB1"],
latitude: 40.1138,
longitude: -88.2249,
},
],
sponsor: "Example sponsor",
eventType: "OTHER",
points: 0,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np: Use satisfies to verify correctness


const INTERNAL_PUBLIC_EVENT = {
...EXTERNAL_PUBLIC_EVENT,
displayOnStaffCheckIn: false,
isPrivate: false,
isAsync: false,
};

const EXTERNAL_STAFF_EVENT = {
eventId: "00000c072182654f163f5f0f9a621d72",
name: "Example Staff Event 10",
description: "This is a description",
startTime: 1532202702,
endTime: 1532212702,
locations: [],
eventType: "OTHER",
};

const INTERNAL_STAFF_EVENT = {
...EXTERNAL_STAFF_EVENT,
sponsor: "Example sponsor",
displayOnStaffCheckIn: false,
isPrivate: false,
isAsync: false,
isStaff: true,
};

const PUBLIC_METADATA = {
eventId: "11111c072182654f163f5f0f9a621d72",
isStaff: false,
exp: 1234567890,
};

const STAFF_METADATA = {
eventId: "00000c072182654f163f5f0f9a621d72",
isStaff: true,
exp: 1234567890,
};

beforeEach(async () => {
Models.initialize();
await Models.StaffEvent.create(INTERNAL_STAFF_EVENT);
await Models.PublicEvent.create(INTERNAL_PUBLIC_EVENT);
await Models.EventMetadata.create(PUBLIC_METADATA);
await Models.EventMetadata.create(STAFF_METADATA);
});

describe("GET /event/", () => {
it("returns only filtered attendee events for attendees", async () => {
const response = await getAsAttendee("/event/").expect(StatusCode.SuccessOK);

expect(JSON.parse(response.text)).toMatchObject({
events: [EXTERNAL_PUBLIC_EVENT],
});
});

it("returns all attendee events for staff", async () => {
const response = await getAsStaff("/event/").expect(StatusCode.SuccessOK);

expect(JSON.parse(response.text)).toMatchObject({
events: [INTERNAL_PUBLIC_EVENT],
});
});
});

describe("GET /event/staff/", () => {
it("cannot be accessed by a non-staff user", async () => {
const response = await getAsAttendee("/event/staff/").expect(StatusCode.ClientErrorForbidden);
expect(response).toHaveProperty("error");
});

it("returns staff events for staff endpoint", async () => {
const response = await getAsStaff("/event/staff/").expect(StatusCode.SuccessOK);
expect(JSON.parse(response.text)).toMatchObject({
events: [EXTERNAL_STAFF_EVENT],
});
});
});

describe("GET /event/:EVENTID", () => {
it("throws an error if the event doesn't exist", async () => {
const eventId = "00000";
const response = await getAsAttendee(`/event/${eventId}`).expect(StatusCode.ClientErrorBadRequest);
expect(response).toHaveProperty("error");
});

it("throws an error if attendees try to access staff events", async () => {
const eventId = STAFF_METADATA.eventId;
const response = await getAsAttendee(`/event/${eventId}`).expect(StatusCode.ClientErrorForbidden);
expect(response).toHaveProperty("error");
});

it("throws an error if it cannot find a staff event", async () => {
const eventId = STAFF_METADATA.eventId;
await Models.StaffEvent.deleteOne({ eventId: eventId });
const response = await getAsStaff(`/event/${eventId}`).expect(StatusCode.ServerErrorInternal);
expect(response).toHaveProperty("error");
});

it("throws an error if it cannot find a public event", async () => {
const eventId = PUBLIC_METADATA.eventId;
await Models.PublicEvent.deleteOne({ eventId: eventId });
const response = await getAsAttendee(`/event/${eventId}`).expect(StatusCode.ServerErrorInternal);
expect(response).toHaveProperty("error");
});

it("successfully returns staff events", async () => {
const eventId = STAFF_METADATA.eventId;
const response = await getAsStaff(`/event/${eventId}`).expect(StatusCode.SuccessOK);
expect(JSON.parse(response.text)).toMatchObject(EXTERNAL_STAFF_EVENT);
});

it("successfully filters and returns a public event for staff", async () => {
const eventId = PUBLIC_METADATA.eventId;
const response = await getAsStaff(`/event/${eventId}`).expect(StatusCode.SuccessOK);
expect(JSON.parse(response.text)).toMatchObject(INTERNAL_PUBLIC_EVENT);
});

it("successfully filters and returns a public event for attendees", async () => {
const eventId = PUBLIC_METADATA.eventId;
const response = await getAsAttendee(`/event/${eventId}`).expect(StatusCode.SuccessOK);
expect(JSON.parse(response.text)).toMatchObject(EXTERNAL_PUBLIC_EVENT);
});
});

describe("GET /event/metadata/:EVENTID", () => {
it("cannot be accessed by a non-staff user", async () => {
const eventId = PUBLIC_METADATA.eventId;
const response = await getAsAttendee(`/event/metadata/${eventId}`).expect(StatusCode.ClientErrorForbidden);
expect(response).toHaveProperty("error");
});

it("returns metadata for a particular eventId", async () => {
const eventId = PUBLIC_METADATA.eventId;
const response = await getAsStaff(`/event/metadata/${eventId}`).expect(StatusCode.SuccessOK);
expect(JSON.parse(response.text)).toMatchObject(PUBLIC_METADATA);
});

it("throws an error if a bad eventId is passed in", async () => {
const eventId = "badEventId";
const response = await getAsStaff(`/event/metadata/${eventId}`).expect(StatusCode.ClientErrorBadRequest);
expect(JSON.parse(response.text)).toHaveProperty("error");
});
});

describe("DELETE /event/:EVENTID", () => {
it("cannot be accessed by an attendee", async () => {
const eventId = "deleteEventId";
const response = await delAsAttendee(`/event/${eventId}`).expect(StatusCode.ClientErrorForbidden);
expect(response).toHaveProperty("error");
});

it("cannot be accessed by staff", async () => {
const eventId = "deleteEventId";
const response = await delAsStaff(`/event/${eventId}`).expect(StatusCode.ClientErrorForbidden);
expect(response).toHaveProperty("error");
});

it("deletes the events correctly from both tables", async () => {
const eventId = "deleteEventId";
await delAsAdmin(`/event/${eventId}`).expect(StatusCode.SuccessNoContent);

const metadata = await Models.EventMetadata.findOne({ eventId: eventId });
const event = await Models.PublicEvent.findOne({ eventId: eventId });

expect(metadata).toBeNull();
expect(event).toBeNull();
});
});

describe("PUT /event/metadata/", () => {
it("cannot be accessed by attendee", async () => {
const response = await putAsAttendee(`/event/metadata/`).expect(StatusCode.ClientErrorForbidden);
expect(response).toHaveProperty("error");
});

it("cannot be accessed by staff", async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't need to test for every level - just test the level below what is expect (staff for admin only, attendee for staff only, etc)

const response = await putAsStaff(`/event/metadata/`).expect(StatusCode.ClientErrorForbidden);
expect(response).toHaveProperty("error");
});

it("throws an error if an invalid input is passed in", async () => {
const mockInput = {
eventId: EXTERNAL_PUBLIC_EVENT.eventId,
};

const response = await putAsAdmin(`/event/metadata/`).send(mockInput).expect(StatusCode.ClientErrorBadRequest);
expect(response).toHaveProperty("error");
});

it("throws an error if an event does not exist", async () => {
const mockInput = {
eventId: "0".repeat(32),
exp: 100,
};

const response = await putAsAdmin(`/event/metadata/`).send(mockInput).expect(StatusCode.ClientErrorBadRequest);
expect(response).toHaveProperty("error");
});

it("modifies the event expiration dates", async () => {
const eventId = EXTERNAL_PUBLIC_EVENT.eventId;
const mockInput = {
eventId: eventId,
exp: 100,
};

const response = await putAsAdmin(`/event/metadata/`).send(mockInput).expect(StatusCode.SuccessOK);
expect(JSON.parse(response.text));
expect(await Models.EventMetadata.findOne({ eventId })).toMatchObject(mockInput);
});
});
49 changes: 15 additions & 34 deletions src/services/event/event-router.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import cors from "cors";
import crypto from "crypto";
import { Request, Router } from "express";
import { NextFunction, Response } from "express-serve-static-core";
import { Request, Response, Router } from "express";

import Config from "../../config.js";
import { strongJwtVerification, weakJwtVerification } from "../../middleware/verify-jwt.js";
Expand Down Expand Up @@ -132,21 +131,16 @@ eventsRouter.get("/staff/", strongJwtVerification, async (_: Request, res: Respo
* HTTP/1.1 403 Forbidden
* {"error": "PrivateEvent"}
*/
eventsRouter.get("/:EVENTID/", weakJwtVerification, async (req: Request, res: Response, next: NextFunction) => {
eventsRouter.get("/:EVENTID/", weakJwtVerification, async (req: Request, res: Response) => {
const eventId: string | undefined = req.params.EVENTID;

if (!eventId) {
return res.redirect("/");
}

const payload: JwtPayload = res.locals.payload as JwtPayload;
const isStaff: boolean = hasStaffPerms(payload);

const metadata: EventMetadata | null = await Models.EventMetadata.findOne({ eventId: eventId });

if (!metadata) {
console.error("no metadata found!");
return next(new Error("no event found!"));
return res.status(StatusCode.ClientErrorBadRequest).send({ error: "EventNotFound" });
}

if (metadata.isStaff) {
Expand All @@ -155,18 +149,24 @@ eventsRouter.get("/:EVENTID/", weakJwtVerification, async (req: Request, res: Re
}

const event: StaffEvent | null = await Models.StaffEvent.findOne({ eventId: eventId });
return res.status(StatusCode.SuccessOK).send({ event: event });
if (!event) {
return res.status(StatusCode.ServerErrorInternal).send({ error: "InternalDatabaseError" });
}

return res.status(StatusCode.SuccessOK).send(event);
} else {
// Not a private event -> convert to Public event and return
const event: PublicEvent | null = await Models.PublicEvent.findOne({ eventId: eventId });

if (!event) {
console.error("no metadata found!");
return next(new Error("no event found!"));
return res.status(StatusCode.ServerErrorInternal).send({ error: "InternalDatabaseError" });
}

if (isStaff) {
return res.status(StatusCode.SuccessOK).send(event);
}

const filteredEvent: FilteredEventView = createFilteredEventView(event);
return res.status(StatusCode.SuccessOK).send({ event: filteredEvent });
return res.status(StatusCode.SuccessOK).send(filteredEvent);
}
});

Expand Down Expand Up @@ -369,7 +369,6 @@ eventsRouter.post("/", strongJwtVerification, async (req: Request, res: Response
const eventId: string = crypto.randomBytes(Config.EVENT_BYTES_GEN).toString("hex");
const isStaffEvent: boolean = eventFormat.isStaff;
const metadata: EventMetadata = new EventMetadata(eventId, isStaffEvent, eventFormat.endTime);
console.log(eventId);
// Populate the new eventFormat object with the needed params
eventFormat._id = new ObjectId().toString();
eventFormat.eventId = eventId;
Expand All @@ -383,14 +382,12 @@ eventsRouter.post("/", strongJwtVerification, async (req: Request, res: Response
return res.status(StatusCode.ClientErrorBadRequest).send({ error: "InvalidParams" });
}
const event: StaffEvent = new StaffEvent(eventFormat);
console.log(event, metadata);
newEvent = await Models.StaffEvent.create(event);
} else {
if (!isValidPublicFormat(eventFormat)) {
return res.status(StatusCode.ClientErrorBadRequest).send({ error: "InvalidParams" });
}
const event: PublicEvent = new PublicEvent(eventFormat);
console.log(event, metadata);
newEvent = await Models.PublicEvent.create(event);
}
await Models.EventMetadata.create(metadata);
Expand Down Expand Up @@ -421,11 +418,6 @@ eventsRouter.delete("/:EVENTID/", strongJwtVerification, async (req: Request, re
return res.status(StatusCode.ClientErrorForbidden).send({ error: "InvalidPermission" });
}

// Check if eventid field doesn't exist -> if not, returns error
if (!eventId) {
return res.status(StatusCode.ClientErrorBadRequest).send({ error: "InvalidParams" });
}

// Perform a lazy delete on both databases, and return true if the operation succeeds
await Models.StaffEvent.findOneAndDelete({ eventId: eventId });
await Models.PublicEvent.findOneAndDelete({ eventId: eventId });
Expand Down Expand Up @@ -517,7 +509,7 @@ eventsRouter.put("/metadata/", strongJwtVerification, async (req: Request, res:
metadata,
);

if (!metadata) {
if (!updatedMetadata) {
return res.status(StatusCode.ClientErrorBadRequest).send({ error: "EventNotFound" });
}

Expand Down Expand Up @@ -603,7 +595,6 @@ eventsRouter.put("/", strongJwtVerification, async (req: Request, res: Response)
const eventFormat: GenericEventFormat = req.body as GenericEventFormat;
const eventId: string = eventFormat.eventId;

console.log(eventFormat.eventId);
if (!eventId) {
return res.status(StatusCode.ClientErrorBadRequest).send({ message: "NoEventId" });
}
Expand Down Expand Up @@ -633,14 +624,4 @@ eventsRouter.put("/", strongJwtVerification, async (req: Request, res: Response)
}
});

// Prototype error handler
eventsRouter.use((err: Error, req: Request, res: Response) => {
if (!err) {
return res.status(StatusCode.SuccessOK).send({ status: "OK" });
}

console.error(err.stack, req.body);
return res.status(StatusCode.ServerErrorInternal).send({ error: err.message });
});

export default eventsRouter;
Loading