mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
251 lines
7.2 KiB
TypeScript
251 lines
7.2 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import {
|
|
getFindingGroupFilterOptions,
|
|
getSelectedFindingCheckOptions,
|
|
} from "./finding-group-filter-options";
|
|
|
|
function makeResponse(
|
|
pageCount: number,
|
|
groups: Array<{ id: string; title: string }>,
|
|
) {
|
|
return {
|
|
data: groups.map(({ id, title }) => ({
|
|
type: "finding-groups",
|
|
id,
|
|
attributes: {
|
|
check_id: id,
|
|
check_title: title,
|
|
check_description: null,
|
|
severity: "high",
|
|
status: "FAIL",
|
|
impacted_providers: [],
|
|
resources_total: 1,
|
|
resources_fail: 1,
|
|
pass_count: 0,
|
|
fail_count: 1,
|
|
muted_count: 0,
|
|
new_count: 0,
|
|
changed_count: 0,
|
|
first_seen_at: null,
|
|
last_seen_at: null,
|
|
failing_since: null,
|
|
},
|
|
})),
|
|
meta: { pagination: { pages: pageCount } },
|
|
};
|
|
}
|
|
|
|
describe("getFindingGroupFilterOptions", () => {
|
|
it("loads every page without applying the filter's own selection", async () => {
|
|
// Given
|
|
const fetchFindingGroups = vi
|
|
.fn()
|
|
.mockResolvedValueOnce(
|
|
makeResponse(2, [{ id: "check-a", title: "Check A" }]),
|
|
)
|
|
.mockResolvedValueOnce(
|
|
makeResponse(2, [
|
|
{ id: "check-a", title: "Check A updated" },
|
|
{ id: "check-b", title: "Check B" },
|
|
]),
|
|
);
|
|
|
|
// When
|
|
const options = await getFindingGroupFilterOptions({
|
|
fetchFindingGroups,
|
|
filters: {
|
|
"filter[check_id__in]": "check-a",
|
|
"filter[severity__in]": "high",
|
|
},
|
|
});
|
|
|
|
// Then
|
|
expect(fetchFindingGroups).toHaveBeenNthCalledWith(1, {
|
|
filters: { "filter[severity__in]": "high" },
|
|
page: 1,
|
|
pageSize: 100,
|
|
sort: "check_id",
|
|
});
|
|
expect(fetchFindingGroups).toHaveBeenNthCalledWith(2, {
|
|
filters: { "filter[severity__in]": "high" },
|
|
page: 2,
|
|
pageSize: 100,
|
|
sort: "check_id",
|
|
});
|
|
expect(options).toEqual([
|
|
{ checkId: "check-a", checkTitle: "Check A updated" },
|
|
{ checkId: "check-b", checkTitle: "Check B" },
|
|
]);
|
|
});
|
|
|
|
it("requests the remaining pages concurrently once the page count is known", async () => {
|
|
// Given
|
|
const pending: Array<(value: unknown) => void> = [];
|
|
const fetchFindingGroups = vi.fn(
|
|
({ page }: { page: number }) =>
|
|
new Promise((resolve) => {
|
|
if (page === 1) {
|
|
resolve(makeResponse(3, [{ id: "check-a", title: "Check A" }]));
|
|
return;
|
|
}
|
|
pending.push(resolve);
|
|
}),
|
|
);
|
|
|
|
// When
|
|
const optionsPromise = getFindingGroupFilterOptions({
|
|
fetchFindingGroups,
|
|
filters: {},
|
|
});
|
|
await vi.waitFor(() => expect(fetchFindingGroups).toHaveBeenCalledTimes(3));
|
|
pending[0](makeResponse(3, [{ id: "check-b", title: "Check B" }]));
|
|
pending[1](makeResponse(3, [{ id: "check-c", title: "Check C" }]));
|
|
const options = await optionsPromise;
|
|
|
|
// Then
|
|
expect(
|
|
fetchFindingGroups.mock.calls.map(([params]) => params.page),
|
|
).toEqual([1, 2, 3]);
|
|
expect(options.map((option) => option.checkId)).toEqual([
|
|
"check-a",
|
|
"check-b",
|
|
"check-c",
|
|
]);
|
|
});
|
|
|
|
it("caps how many pages are in flight at the same time", async () => {
|
|
// Given
|
|
const resolvers: Array<() => void> = [];
|
|
const fetchFindingGroups = vi.fn(({ page }: { page: number }) => {
|
|
const response = makeResponse(12, [
|
|
{ id: `check-${page}`, title: `Check ${page}` },
|
|
]);
|
|
if (page === 1) return Promise.resolve(response);
|
|
return new Promise((resolve) => {
|
|
resolvers.push(() => resolve(response));
|
|
});
|
|
});
|
|
|
|
// When
|
|
const optionsPromise = getFindingGroupFilterOptions({
|
|
fetchFindingGroups,
|
|
filters: {},
|
|
});
|
|
await vi.waitFor(() => expect(fetchFindingGroups).toHaveBeenCalledTimes(5));
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
expect(fetchFindingGroups).toHaveBeenCalledTimes(5);
|
|
while (fetchFindingGroups.mock.calls.length < 12 || resolvers.length > 0) {
|
|
await vi.waitFor(() => expect(resolvers.length).toBeGreaterThan(0));
|
|
resolvers.pop()?.();
|
|
}
|
|
const options = await optionsPromise;
|
|
|
|
// Then
|
|
expect(fetchFindingGroups).toHaveBeenCalledTimes(12);
|
|
expect(options.map((option) => option.checkId)).toEqual(
|
|
Array.from({ length: 12 }, (_, index) => `check-${index + 1}`),
|
|
);
|
|
});
|
|
|
|
it("stops dequeuing pages once one of them rejects", async () => {
|
|
// Given
|
|
let rejectPage: (error: Error) => void = () => undefined;
|
|
const heldPages: Array<() => void> = [];
|
|
const fetchFindingGroups = vi.fn(({ page }: { page: number }) => {
|
|
const response = makeResponse(12, [
|
|
{ id: `check-${page}`, title: `Check ${page}` },
|
|
]);
|
|
if (page === 1) return Promise.resolve(response);
|
|
if (page === 2) {
|
|
return new Promise((_, reject) => {
|
|
rejectPage = reject;
|
|
});
|
|
}
|
|
return new Promise((resolve) => {
|
|
heldPages.push(() => resolve(response));
|
|
});
|
|
});
|
|
|
|
// When
|
|
const optionsPromise = getFindingGroupFilterOptions({
|
|
fetchFindingGroups,
|
|
filters: {},
|
|
});
|
|
await vi.waitFor(() => expect(fetchFindingGroups).toHaveBeenCalledTimes(5));
|
|
rejectPage(new Error("auth failed"));
|
|
await expect(optionsPromise).rejects.toThrow("auth failed");
|
|
// The other workers finish their current page after the failure.
|
|
heldPages.forEach((release) => release());
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
|
|
// Then
|
|
expect(fetchFindingGroups).toHaveBeenCalledTimes(5);
|
|
});
|
|
|
|
it("stops after the first page when the response has no pagination", async () => {
|
|
// Given
|
|
const fetchFindingGroups = vi.fn().mockResolvedValue(undefined);
|
|
|
|
// When
|
|
const options = await getFindingGroupFilterOptions({
|
|
fetchFindingGroups,
|
|
filters: {},
|
|
});
|
|
|
|
// Then
|
|
expect(fetchFindingGroups).toHaveBeenCalledTimes(1);
|
|
expect(options).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("getSelectedFindingCheckOptions", () => {
|
|
it("resolves the selected titles in one request, replacing the filter's own selection", async () => {
|
|
// Given
|
|
const fetchFindingGroups = vi
|
|
.fn()
|
|
.mockResolvedValue(
|
|
makeResponse(1, [{ id: "check-a", title: "Check A" }]),
|
|
);
|
|
|
|
// When
|
|
const options = await getSelectedFindingCheckOptions({
|
|
fetchFindingGroups,
|
|
filters: {
|
|
"filter[check_id__in]": "check-a",
|
|
"filter[severity__in]": "high",
|
|
},
|
|
selectedCheckIds: ["check-a", "check-b", "check-a"],
|
|
});
|
|
|
|
// Then
|
|
expect(fetchFindingGroups).toHaveBeenCalledTimes(1);
|
|
expect(fetchFindingGroups).toHaveBeenCalledWith({
|
|
filters: {
|
|
"filter[severity__in]": "high",
|
|
"filter[check_id__in]": "check-a,check-b",
|
|
},
|
|
page: 1,
|
|
pageSize: 100,
|
|
sort: "check_id",
|
|
});
|
|
expect(options).toEqual([{ checkId: "check-a", checkTitle: "Check A" }]);
|
|
});
|
|
|
|
it("requests nothing when no check is selected", async () => {
|
|
// Given
|
|
const fetchFindingGroups = vi.fn();
|
|
|
|
// When
|
|
const options = await getSelectedFindingCheckOptions({
|
|
fetchFindingGroups,
|
|
filters: {},
|
|
selectedCheckIds: [],
|
|
});
|
|
|
|
// Then
|
|
expect(fetchFindingGroups).not.toHaveBeenCalled();
|
|
expect(options).toEqual([]);
|
|
});
|
|
});
|