mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
207 lines
5.6 KiB
TypeScript
207 lines
5.6 KiB
TypeScript
import { act, renderHook } from "@testing-library/react";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { useFindingGroupResources } from "./use-finding-group-resources";
|
|
|
|
type IntersectionCallback = (entries: IntersectionObserverEntry[]) => void;
|
|
|
|
let latestObserverCallback: IntersectionCallback | null = null;
|
|
|
|
class MockIntersectionObserver {
|
|
callback: IntersectionCallback;
|
|
constructor(callback: IntersectionCallback) {
|
|
this.callback = callback;
|
|
latestObserverCallback = callback;
|
|
}
|
|
observe() {}
|
|
unobserve() {}
|
|
disconnect() {
|
|
if (latestObserverCallback === this.callback) {
|
|
latestObserverCallback = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
function triggerIntersection() {
|
|
latestObserverCallback?.([
|
|
{ isIntersecting: true } as IntersectionObserverEntry,
|
|
]);
|
|
}
|
|
|
|
const findingGroupActionsMock = vi.hoisted(() => ({
|
|
getLatestFindingGroupResources: vi.fn(),
|
|
getFindingGroupResources: vi.fn(),
|
|
adaptFindingGroupResourcesResponse: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/actions/finding-groups", () => findingGroupActionsMock);
|
|
|
|
function makeApiResponse(
|
|
resources: { id: string }[],
|
|
{ pages = 1 }: { pages?: number } = {},
|
|
) {
|
|
return {
|
|
data: resources,
|
|
meta: { pagination: { pages } },
|
|
};
|
|
}
|
|
|
|
function fakeResource(id: string) {
|
|
return {
|
|
findingId: id,
|
|
resourceUid: `uid-${id}`,
|
|
resourceName: `Resource ${id}`,
|
|
status: "FAIL",
|
|
severity: "high",
|
|
isMuted: false,
|
|
};
|
|
}
|
|
|
|
function defaultOptions(overrides?: Record<string, unknown>) {
|
|
return {
|
|
checkId: "check_1",
|
|
hasDateOrScanFilter: false,
|
|
filters: {},
|
|
onSetResources: vi.fn(),
|
|
onAppendResources: vi.fn(),
|
|
onSetLoading: vi.fn(),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
async function flushAsync() {
|
|
await act(async () => {
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
});
|
|
}
|
|
|
|
describe("useFindingGroupResources", () => {
|
|
beforeEach(() => {
|
|
vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
|
|
for (const mockFn of Object.values(findingGroupActionsMock)) {
|
|
mockFn.mockReset();
|
|
}
|
|
});
|
|
|
|
describe("when mounting", () => {
|
|
it("should fetch page 1 and deliver resources via onSetResources", async () => {
|
|
const apiResponse = makeApiResponse([{ id: "r1" }, { id: "r2" }], {
|
|
pages: 1,
|
|
});
|
|
const adapted = [fakeResource("r1"), fakeResource("r2")];
|
|
|
|
findingGroupActionsMock.getLatestFindingGroupResources.mockResolvedValue(
|
|
apiResponse,
|
|
);
|
|
findingGroupActionsMock.adaptFindingGroupResourcesResponse.mockReturnValue(
|
|
adapted,
|
|
);
|
|
|
|
const onSetResources = vi.fn();
|
|
const onSetLoading = vi.fn();
|
|
|
|
renderHook(() =>
|
|
useFindingGroupResources(
|
|
defaultOptions({ onSetResources, onSetLoading }),
|
|
),
|
|
);
|
|
await flushAsync();
|
|
|
|
expect(
|
|
findingGroupActionsMock.getLatestFindingGroupResources,
|
|
).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
checkId: "check_1",
|
|
page: 1,
|
|
pageSize: 10,
|
|
}),
|
|
);
|
|
expect(onSetResources).toHaveBeenCalledWith(adapted, false);
|
|
});
|
|
|
|
it("should use getFindingGroupResources when hasDateOrScanFilter is true", async () => {
|
|
const apiResponse = makeApiResponse([], { pages: 1 });
|
|
findingGroupActionsMock.getFindingGroupResources.mockResolvedValue(
|
|
apiResponse,
|
|
);
|
|
findingGroupActionsMock.adaptFindingGroupResourcesResponse.mockReturnValue(
|
|
[],
|
|
);
|
|
|
|
renderHook(() =>
|
|
useFindingGroupResources(defaultOptions({ hasDateOrScanFilter: true })),
|
|
);
|
|
await flushAsync();
|
|
|
|
expect(
|
|
findingGroupActionsMock.getFindingGroupResources,
|
|
).toHaveBeenCalledTimes(1);
|
|
expect(
|
|
findingGroupActionsMock.getLatestFindingGroupResources,
|
|
).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should forward the active finding-group filters to the resources endpoint", async () => {
|
|
const apiResponse = makeApiResponse([], { pages: 1 });
|
|
const filters = {
|
|
"filter[status__in]": "PASS",
|
|
"filter[severity__in]": "medium",
|
|
"filter[provider_type__in]": "aws",
|
|
};
|
|
findingGroupActionsMock.getLatestFindingGroupResources.mockResolvedValue(
|
|
apiResponse,
|
|
);
|
|
findingGroupActionsMock.adaptFindingGroupResourcesResponse.mockReturnValue(
|
|
[],
|
|
);
|
|
|
|
renderHook(() => useFindingGroupResources(defaultOptions({ filters })));
|
|
await flushAsync();
|
|
|
|
expect(
|
|
findingGroupActionsMock.getLatestFindingGroupResources,
|
|
).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
checkId: "check_1",
|
|
page: 1,
|
|
pageSize: 10,
|
|
filters,
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("when all resources fit in one page", () => {
|
|
it("should not fetch page 2 after page 1 completes", async () => {
|
|
const apiResponse = makeApiResponse(
|
|
[{ id: "r1" }, { id: "r2" }, { id: "r3" }, { id: "r4" }],
|
|
{ pages: 1 },
|
|
);
|
|
findingGroupActionsMock.getLatestFindingGroupResources.mockResolvedValue(
|
|
apiResponse,
|
|
);
|
|
findingGroupActionsMock.adaptFindingGroupResourcesResponse.mockReturnValue(
|
|
[
|
|
fakeResource("r1"),
|
|
fakeResource("r2"),
|
|
fakeResource("r3"),
|
|
fakeResource("r4"),
|
|
],
|
|
);
|
|
|
|
const { result } = renderHook(() =>
|
|
useFindingGroupResources(defaultOptions()),
|
|
);
|
|
await flushAsync();
|
|
|
|
act(() => triggerIntersection());
|
|
await flushAsync();
|
|
|
|
expect(
|
|
findingGroupActionsMock.getLatestFindingGroupResources,
|
|
).toHaveBeenCalledTimes(1);
|
|
expect(result.current.totalCount).toBeNull();
|
|
});
|
|
});
|
|
});
|