Skip to content

Commit

Permalink
wip: new tests added and fixed broken tests
Browse files Browse the repository at this point in the history
Refs: BLAIS5-4525
  • Loading branch information
SidraJaved committed Dec 24, 2024
1 parent 202a739 commit f258831
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 58 deletions.
10 changes: 5 additions & 5 deletions server/routes/blaiseApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,17 @@ export default function blaiseApi(config: CustomConfig, auth: Auth, blaiseApiCli

router.post("/api/change-password/:user", auth.Middleware, async function (req: Request, res: Response) {
const currentUser = auth.GetUser(auth.GetToken(req));
let { password } = req.body.password;
const data = req.body;

if (Array.isArray(password)) {
password = password.join("");
if (Array.isArray(data.password)) {
data.password = data.password.join("");
}

if (!req.params.user || !password) {
if (!req.params.user || !data.password) {
return res.status(400).json("No user or password provided");
}

blaiseApiClient.changePassword(req.params.user, password).then(() => {
blaiseApiClient.changePassword(req.params.user, data.password).then(() => {
auditLogger.info(req.log, `${currentUser.name || "Unknown"} has successfully changed the password for ${req.params.user}`);
return res.status(204).json(null);
}).catch((error: unknown) => {
Expand Down
50 changes: 23 additions & 27 deletions server/tests/routes/blaiseApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,14 @@ describe("POST /api/users endpoint", () => {
it("should call Blaise API createUser endpoint with correct serverParks for each role EXISTING in server/role-to-serverparks-map.json AND return http status OK_200", async () => {
let currentRoleNo = 0;
const totalRoleCount = size(role_to_serverparks_map);
for (const roleName in role_to_serverparks_map)
{
for (const roleName in role_to_serverparks_map) {
logInfo.mockReset();
blaiseApiMock.reset();
console.log("Running for role %i of %i: %s", ++currentRoleNo, totalRoleCount, roleName);

const spmap = role_to_serverparks_map[roleName];
const newUser : NewUser = {
name: "name1",
const newUser: NewUser = {
name: "name1",
password: "password1",
role: roleName,
serverParks: spmap,
Expand All @@ -78,7 +77,7 @@ describe("POST /api/users endpoint", () => {
expect(logInfo.mock.calls[0][0]).toEqual(`AUDIT_LOG: ${mockUser.name} has successfully created user, ${newUser.name}, with an assigned role of ${roleName}`);
expect(response.statusCode).toEqual(200);
blaiseApiMock.verify(a => a.createUser(It.is<NewUser>(
x=> x.defaultServerPark == newUser.defaultServerPark
x => x.defaultServerPark == newUser.defaultServerPark
&& x.role == newUser.role
&& Array.isArray(x.serverParks) && x.serverParks.every(item => typeof item === "string")
&& x.serverParks.every((val, idx) => val === newUser.serverParks[idx])
Expand All @@ -90,8 +89,8 @@ describe("POST /api/users endpoint", () => {
it("should call Blaise API createUser endpoint with DEFAULT serverParks for a role MISSING in server/role-to-serverparks-map.json AND return http status OK_200)", async () => {
const roleName = "this role is missing in server/role-to-serverparks-map.json file";
const spmap = role_to_serverparks_map.DEFAULT;
const newUser : NewUser = {
name: "name1",
const newUser: NewUser = {
name: "name1",
password: "password1",
role: roleName,
serverParks: spmap,
Expand All @@ -108,7 +107,7 @@ describe("POST /api/users endpoint", () => {
expect(log).toEqual(`AUDIT_LOG: ${mockUser.name} has successfully created user, ${newUser.name}, with an assigned role of ${roleName}`);
expect(response.statusCode).toEqual(200);
blaiseApiMock.verify(a => a.createUser(It.is<NewUser>(
x=> x.defaultServerPark == newUser.defaultServerPark
x => x.defaultServerPark == newUser.defaultServerPark
&& x.role == newUser.role
&& Array.isArray(x.serverParks) && x.serverParks.every(item => typeof item === "string")
&& x.serverParks.every((val, idx) => val === newUser.serverParks[idx])
Expand All @@ -133,8 +132,8 @@ describe("POST /api/users endpoint", () => {
it("should return http status INTERNAL_SERVER_ERROR_500 if Blaise API client throws an error", async () => {
const roleName = "IPS Manager";
const spmap = role_to_serverparks_map.DEFAULT;
const newUser : NewUser = {
name: "name1",
const newUser: NewUser = {
name: "name1",
password: "password1",
role: roleName,
serverParks: spmap,
Expand Down Expand Up @@ -215,8 +214,8 @@ describe("GET /api/users endpoint", () => {
});

it("should call Blaise API getUsers endpoint AND return http status OK_200", async () => {
const newUser1 : NewUser = {
name: "name1",
const newUser1: NewUser = {
name: "name1",
password: "password1",
role: "role1",
serverParks: ["sp1", "sp2"],
Expand All @@ -226,7 +225,7 @@ describe("GET /api/users endpoint", () => {
newUser2.name = "name2";
const newUser3 = newUser2;
newUser3.name = "name3";
const userArray : NewUser [] = [newUser1, newUser2, newUser3];
const userArray: NewUser[] = [newUser1, newUser2, newUser3];
blaiseApiMock.setup((api) => api.getUsers()).returns(_ => Promise.resolve(userArray));

const response = await sut.get("/api/users")
Expand All @@ -243,16 +242,16 @@ describe("GET /api/roles endpoint", () => {
});

it("should call Blaise API getUserRoles endpoint AND return http status OK_200", async () => {
const userRole1 : UserRole = {
name: "name1",
const userRole1: UserRole = {
name: "name1",
description: "desc1",
permissions: ["perm1", "perm2"]
};
const userRole2 = userRole1;
userRole2.name = "name2";
const userRole3 = userRole2;
userRole3.name = "name3";
const userRoleArray : UserRole [] = [userRole1, userRole2, userRole3];
const userRoleArray: UserRole[] = [userRole1, userRole2, userRole3];
blaiseApiMock.setup((api) => api.getUserRoles()).returns(_ => Promise.resolve(userRoleArray));

const response = await sut.get("/api/roles")
Expand All @@ -263,24 +262,22 @@ describe("GET /api/roles endpoint", () => {
});
});

describe("GET /api/change-password/:user endpoint", () => {
describe("POST /api/change-password/:user endpoint", () => {
beforeEach(() => {
blaiseApiMock.reset();
jest.clearAllMocks();
});

afterAll(() => {
blaiseApiMock.reset();
});

it("should call Blaise API changePassword endpoint for VALID request AND return http status NO_CONTENT_204", async () => {
const username = "user1";
const password = "password-1234";
blaiseApiMock.setup((api) => api.changePassword(It.isAnyString(), It.isAnyString())).returns(_ => Promise.resolve(null));

const response = await sut.get("/api/change-password/"+username)
const response = await sut.post(`/api/change-password/${username}`)
.set("Authorization", `${mockAuthToken}`)
.set("password", password);
.field("password", password);

const log = logInfo.mock.calls[0][0];
expect(log).toEqual(`AUDIT_LOG: ${mockUser.name} has successfully changed the password for ${username}`);
Expand All @@ -292,9 +289,9 @@ describe("GET /api/change-password/:user endpoint", () => {
const username = "user1";
const password = "";

const response = await sut.get("/api/change-password/"+ username)
const response = await sut.post(`/api/change-password/${username}`)
.set("Authorization", `${mockAuthToken}`)
.set("password", password);
.field("password", password);

expect(response.statusCode).toEqual(400);
blaiseApiMock.verify(a => a.changePassword(It.isAnyString(), It.isAnyString()), Times.never());
Expand All @@ -304,12 +301,11 @@ describe("GET /api/change-password/:user endpoint", () => {
const username = "user1";
const password = "password-1234";
const errorMessage = "Error occured when calling changePassword on Blaise API Rest Service";
blaiseApiMock.setup((a) => a.changePassword(It.isAnyString(), It.isAnyString()))
.returns(_ => Promise.reject(errorMessage));
blaiseApiMock.setup((api) => api.changePassword(It.isAnyString(), It.isAnyString())).returns(_ => Promise.reject(errorMessage));

const response = await sut.get("/api/change-password/"+ username)
const response = await sut.post(`/api/change-password/${username}`)
.set("Authorization", `${mockAuthToken}`)
.set("password", password);
.field("password", password);

const log = logError.mock.calls[0][0];
expect(log).toEqual(`AUDIT_LOG: Error whilst trying to change password for ${username}: ${errorMessage}`);
Expand Down
4 changes: 2 additions & 2 deletions src/api/http/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ async function patchUserRolesAndPermissions(user: string, role: string): Promise
}
}

function updatePassword(username: string, newPassword: string): Promise<boolean> {
function editPassword(username: string, newPassword: string): Promise<boolean> {
const url = "/api/change-password/" + username;
const authManager = new AuthManager();
const headers = authManager.authHeader();
Expand All @@ -131,4 +131,4 @@ function updatePassword(username: string, newPassword: string): Promise<boolean>
});
}

export { getAllUsers, getUser, addNewUser, deleteUser, patchUserRolesAndPermissions, updatePassword };
export { getAllUsers, getUser, addNewUser, deleteUser, patchUserRolesAndPermissions, editPassword };
103 changes: 81 additions & 22 deletions src/pages/users/UserProfileEdits/ChangePassword.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React from "react";
import { render, cleanup } from "@testing-library/react";
import { render, screen, cleanup } from "@testing-library/react";
import "@testing-library/jest-dom";
import { MemoryRouter, useParams } from "react-router-dom";
import { MemoryRouter, Route, Routes, useParams } from "react-router-dom";
import userEvent from "@testing-library/user-event";
import { act } from "react-dom/test-utils";
import ChangePassword from "./ChangePassword";
import { editPassword } from "../../../api/http/users";
import { AuthManager } from "blaise-login-react/blaise-login-react-client";
import UserProfile from "./UserProfile";

jest.mock("react-router-dom", () => ({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand All @@ -21,12 +24,12 @@ jest.mock("blaise-login-react/blaise-login-react-client", () => ({
}))
}));

global.fetch = jest.fn(() =>
Promise.resolve({
status: 204,
json: () => Promise.resolve({ message: "Password changed successfully" })
})
) as jest.Mock;
jest.mock("../../../api/http/users", () => ({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
...jest.requireActual("../../../api/http/users"),
editPassword: jest.fn()
}));

const mockUserDetails = {
name: "testUser"
Expand All @@ -38,7 +41,7 @@ const mockState = {
};

beforeEach(() => {
(fetch as jest.Mock).mockClear();
(editPassword as jest.Mock).mockClear();
(useParams as jest.Mock).mockReturnValue({ user: mockUserDetails.name });
});

Expand Down Expand Up @@ -98,7 +101,7 @@ describe("ChangePassword Component", () => {
expect(await findByText(/Passwords do not match/i)).toBeVisible();
});

it("calls fetch with correct parameters upon form submission with matching passwords that remove any trailing whitespaces", async () => {
it("calls editPassword function with correct parameters upon form submission with correct username and password without any trailing whitespaces", async () => {
const { getByLabelText, getByText } = render(
<MemoryRouter initialEntries={[mockState]}>
<ChangePassword />
Expand All @@ -116,15 +119,10 @@ describe("ChangePassword Component", () => {
userEvent.click(saveButton);
});

expect(fetch).toHaveBeenCalledWith("/api/change-password/testUser", {
headers: {
Authorization: process.env.MOCK_AUTH_TOKEN,
password: "password123"
}
});
expect(editPassword).toHaveBeenCalledWith("testUser", "password123");
});

it("calls fetch with correct parameters upon form submission with matching passwords", async () => {
it("calls editPassword function with correct parameters upon form submission with correct username and password", async () => {
const { getByLabelText, getByText, findByText } = render(
<MemoryRouter initialEntries={[mockState]}>
<ChangePassword />
Expand All @@ -142,11 +140,72 @@ describe("ChangePassword Component", () => {
userEvent.click(saveButton);
});

expect(fetch).toHaveBeenCalledWith("/api/change-password/testUser", {
headers: {
Authorization: process.env.MOCK_AUTH_TOKEN,
password: "password123"
}
const url = "/api/change-password/testUser";

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused variable url.
const authManager = new AuthManager();
const headers = authManager.authHeader();

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused variable headers.
const formData = new FormData();
formData.append("password", "password123");

expect(editPassword).toHaveBeenCalledWith("testUser", "password123");
});

it("displays error message if the function returns false", async () => {
const { getByLabelText, getByText, findByText } = render(
<MemoryRouter initialEntries={[mockState]}>
<ChangePassword />
</MemoryRouter>
);

const newPasswordInput = getByLabelText("New password");
const confirmPasswordInput = getByLabelText("Confirm password");
const saveButton = getByText("Save");

(editPassword as jest.Mock).mockResolvedValue(false);
act(() => {
userEvent.type(newPasswordInput, "password123");
userEvent.type(confirmPasswordInput, "password123");
userEvent.click(saveButton);
});

expect(editPassword).toHaveBeenCalledWith("testUser", "password123");
expect(await findByText(/Set password failed/i)).toBeVisible();
});

it("displays success message if the function returns true", async () => {

const initalPath = `/users/${mockUserDetails.name}/change-password`;
const destinationPath = `/users/${mockUserDetails.name}`;

const { getByLabelText, getByText, findByText } = render(
<MemoryRouter initialEntries={[mockState]}>
<Routes>
<Route
path={initalPath}
element={<ChangePassword />}
/>
<Route
path={destinationPath}
element={
<UserProfile />
}
/>
</Routes>
</MemoryRouter>
);

const newPasswordInput = getByLabelText("New password");
const confirmPasswordInput = getByLabelText("Confirm password");
const saveButton = getByText("Save");

(editPassword as jest.Mock).mockResolvedValue(true);
act(() => {
userEvent.type(newPasswordInput, "password123");
userEvent.type(confirmPasswordInput, "password123");
userEvent.click(saveButton);
});
expect(await findByText(/Loading/i)).toBeVisible();
expect(editPassword).toHaveBeenCalledWith("testUser", "password123");
expect(screen.getByText("Password successfully changed for user called testUser")).toBeInTheDocument();
});

});
4 changes: 2 additions & 2 deletions src/pages/users/UserProfileEdits/ChangePassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AuthManager } from "blaise-login-react/blaise-login-react-client";
import { UserRouteParams } from "../../../Interfaces/usersPage";
import Breadcrumbs from "../../../Components/Breadcrumbs";
import UserSignInErrorPanel from "../../../Components/UserSignInErrorPanel";
import { updatePassword } from "../../../api/http";
import { editPassword } from "../../../api/http/users";

export default function ChangePassword(): ReactElement {
const { user: viewedUsername }: UserRouteParams = useParams() as unknown as UserRouteParams;
Expand All @@ -33,7 +33,7 @@ export default function ChangePassword(): ReactElement {

setButtonLoading(true);

const updated = await updatePassword(viewedUsername, sanitisedPassword);
const updated = await editPassword(viewedUsername, sanitisedPassword);

if (!updated) {
setMessage("Set password failed");
Expand Down

0 comments on commit f258831

Please sign in to comment.