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

Api unit tests #15048

Merged
merged 37 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2103790
Install vitest
benmartin-coforma Feb 1, 2025
7c3ca91
Fix invocation for jwt-decode
benmartin-coforma Feb 1, 2025
f6bc019
Add tests for authConditions
benmartin-coforma Feb 1, 2025
02d2b67
Add tests for cognito-auth
benmartin-coforma Feb 5, 2025
2d9b0f0
Add mocking package for aws sdk clients
benmartin-coforma Feb 1, 2025
035706a
Add test setup file for environment variables
benmartin-coforma Feb 5, 2025
6ad15fc
Add tests for obtainFormTemplate
benmartin-coforma Feb 5, 2025
84f2558
Add tests for obtainFormTemplateYears
benmartin-coforma Feb 5, 2025
74c7160
Add tests for updateCreateFormTemplate
benmartin-coforma Feb 1, 2025
0cd38f0
Add tests for getFormTypes
benmartin-coforma Feb 1, 2025
d0e589f
Add tests for generateQuarterForms
benmartin-coforma Feb 2, 2025
549e369
Add tests for obtainAvailableForms
benmartin-coforma Feb 2, 2025
1a5fb51
Add tests for obtainFormsList
benmartin-coforma Feb 2, 2025
75a34a6
Add tests for saveForm
benmartin-coforma Feb 3, 2025
8d09d2b
Add tests for forms/get
benmartin-coforma Feb 3, 2025
b497b60
Add tests for sharedFunctions
benmartin-coforma Feb 3, 2025
fa934f4
Add tests for generateEnrollmentTotals
benmartin-coforma Feb 3, 2025
7da5074
Add tests for updateStateForms
benmartin-coforma Feb 3, 2025
f051e05
Add tests for getUserById
benmartin-coforma Feb 3, 2025
60728a6
Add test for getUsername
benmartin-coforma Feb 3, 2025
0b602f2
Add tests for listUsers
benmartin-coforma Feb 3, 2025
b67ca4b
Add missing awaits
benmartin-coforma Feb 3, 2025
b79a49c
Add tests for createUser
benmartin-coforma Feb 3, 2025
f8bfc30
Add tests for deleteUser
benmartin-coforma Feb 3, 2025
4cd940d
Add tests for obtainUserByEmail
benmartin-coforma Feb 3, 2025
813d494
Add tests for obtainUserByUsername
benmartin-coforma Feb 3, 2025
5a5a4de
Add tests for obtainUsernameBySub
benmartin-coforma Feb 3, 2025
53ed6cc
Add tests for updateUser
benmartin-coforma Feb 3, 2025
adec62f
Add tests for authorization
benmartin-coforma Feb 4, 2025
979ccda
Add tests for kafka-source-lib
benmartin-coforma Feb 4, 2025
d41f4d6
Add test for forceKafkaSync
benmartin-coforma Feb 4, 2025
32d1b6c
Add test for postKafkaData
benmartin-coforma Feb 4, 2025
79f8670
Add test for notification/businessUsers
benmartin-coforma Feb 4, 2025
827a5ec
Add test for notification/stateUsers
benmartin-coforma Feb 4, 2025
de8fc4b
Add test for notification/uncertified
benmartin-coforma Feb 4, 2025
8fea79a
Provide a clearer error message when this expectation fails
benmartin-coforma Feb 8, 2025
05c1405
Merge branch 'master' into api-unit-tests
benmartin-coforma Feb 12, 2025
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
114 changes: 114 additions & 0 deletions services/app-api/auth/authConditions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { describe, expect, test, vi } from "vitest";
import {
authorizeAdmin,
authorizeAnyUser,
authorizeAdminOrUserWithEmail,
authorizeAdminOrUserForState,
authorizeUserForState,
authorizeStateUser,
authorizeUserForState
} from "./authConditions.js";
import { getCurrentUserInfo } from "./cognito-auth.js";

vi.mock("./cognito-auth.js", () => ({
getCurrentUserInfo: vi.fn(),
}));

const mockEvent = {};

const stateUserCO = {
email: "[email protected]",
role: "state",
states: ["CO"],
};

const stateUserTX = {
email: "[email protected]",
role: "state",
states: ["TX"],
};

const adminUser = {
email: "[email protected]",
role: "admin",
states: [],
};

const businessUser = {
email: "[email protected]",
role: "business",
states: ["CO", "TX", "etc"],
};

const assertAllow = async (authCall, user) => {
getCurrentUserInfo.mockResolvedValueOnce({ data: user });
await expect(authCall()).resolves.not.toThrow();
};

const assertDeny = async (authCall, user) => {
getCurrentUserInfo.mockResolvedValueOnce({ data: user });
await expect(authCall()).rejects.toThrow("Forbidden");
};

describe("authConditions", () => {
test("authorizeAnyUser should allow the expected users", async () => {
const authCall = () => authorizeAnyUser(mockEvent);
await assertAllow(authCall, adminUser);
await assertAllow(authCall, businessUser);
await assertAllow(authCall, stateUserCO);
await assertAllow(authCall, stateUserTX);
});

test("authorizeAnyUser should reject when a user cannot be found", async () => {
getCurrentUserInfo.mockResolvedValueOnce(undefined);
await expect(authorizeAnyUser(mockEvent)).rejects.toThrow();

getCurrentUserInfo.mockResolvedValueOnce({ data: {} });
await expect(authorizeAnyUser(mockEvent)).rejects.toThrow();
});

test("authorizeAnyUser should reject token decoding fails", async () => {
getCurrentUserInfo.mockImplementationOnce(() => { throw new Error(); });
await expect(authorizeAnyUser(mockEvent)).rejects.toThrow();
});

test("authorizeAdmin should allow the expected users", async () => {
const authCall = () => authorizeAdmin(mockEvent);
await assertAllow(authCall, adminUser);
await assertDeny(authCall, businessUser);
await assertDeny(authCall, stateUserCO);
await assertDeny(authCall, stateUserTX);
});

test("authorizeAdminOrUserWithEmail should allow the expected users", async () => {
const authCall = () => authorizeAdminOrUserWithEmail(mockEvent, "[email protected]");
await assertAllow(authCall, adminUser);
await assertDeny(authCall, businessUser);
await assertAllow(authCall, stateUserCO);
await assertDeny(authCall, stateUserTX);
});

test("authorizeAdminOrUserForState should allow the expected users", async () => {
const authCall = () => authorizeAdminOrUserForState(mockEvent, "CO");
await assertAllow(authCall, adminUser);
await assertAllow(authCall, businessUser);
await assertAllow(authCall, stateUserCO);
await assertDeny(authCall, stateUserTX);
});

test("authorizeStateUser should allow the expected users", async () => {
const authCall = () => authorizeStateUser(mockEvent, "CO");
await assertDeny(authCall, adminUser);
await assertDeny(authCall, businessUser);
await assertAllow(authCall, stateUserCO);
await assertDeny(authCall, stateUserTX);
});

test("authorizeUserForState should allow the expected users", async () => {
const authCall = () => authorizeUserForState(mockEvent, "CO");
await assertDeny(authCall, adminUser);
await assertAllow(authCall, businessUser);
await assertAllow(authCall, stateUserCO);
await assertDeny(authCall, stateUserTX);
});
});
54 changes: 54 additions & 0 deletions services/app-api/auth/cognito-auth.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { describe, expect, it, vi } from "vitest";
import { getCurrentUserInfo } from "./cognito-auth.js";
import { getUserDetailsFromEvent } from "../libs/authorization.js";
import { obtainUserByEmail } from "../handlers/users/post/obtainUserByEmail.js";

vi.mock("../libs/authorization.js", () => ({
getUserDetailsFromEvent: vi.fn(),
}));

vi.mock("../handlers/users/post/obtainUserByEmail.js", () => ({
obtainUserByEmail: vi.fn(),
}));

const mockEvent = {};
const mockUser = {
email: "[email protected]",
role: "state",
states: ["CO"],
};

describe("getCurrentUserInfo", () => {
it("should return the user if they can be found", async () => {
getUserDetailsFromEvent.mockResolvedValueOnce({ email: mockUser.email });
obtainUserByEmail.mockResolvedValueOnce({ Items: [mockUser] });

const response = await getCurrentUserInfo(mockEvent);

expect(response).toEqual({
status: "success",
data: mockUser,
});
expect(obtainUserByEmail).toHaveBeenCalledWith(mockUser.email);
});

it("should return a shell user if none can be found", async () => {
getUserDetailsFromEvent.mockResolvedValueOnce({ email: mockUser.email });
obtainUserByEmail.mockResolvedValueOnce(undefined);

const response = await getCurrentUserInfo(mockEvent);

expect(response).toEqual({
data: {
email: mockUser.email,
}
});
expect(obtainUserByEmail).toHaveBeenCalledWith(mockUser.email);
});

it("should throw if token cannot be decoded", async () => {
getUserDetailsFromEvent.mockRejectedValueOnce("nope");

await expect(getCurrentUserInfo(mockEvent)).rejects.toThrow();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { main as obtainFormTemplate } from "./obtainFormTemplate.js";
import { authorizeAdmin } from "../../../auth/authConditions.js";
import {
DynamoDBDocumentClient,
QueryCommand,
} from "@aws-sdk/lib-dynamodb";
import { mockClient } from "aws-sdk-client-mock";

vi.mock("../../../auth/authConditions.js", () => ({
authorizeAdmin: vi.fn(),
}));

const mockDynamo = mockClient(DynamoDBDocumentClient);

const mockEvent = {
body: JSON.stringify({ year: 2025 }),
};

const mockFormTemplate = {
mockProp: "mockValue",
};

describe("obtainFormTemplate.js", () => {
beforeEach(() => {
mockDynamo.reset();
});

it("should query dynamo for the appropriate data", async () => {
const mockQuery = vi.fn().mockResolvedValueOnce({
Count: 1,
Items: [mockFormTemplate],
});
mockDynamo.on(QueryCommand).callsFakeOnce(mockQuery);

const response = await obtainFormTemplate(mockEvent);

expect(response).toEqual(expect.objectContaining({
statusCode: 200,
body: JSON.stringify([mockFormTemplate]),
}));
expect(mockQuery).toHaveBeenCalledWith({
TableName: "local-form-templates",
ExpressionAttributeNames: { "#theYear": "year" },
ExpressionAttributeValues: { ":year": 2025 },
KeyConditionExpression: "#theYear = :year",
}, expect.any(Function));
});

it("should return Not Found if there are no results", async () => {
const mockQuery = vi.fn().mockResolvedValueOnce({ Count: 0 });
mockDynamo.on(QueryCommand).callsFakeOnce(mockQuery);

const response = await obtainFormTemplate(mockEvent);

expect(response).toEqual(expect.objectContaining({
statusCode: 200,
body: JSON.stringify({
status: 404,
message: "Could not find form template for year: 2025",
}),
}));
});

it("should return Internal Server Error if the user is not an admin", async () => {
authorizeAdmin.mockRejectedValueOnce(new Error("Forbidden"));

const response = await obtainFormTemplate(mockEvent);

expect(response).toEqual(expect.objectContaining({
statusCode: 500,
body: JSON.stringify({ error: "Forbidden" }),
}));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { main as obtainFormTemplateYears } from "./obtainFormTemplateYears.js";
import { authorizeAdmin } from "../../../auth/authConditions.js";
import {
DynamoDBDocumentClient,
ScanCommand,
} from "@aws-sdk/lib-dynamodb";
import { mockClient } from "aws-sdk-client-mock";

vi.mock("../../../auth/authConditions.js", () => ({
authorizeAdmin: vi.fn(),
}));

const mockDynamo = mockClient(DynamoDBDocumentClient);

const mockEvent = {};

describe("obtainFormTemplateYears.js", () => {
beforeEach(() => {
mockDynamo.reset();
});

it("should query dynamo for the appropriate data", async () => {
const mockScan = vi.fn().mockResolvedValueOnce({
Count: 1,
Items: [{ year: 2024 }, { year: 2023 }, { year: 2025 }],
});
mockDynamo.on(ScanCommand).callsFakeOnce(mockScan);

const response = await obtainFormTemplateYears(mockEvent);

expect(response).toEqual(expect.objectContaining({
statusCode: 200,
// Note the sort order: most recent year first
body: JSON.stringify([2025, 2024, 2023]),
}));
expect(mockScan).toHaveBeenCalledWith({
TableName: "local-form-templates",
ExpressionAttributeNames: { "#theYear": "year" },
ProjectionExpression: "#theYear",
}, expect.any(Function));
});

it("should return Not Found if there are no results", async () => {
const mockScan = vi.fn().mockResolvedValueOnce({ Count: 0 });
mockDynamo.on(ScanCommand).callsFakeOnce(mockScan);

const response = await obtainFormTemplateYears(mockEvent);

expect(response).toEqual(expect.objectContaining({
statusCode: 200,
body: JSON.stringify([]),
}));
});

it("should return Internal Server Error if the user is not an admin", async () => {
authorizeAdmin.mockRejectedValueOnce(new Error("Forbidden"));

const response = await obtainFormTemplateYears(mockEvent);

expect(response).toEqual(expect.objectContaining({
statusCode: 500,
body: JSON.stringify({ error: "Forbidden" }),
}));
});
});
Loading