Skip to content

Commit

Permalink
#3222 - Provide Ministry the ability to edit Basic BCeID Profile Info…
Browse files Browse the repository at this point in the history
… (e2e tests) (#3794)

### As a part of this PR, the following e2e tests were written:

`StudentAESTController(e2e)-updateProfileInformation`
√ Should allow the student profile update when the student is a Basic
BCeID user. (5472 ms)
√ Should throw an HTTP Unprocessable Entity (422) error when the student
is not a Basic BCeID user. (2619 ms)
√ Should throw an HTTP Forbidden (403) error when the ministry user
performing the profile update does not have the associated role. (497
ms)
√ Should throw an HTTP Unprocessable Entity (422) error when the student
is a Basic BCeID user but none of the user profile information that
needs to be updated has changed. (3076 ms)
√ Should throw an HTTP Not Found (404) error when the student does not
exist. (514 ms)
√ Should throw an HTTP Bad Request error when some mandatory profile
update information is missing from the payload. (7 ms)
√ Should allow the student profile update when the student is a Basic
BCeID user and the givenNames is not provided. (4945 ms)

### Screenshot of the passed e2e tests: 

<img width="1244" alt="image"
src="https://github.com/user-attachments/assets/32d19cac-be2c-477c-b7a7-250532132d45">
  • Loading branch information
sh16011993 authored Oct 16, 2024
1 parent 7f6026b commit e79e7df
Show file tree
Hide file tree
Showing 2 changed files with 259 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
import { HttpStatus, INestApplication } from "@nestjs/common";
import * as request from "supertest";
import {
AESTGroups,
BEARER_AUTH_TYPE,
createTestingAppModule,
getAESTToken,
} from "../../../../testHelpers";
import {
E2EDataSources,
createE2EDataSources,
saveFakeStudent,
} from "@sims/test-utils";
import { IdentityProviders } from "@sims/sims-db";
import * as faker from "faker";

interface StudentProfileUpdateInfo {
givenNames: string;
lastName: string;
birthdate: string;
email: string;
noteDescription: string;
}

describe("StudentAESTController(e2e)-updateProfileInformation", () => {
let app: INestApplication;
let db: E2EDataSources;
let studentProfileUpdateInfo: StudentProfileUpdateInfo;

beforeAll(async () => {
const { nestApplication, dataSource } = await createTestingAppModule();
app = nestApplication;
db = createE2EDataSources(dataSource);
studentProfileUpdateInfo = {
givenNames: faker.name.firstName(),
lastName: faker.name.lastName(),
birthdate: "1990-01-15",
email: faker.internet.email(),
noteDescription: faker.datatype.uuid(),
};
});

it("Should allow the ministry user to update a student profile when the student is a Basic BCeID user and the ministry user has the associated role.", async () => {
// Arrange
const student = await saveFakeStudent(db.dataSource);
student.user.identityProviderType = IdentityProviders.BCeIDBasic;
await db.user.save(student.user);
const token = await getAESTToken(AESTGroups.OperationsAdministrators);
const endpoint = `/aest/student/${student.id}`;

// Act/Assert
await request(app.getHttpServer())
.patch(endpoint)
.send(studentProfileUpdateInfo)
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.OK);

const updatedStudent = await db.student.findOne({
select: {
id: true,
birthDate: true,
user: {
id: true,
firstName: true,
lastName: true,
email: true,
createdAt: true,
identityProviderType: true,
isActive: true,
updatedAt: true,
userName: true,
},
sinValidation: { id: true },
},
relations: { user: true, sinValidation: true },
where: {
id: student.id,
notes: { description: studentProfileUpdateInfo.noteDescription },
},
});
expect(updatedStudent).toEqual({
id: updatedStudent.id,
birthDate: studentProfileUpdateInfo.birthdate,
sinValidation: { id: updatedStudent.sinValidation.id },
user: {
// Validating all the properties since eager is set to true for the User in the Student model resulting in all properties being fetched.
email: studentProfileUpdateInfo.email,
firstName: studentProfileUpdateInfo.givenNames,
lastName: studentProfileUpdateInfo.lastName,
id: updatedStudent.user.id,
createdAt: updatedStudent.user.createdAt,
identityProviderType: updatedStudent.user.identityProviderType,
isActive: updatedStudent.user.isActive,
updatedAt: updatedStudent.user.updatedAt,
userName: updatedStudent.user.userName,
},
});
expect(updatedStudent.sinValidation.id).not.toBe(student.sinValidation.id);
});

it("Should throw an HTTP Unprocessable Entity (422) error when the student is not a Basic BCeID user.", async () => {
// Arrange
const student = await saveFakeStudent(db.dataSource);
student.user.identityProviderType = IdentityProviders.BCeIDBusiness;
await db.user.save(student.user);
const token = await getAESTToken(AESTGroups.OperationsAdministrators);
const endpoint = `/aest/student/${student.id}`;

// Act/Assert
await request(app.getHttpServer())
.patch(endpoint)
.send(studentProfileUpdateInfo)
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.UNPROCESSABLE_ENTITY)
.expect({
error: "Unprocessable Entity",
message: "Not possible to update a non-basic-BCeID student.",
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
});
});

it("Should throw an HTTP Forbidden (403) error when the ministry user performing the profile update does not have the associated role.", async () => {
// Arrange
const token = await getAESTToken(AESTGroups.MOFOperations);
const endpoint = "/aest/student/9999";

// Act/Assert
await request(app.getHttpServer())
.patch(endpoint)
.send({})
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.FORBIDDEN)
.expect({
error: "Forbidden",
message: "Forbidden resource",
statusCode: HttpStatus.FORBIDDEN,
});
});

it("Should throw an HTTP Unprocessable Entity (422) error when the student is a Basic BCeID user but none of the user profile information that needs to be updated has changed.", async () => {
// Arrange
const student = await saveFakeStudent(db.dataSource);
student.user.identityProviderType = IdentityProviders.BCeIDBasic;
await db.user.save(student.user);
const studentProfileUpdateInfo = {
givenNames: student.user.firstName?.toUpperCase(),
lastName: student.user.lastName.toUpperCase(),
birthdate: student.birthDate,
email: student.user.email.toUpperCase(),
noteDescription: "Some other dummy note.",
};
const token = await getAESTToken(AESTGroups.BusinessAdministrators);
const endpoint = `/aest/student/${student.id}`;

// Act/Assert
await request(app.getHttpServer())
.patch(endpoint)
.send(studentProfileUpdateInfo)
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.UNPROCESSABLE_ENTITY)
.expect({
error: "Unprocessable Entity",
message: "No profile data updated because no changes were detected.",
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
});
});

it("Should throw an HTTP Not Found (404) error when the student does not exist.", async () => {
// Arrange
const token = await getAESTToken(AESTGroups.BusinessAdministrators);
const endpoint = "/aest/student/9999";

// Act/Assert
await request(app.getHttpServer())
.patch(endpoint)
.send(studentProfileUpdateInfo)
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.NOT_FOUND)
.expect({
error: "Not Found",
message: "Student does not exist.",
statusCode: HttpStatus.NOT_FOUND,
});
});

it("Should throw an HTTP Bad Request error when some mandatory profile update information is missing from the payload.", async () => {
// Arrange
const token = await getAESTToken(AESTGroups.OperationsAdministrators);
const endpoint = "/aest/student/9999";

// Act/Assert
await request(app.getHttpServer())
.patch(endpoint)
.send({
givenNames: faker.name.firstName(),
birthdate: "1990-01-15",
email: faker.internet.email(),
noteDescription: faker.lorem.text(),
})
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.BAD_REQUEST)
.expect({
error: "Bad Request",
message: [
"lastName must be shorter than or equal to 100 characters",
"lastName should not be empty",
],
statusCode: HttpStatus.BAD_REQUEST,
});
});

it("Should allow the student profile update when the student is a Basic BCeID user and the givenNames is not provided.", async () => {
// Arrange
const student = await saveFakeStudent(db.dataSource);
student.user.identityProviderType = IdentityProviders.BCeIDBasic;
await db.user.save(student.user);
const updatedStudentProfile = {
...studentProfileUpdateInfo,
givenNames: null,
};
const token = await getAESTToken(AESTGroups.OperationsAdministrators);
const endpoint = `/aest/student/${student.id}`;

// Act/Assert
await request(app.getHttpServer())
.patch(endpoint)
.send(updatedStudentProfile)
.auth(token, BEARER_AUTH_TYPE)
.expect(HttpStatus.OK);

const updatedStudent = await db.student.findOne({
select: {
id: true,
user: {
firstName: true,
lastName: true,
},
},
relations: { user: true },
where: {
id: student.id,
notes: { description: updatedStudentProfile.noteDescription },
},
});
expect(updatedStudent.user.firstName).toEqual(null);
expect(updatedStudent.user.lastName).toEqual(
updatedStudentProfile.lastName,
);
});

afterAll(async () => {
await app?.close();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -510,9 +510,13 @@ export class StudentService extends RecordDataModelService<Student> {
studentUserData: StudentUserData,
auditUserId: number,
): Promise<boolean> {
// TODO: Remove the trim function below once the DTO sanitization is done.
studentUserData.givenNames = studentUserData.givenNames?.trim();
studentUserData.lastName = studentUserData.lastName.trim();
studentUserData.email = studentUserData.email.trim();
const studentToSync = await this.getStudentById(studentUserData.studentId);
let mustSave = false;
if (!studentUserData.givenNames?.trim()) {
if (!studentUserData.givenNames) {
studentUserData.givenNames = null;
}
if (
Expand Down

0 comments on commit e79e7df

Please sign in to comment.