Skip to content

Commit

Permalink
merge from dev
Browse files Browse the repository at this point in the history
  • Loading branch information
curtwl committed Jun 17, 2024
2 parents e3a39d4 + 07cfa31 commit c8f8aeb
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Another example [here](https://co-pilot.dev/changelog)
- Add e2e tests for teams controller ([#162](https://github.com/chingu-x/chingu-dashboard-be/pull/162))
- Add swagger access info, add forms authorization and e2e tests ([#160](https://github.com/chingu-x/chingu-dashboard-be/pull/160))
- Add voyages unit test, also had to update all files (seed, tests, services) to meet strict null rule due to prismaMock requirements ([#163](https://github.com/chingu-x/chingu-dashboard-be/pull/163))
- Add e2e tests for users controller ([#165](https://github.com/chingu-x/chingu-dashboard-be/pull/165))
- Add GET endpoint for check-in form responses ([#166](https://github.com/chingu-x/chingu-dashboard-be/pull/166))


Expand Down
10 changes: 10 additions & 0 deletions src/teams/teams.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ export class TeamsController {
description: "Voyage with given ID does not exist.",
type: NotFoundErrorResponse,
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: "unauthorized access - user is not logged in",
type: UnauthorizedErrorResponse,
})
@ApiResponse({
status: HttpStatus.FORBIDDEN,
description: "forbidden - user does not have the required permission",
type: ForbiddenErrorResponse,
})
@ApiParam({
name: "voyageId",
description: "voyage id from the voyage table",
Expand Down
31 changes: 31 additions & 0 deletions src/users/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ApiOperation, ApiParam, ApiResponse, ApiTags } from "@nestjs/swagger";
import { FullUserResponse, PrivateUserResponse } from "./users.response";
import {
BadRequestErrorResponse,
ForbiddenErrorResponse,
NotFoundErrorResponse,
UnauthorizedErrorResponse,
} from "../global/responses/errors";
Expand All @@ -40,6 +41,16 @@ export class UsersController {
isArray: true,
type: FullUserResponse,
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: "unauthorized access - user is not logged in",
type: UnauthorizedErrorResponse,
})
@ApiResponse({
status: HttpStatus.FORBIDDEN,
description: "forbidden - user does not have the required permission",
type: ForbiddenErrorResponse,
})
@CheckAbilities({ action: Action.Manage, subject: "all" })
@Get()
findAll() {
Expand Down Expand Up @@ -91,6 +102,16 @@ export class UsersController {
description: "UserId is not a valid UUID",
type: BadRequestErrorResponse,
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: "unauthorized access - user is not logged in",
type: UnauthorizedErrorResponse,
})
@ApiResponse({
status: HttpStatus.FORBIDDEN,
description: "forbidden - user does not have the required permission",
type: ForbiddenErrorResponse,
})
@ApiParam({
name: "userId",
required: true,
Expand Down Expand Up @@ -125,6 +146,16 @@ export class UsersController {
description: "Given email is not in a valid email syntax.",
type: BadRequestErrorResponse,
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: "unauthorized access - user is not logged in",
type: UnauthorizedErrorResponse,
})
@ApiResponse({
status: HttpStatus.FORBIDDEN,
description: "forbidden - user does not have the required permission",
type: ForbiddenErrorResponse,
})
@CheckAbilities({ action: Action.Manage, subject: "all" })
@HttpCode(200)
@Post("/lookup-by-email")
Expand Down
230 changes: 230 additions & 0 deletions test/users.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication, ValidationPipe } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
import { PrismaService } from "../src/prisma/prisma.service";
import { seed } from "../prisma/seed/seed";
import { getUseridFromEmail, loginAndGetTokens } from "./utils";
import * as cookieParser from "cookie-parser";
import { CASLForbiddenExceptionFilter } from "src/exception-filters/casl-forbidden-exception.filter";

//Logged in user is Jessica Williamson for admin routes
//Logged in user is Dan ko for non admin routes

describe("Users Controller (e2e)", () => {
let app: INestApplication;
let prisma: PrismaService;
let userId: string | undefined;

const userEmail: string = "leo.rowe@outlook.com";

beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();

await seed();
app = moduleFixture.createNestApplication();
prisma = moduleFixture.get<PrismaService>(PrismaService);
app.useGlobalPipes(new ValidationPipe());
app.useGlobalFilters(new CASLForbiddenExceptionFilter());
app.use(cookieParser());
await app.init();
});

afterAll(async () => {
await prisma.$disconnect();
await app.close();
});

describe("GET /users - [ role - Admin ] - gets all users", () => {
it("should return 200 and array of users", async () => {
const { access_token, refresh_token } = await loginAndGetTokens(
"jessica.williamson@gmail.com",
"password",
app,
);
await request(app.getHttpServer())
.get("/users")
.set("Cookie", [access_token, refresh_token])
.expect(200)
.expect("Content-Type", /json/)
.expect((res) => {
expect(res.body).toBeArray;
});
});
it("should return 401 when user is not logged in", async () => {
await request(app.getHttpServer())
.get("/users")
.set("Authorization", `Bearer ${undefined}`)
.expect(401)
.expect("Content-Type", /json/);
});
it("should return 403 when non-admin user tries to access it", async () => {
const { access_token, refresh_token } = await loginAndGetTokens(
"dan@random.com",
"password",
app,
);
await request(app.getHttpServer())
.get("/users")
.set("Cookie", [access_token, refresh_token])
.expect(403)
.expect("Content-Type", /json/);
});
});
describe("GET /users/me - get logged in users own details", () => {
it("should return 200 and array of users", async () => {
const { access_token, refresh_token } = await loginAndGetTokens(
"jessica.williamson@gmail.com",
"password",
app,
);
await request(app.getHttpServer())
.get("/users/me")
.set("Cookie", [access_token, refresh_token])
.expect(200)
.expect("Content-Type", /json/)
.expect((res) => {
expect(res.body).toBeObject;
});
});
it("should return 401 when user is not logged in", async () => {
await request(app.getHttpServer())
.get("/users/me")
.set("Authorization", `Bearer ${undefined}`)
.expect(401)
.expect("Content-Type", /json/);
});
});
describe("GET /users/:userId - [ role - Admin ] - gets a user with full deatils given a user id", () => {
it("should return 200 and a object containing user details", async () => {
const { access_token, refresh_token } = await loginAndGetTokens(
"jessica.williamson@gmail.com",
"password",
app,
);
userId = await getUseridFromEmail("leo.rowe@outlook.com");
await request(app.getHttpServer())
.get(`/users/${userId}`)
.set("Cookie", [access_token, refresh_token])
.expect(200)
.expect("Content-Type", /json/)
.expect((res) => {
expect(res.body.email).toEqual("leo.rowe@outlook.com");
});
});
it("should return 400 for a invalid UUID", async () => {
const invalidUUID = "dd7851f9-12aa-e098a9df380c";
const { access_token, refresh_token } = await loginAndGetTokens(
"jessica.williamson@gmail.com",
"password",
app,
);
await request(app.getHttpServer())
.get(`/users/${invalidUUID}`)
.set("Cookie", [access_token, refresh_token])
.expect(400)
.expect("Content-Type", /json/);
});
it("should return 401 when user is not logged in", async () => {
await request(app.getHttpServer())
.get(`/users/${userId}`)
.set("Authorization", `Bearer ${undefined}`)
.expect(401)
.expect("Content-Type", /json/);
});
it("should return 403 when non-admin user tries to access it", async () => {
const { access_token, refresh_token } = await loginAndGetTokens(
"dan@random.com",
"password",
app,
);
await request(app.getHttpServer())
.get(`/users/${userId}`)
.set("Cookie", [access_token, refresh_token])
.expect(403)
.expect("Content-Type", /json/);
});
it("should return 404 if a user is not found for a given user id", async () => {
const invalidUUID = "dd7851f9-12aa-47c9-a06f-e098a9df380c";
const { access_token, refresh_token } = await loginAndGetTokens(
"jessica.williamson@gmail.com",
"password",
app,
);
await request(app.getHttpServer())
.get(`/users/${invalidUUID}`)
.set("Cookie", [access_token, refresh_token])
.expect(404)
.expect("Content-Type", /json/);
});
});
describe("GET /users/lookup-by-email - [ role - Admin ] - gets a user with full deatils given a user email", () => {
it("should return 200 and a object containing the user details", async () => {
const { access_token, refresh_token } = await loginAndGetTokens(
"jessica.williamson@gmail.com",
"password",
app,
);
await request(app.getHttpServer())
.post("/users/lookup-by-email")
.set("Cookie", [access_token, refresh_token])
.send({ email: userEmail })
.expect(200)
.expect("Content-Type", /json/)
.expect((res) => {
expect(res.body.email).toEqual("leo.rowe@outlook.com");
});
});
it("should return 404 if the is not found for the given email id", async () => {
const notFoundEmail: string = "notfound@gmail.com";
const { access_token, refresh_token } = await loginAndGetTokens(
"jessica.williamson@gmail.com",
"password",
app,
);
await request(app.getHttpServer())
.post("/users/lookup-by-email")
.set("Cookie", [access_token, refresh_token])
.send({ email: notFoundEmail })
.expect(404)
.expect("Content-Type", /json/);
});
it("should return 400 if invalid email syntax is provided", async () => {
const invalidEmail = "invalid.com";
const { access_token, refresh_token } = await loginAndGetTokens(
"jessica.williamson@gmail.com",
"password",
app,
);
await request(app.getHttpServer())
.post("/users/lookup-by-email")
.set("Cookie", [access_token, refresh_token])
.send({ email: invalidEmail })
.expect(400)
.expect("Content-Type", /json/);
});
it("should return 401 when user is not logged in", async () => {
await request(app.getHttpServer())
.post("/users/lookup-by-email")
.set("Authorization", `Bearer ${undefined}`)
.send({ email: userEmail })
.expect(401)
.expect("Content-Type", /json/);
});
it("should return 403 when non-admin user tries to access it", async () => {
const { access_token, refresh_token } = await loginAndGetTokens(
"dan@random.com",
"password",
app,
);
await request(app.getHttpServer())
.post("/users/lookup-by-email")
.set("Cookie", [access_token, refresh_token])
.send({ email: userEmail })
.expect(403)
.expect("Content-Type", /json/);
});
});
});
22 changes: 22 additions & 0 deletions test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,25 @@ export const getNonAdminUser = async () => {
);
return adminUser;
};
export const getUseridFromEmail = async (
email: string,
): Promise<string | undefined> => {
try {
const user = await prisma.user.findUnique({
where: {
email,
},
select: {
id: true,
},
});
if (!user) {
throw new InternalServerErrorException(
"test/utils.ts: user not found",
);
}
return user.id;
} catch (e) {
console.log(e);
}
};

0 comments on commit c8f8aeb

Please sign in to comment.