Files
prowler/ui/actions/providers/providers.test.ts
T

238 lines
6.4 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const {
fetchMock,
getAuthHeadersMock,
getFormValueMock,
handleApiErrorMock,
handleApiResponseMock,
waitMock,
} = vi.hoisted(() => ({
fetchMock: vi.fn(),
getAuthHeadersMock: vi.fn(),
getFormValueMock: vi.fn(),
handleApiErrorMock: vi.fn(),
handleApiResponseMock: vi.fn(),
waitMock: vi.fn(),
}));
vi.mock("next/cache", () => ({
revalidatePath: vi.fn(),
}));
vi.mock("next/navigation", () => ({
redirect: vi.fn(),
}));
vi.mock("@/lib", () => ({
apiBaseUrl: "https://api.example.com/api/v1",
getAuthHeaders: getAuthHeadersMock,
getFormValue: getFormValueMock,
wait: waitMock,
}));
vi.mock("@/lib/provider-credentials/build-credentials", () => ({
buildSecretConfig: vi.fn(() => ({
secretType: "access-secret-key",
secret: { key: "value" },
})),
}));
vi.mock("@/lib/provider-filters", () => ({
appendSanitizedProviderInFilters: vi.fn(),
}));
vi.mock("@/lib/server-actions-helper", () => ({
handleApiError: handleApiErrorMock,
handleApiResponse: handleApiResponseMock,
}));
import {
addCredentialsProvider,
addProvider,
checkConnectionProvider,
startProviderConnectionChecks,
updateCredentialsProvider,
} from "./providers";
describe("providers actions", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.stubGlobal("fetch", fetchMock);
getAuthHeadersMock.mockResolvedValue({ Authorization: "Bearer token" });
getFormValueMock.mockImplementation((formData: FormData, field: string) =>
formData.get(field),
);
handleApiErrorMock.mockReturnValue({ error: "Unexpected error" });
handleApiResponseMock.mockResolvedValue({ data: { id: "secret-1" } });
fetchMock.mockResolvedValue(
new Response(JSON.stringify({ data: { id: "secret-1" } }), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
);
});
it("should revalidate providers after linking a cloud provider", async () => {
// Given
const formData = new FormData();
formData.set("providerType", "aws");
formData.set("providerUid", "111111111111");
// When
await addProvider(formData);
// Then
expect(handleApiResponseMock).toHaveBeenCalledWith(
expect.any(Response),
"/providers",
);
});
it("should revalidate providers after adding credentials in the wizard", async () => {
// Given
const formData = new FormData();
formData.set("providerId", "provider-1");
formData.set("providerType", "aws");
// When
await addCredentialsProvider(formData);
// Then
expect(handleApiResponseMock).toHaveBeenCalledWith(
expect.any(Response),
"/providers",
);
});
it("should revalidate providers after updating credentials in the wizard", async () => {
// Given
const formData = new FormData();
formData.set("providerId", "provider-1");
formData.set("providerType", "oraclecloud");
// When
await updateCredentialsProvider("secret-1", formData);
// Then
expect(handleApiResponseMock).toHaveBeenCalledWith(
expect.any(Response),
"/providers",
);
});
it("should revalidate providers when checking connection from the wizard", async () => {
// Given
const formData = new FormData();
formData.set("providerId", "provider-1");
// When
await checkConnectionProvider(formData);
// Then
expect(handleApiResponseMock).toHaveBeenCalledWith(
expect.any(Response),
"/providers",
);
});
});
describe("startProviderConnectionChecks", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.stubGlobal("fetch", fetchMock);
getAuthHeadersMock.mockResolvedValue({ Authorization: "Bearer token" });
handleApiErrorMock.mockReturnValue({ error: "Unexpected error" });
fetchMock.mockResolvedValue(new Response(null, { status: 202 }));
});
it("dispatches one check per provider and returns the task each is tested by", async () => {
// Given
handleApiResponseMock.mockImplementation(async () => ({
data: { id: `task-${handleApiResponseMock.mock.calls.length}` },
}));
// When
const outcomes = await startProviderConnectionChecks([
"provider-1",
"provider-2",
]);
// Then
expect(fetchMock).toHaveBeenCalledTimes(2);
expect(Object.keys(outcomes).sort()).toEqual(["provider-1", "provider-2"]);
expect(Object.values(outcomes).map((outcome) => outcome.taskId)).toEqual(
expect.arrayContaining(["task-1", "task-2"]),
);
});
it("skips the single-provider padding and the per-provider revalidation", async () => {
// Given
handleApiResponseMock.mockResolvedValue({ data: { id: "task-1" } });
// When
await startProviderConnectionChecks(["provider-1", "provider-2"]);
// Then
expect(waitMock).not.toHaveBeenCalled();
expect(handleApiResponseMock).toHaveBeenCalledWith(
expect.any(Response),
undefined,
);
});
it("keeps a failed dispatch to its own provider, error payload included", async () => {
// Given
const failure = { error: "Provider not found.", status: 404 };
handleApiResponseMock
.mockResolvedValueOnce({ data: { id: "task-1" } })
.mockResolvedValueOnce(failure);
// When
const outcomes = await startProviderConnectionChecks([
"provider-1",
"provider-2",
]);
// Then
expect(outcomes["provider-1"]).toEqual({ taskId: "task-1" });
expect(outcomes["provider-2"]).toEqual({ error: failure });
});
it("keeps a thrown request to its own provider", async () => {
// Given
handleApiResponseMock.mockResolvedValue({ data: { id: "task-1" } });
fetchMock
.mockResolvedValueOnce(new Response(null, { status: 202 }))
.mockRejectedValueOnce(new Error("socket hang up"));
// When
const outcomes = await startProviderConnectionChecks([
"provider-1",
"provider-2",
]);
// Then
expect(outcomes["provider-1"]).toEqual({ taskId: "task-1" });
expect(outcomes["provider-2"]).toEqual({
error: { error: "Unexpected error" },
});
});
it("ignores blank and duplicated ids", async () => {
// Given
handleApiResponseMock.mockResolvedValue({ data: { id: "task-1" } });
// When
const outcomes = await startProviderConnectionChecks([
"provider-1",
"provider-1",
"",
]);
// Then
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(Object.keys(outcomes)).toEqual(["provider-1"]);
});
});