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

Test/e2e tests for users #165

Merged
merged 14 commits into from
Jun 16, 2024
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +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))

### Changed

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);
}
};
Loading