mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-08-19 09:30:21 +00:00
test(ui): drop organization unit tests restated by integration (#12382)
This commit is contained in:
@@ -7,6 +7,8 @@ import { CLOUD_UPGRADE_FEATURE } from "@/types/cloud-upgrade";
|
||||
|
||||
import { GcpMethodSelector } from "./gcp-method-selector";
|
||||
|
||||
// OSS gating only: `CloudUpgradeModal` mounts outside the providers page, so the
|
||||
// harness cannot see the upsell open. The Cloud case is restated by integration.
|
||||
describe("GcpMethodSelector", () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
@@ -40,29 +42,4 @@ describe("GcpMethodSelector", () => {
|
||||
CLOUD_UPGRADE_FEATURE.GCP_ORGANIZATIONS,
|
||||
);
|
||||
});
|
||||
|
||||
it("enters the GCP org flow in Cloud", async () => {
|
||||
// Given
|
||||
vi.stubEnv("UI_CLOUD_ENABLED", "true");
|
||||
const user = userEvent.setup();
|
||||
const onSelectOrganizations = vi.fn();
|
||||
|
||||
// When
|
||||
render(
|
||||
<GcpMethodSelector
|
||||
onSelectSingle={vi.fn()}
|
||||
onSelectOrganizations={onSelectOrganizations}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Then
|
||||
await user.click(
|
||||
screen.getByRole("radio", {
|
||||
name: /add multiple projects with gcp organization/i,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(onSelectOrganizations).toHaveBeenCalledTimes(1);
|
||||
expect(useCloudUpgradeStore.getState().activeFeature).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { createElement, type PropsWithChildren, StrictMode } from "react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { useOrgSetupStore } from "@/store/organizations/store";
|
||||
import {
|
||||
APPLY_STATUS,
|
||||
DISCOVERY_STATUS,
|
||||
ORG_SECRET_TYPE,
|
||||
ORGANIZATION_TYPE,
|
||||
@@ -80,10 +78,6 @@ vi.mock(
|
||||
() => organizationsActionsMock,
|
||||
);
|
||||
|
||||
function StrictModeWrapper({ children }: PropsWithChildren) {
|
||||
return createElement(StrictMode, null, children);
|
||||
}
|
||||
|
||||
/** Mocks the chain up to (and including) triggering discovery, all succeeding. */
|
||||
function mockChainThroughDiscoveryTrigger() {
|
||||
organizationsActionsMock.listOrganizationsByExternalId.mockResolvedValue({
|
||||
@@ -123,116 +117,6 @@ describe("useOrgSetupSubmission", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("completes the setup chain and stores selectable candidates", async () => {
|
||||
// Given
|
||||
const onNext = vi.fn();
|
||||
// `true` = the form owns the field and rendered the error on it.
|
||||
const setFieldError = vi.fn(() => true);
|
||||
const discoveryResult = {
|
||||
roots: [
|
||||
{ id: "r-root", arn: "arn:root", name: "Root", policy_types: [] },
|
||||
],
|
||||
organizational_units: [],
|
||||
accounts: [
|
||||
{
|
||||
id: "111111111111",
|
||||
name: "Account One",
|
||||
arn: "arn:aws:organizations::111111111111:account/o-123/111111111111",
|
||||
email: "one@example.com",
|
||||
status: "ACTIVE",
|
||||
joined_method: "CREATED",
|
||||
joined_timestamp: "2024-01-01T00:00:00Z",
|
||||
parent_id: "r-root",
|
||||
registration: {
|
||||
provider_exists: false,
|
||||
provider_id: null,
|
||||
organization_relation: "link_required",
|
||||
organization_node_relation: "not_applicable",
|
||||
provider_secret_state: "will_create",
|
||||
apply_status: APPLY_STATUS.READY,
|
||||
blocked_reasons: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "222222222222",
|
||||
name: "Account Two",
|
||||
arn: "arn:aws:organizations::222222222222:account/o-123/222222222222",
|
||||
email: "two@example.com",
|
||||
status: "ACTIVE",
|
||||
joined_method: "CREATED",
|
||||
joined_timestamp: "2024-01-01T00:00:00Z",
|
||||
parent_id: "r-root",
|
||||
registration: {
|
||||
provider_exists: false,
|
||||
provider_id: null,
|
||||
organization_relation: "link_required",
|
||||
organization_node_relation: "not_applicable",
|
||||
provider_secret_state: "will_create",
|
||||
apply_status: APPLY_STATUS.BLOCKED,
|
||||
blocked_reasons: ["Already linked"],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
organizationsActionsMock.listOrganizationsByExternalId.mockResolvedValue({
|
||||
data: [],
|
||||
});
|
||||
organizationsActionsMock.createOrganization.mockResolvedValue({
|
||||
data: { id: "org-1" },
|
||||
});
|
||||
organizationsActionsMock.listOrganizationSecretsByOrganizationId.mockResolvedValue(
|
||||
{
|
||||
data: [],
|
||||
},
|
||||
);
|
||||
organizationsActionsMock.createOrganizationSecret.mockResolvedValue({
|
||||
data: { id: "secret-1" },
|
||||
});
|
||||
organizationsActionsMock.triggerDiscovery.mockResolvedValue({
|
||||
data: { id: "discovery-1" },
|
||||
});
|
||||
organizationsActionsMock.getDiscovery.mockResolvedValue({
|
||||
data: {
|
||||
attributes: {
|
||||
status: DISCOVERY_STATUS.SUCCEEDED,
|
||||
result: discoveryResult,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useOrgSetupSubmission({
|
||||
stackSetExternalId: "tenant-external-id",
|
||||
onNext,
|
||||
setFieldError,
|
||||
}),
|
||||
{ wrapper: StrictModeWrapper },
|
||||
);
|
||||
|
||||
// When
|
||||
await act(async () => {
|
||||
await result.current.submitOrganizationSetup({
|
||||
orgType: ORGANIZATION_TYPE.AWS,
|
||||
organizationName: "Acme",
|
||||
awsOrgId: "o-abc123def4",
|
||||
roleArn: "arn:aws:iam::123456789012:role/ProwlerOrgRole",
|
||||
});
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(onNext).toHaveBeenCalledTimes(1);
|
||||
expect(setFieldError).not.toHaveBeenCalled();
|
||||
|
||||
const state = useOrgSetupStore.getState();
|
||||
expect(state.organizationId).toBe("org-1");
|
||||
expect(state.organizationExternalId).toBe("o-abc123def4");
|
||||
expect(state.discoveryId).toBe("discovery-1");
|
||||
expect(state.selectedCandidateIds).toEqual(["111111111111"]);
|
||||
expect(state.selectableCandidateIds).toEqual(["111111111111"]);
|
||||
});
|
||||
|
||||
it("times out then resumes the same discovery via keep waiting", async () => {
|
||||
// Given — a discovery that stays running until the client budget is spent.
|
||||
vi.useFakeTimers();
|
||||
@@ -292,6 +176,8 @@ describe("useOrgSetupSubmission", () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
// The MSW handler answers every trigger with the same static discovery, so
|
||||
// integration can only count POSTs — not the retry clearing the failure.
|
||||
it("retry triggers a fresh discovery after a failed one", async () => {
|
||||
// Given — a discovery that completes as failed.
|
||||
const onNext = vi.fn();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { act, render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import type { ComponentProps } from "react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
@@ -138,47 +137,6 @@ describe("OrgLaunchScan", () => {
|
||||
).toBe(`/scans?tab=${SCAN_JOBS_TAB.SCHEDULED}`);
|
||||
});
|
||||
|
||||
it("should launch the complete organization after a partial schedule save", async () => {
|
||||
// Given
|
||||
const user = userEvent.setup();
|
||||
const onFooterChange = vi.fn();
|
||||
updateSchedulesBulkMock.mockResolvedValue({
|
||||
data: {
|
||||
updated: ["provider-2"],
|
||||
failed: [{ id: "provider-1", error: "Denied" }],
|
||||
},
|
||||
});
|
||||
|
||||
render(
|
||||
<OrgLaunchScan
|
||||
onClose={vi.fn()}
|
||||
onBack={vi.fn()}
|
||||
onFooterChange={onFooterChange}
|
||||
capability={SCAN_SCHEDULE_CAPABILITY.ADVANCED}
|
||||
/>,
|
||||
);
|
||||
|
||||
// When
|
||||
await user.click(
|
||||
await screen.findByRole("checkbox", {
|
||||
name: /launch an initial scan now/i,
|
||||
}),
|
||||
);
|
||||
await act(async () => {
|
||||
lastFooterConfig(onFooterChange)?.onAction?.();
|
||||
});
|
||||
|
||||
// Then
|
||||
await waitFor(() =>
|
||||
expect(launchOrganizationScansMock).toHaveBeenCalledTimes(1),
|
||||
);
|
||||
expect(launchOrganizationScansMock).toHaveBeenCalledWith("org-1");
|
||||
expect(scheduleOrganizationDailyScansMock).not.toHaveBeenCalled();
|
||||
expect(
|
||||
toastMock.mock.calls[0]?.[0].action.props.children.props.href,
|
||||
).toBe(`/scans?tab=${SCAN_JOBS_TAB.ACTIVE}`);
|
||||
});
|
||||
|
||||
it("should disable launch actions while schedule capability is loading", async () => {
|
||||
// Given
|
||||
const onFooterChange = vi.fn();
|
||||
@@ -246,126 +204,6 @@ describe("OrgLaunchScan", () => {
|
||||
expect(pushMock).not.toHaveBeenCalled();
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should treat a fully-failed bulk response as an error without navigating away", async () => {
|
||||
// Given
|
||||
const onClose = vi.fn();
|
||||
const onFooterChange = vi.fn();
|
||||
updateSchedulesBulkMock.mockResolvedValue({
|
||||
data: {
|
||||
updated: [],
|
||||
failed: [
|
||||
{ id: "provider-1", error: "Denied" },
|
||||
{ id: "provider-2", error: "Denied" },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
render(
|
||||
<OrgLaunchScan
|
||||
onClose={onClose}
|
||||
onBack={vi.fn()}
|
||||
onFooterChange={onFooterChange}
|
||||
capability={SCAN_SCHEDULE_CAPABILITY.ADVANCED}
|
||||
/>,
|
||||
);
|
||||
|
||||
// When
|
||||
await screen.findByText("Scan Schedule");
|
||||
await act(async () => {
|
||||
lastFooterConfig(onFooterChange)?.onAction?.();
|
||||
});
|
||||
|
||||
// Then
|
||||
await waitFor(() =>
|
||||
expect(toastMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
variant: "destructive",
|
||||
title: "Unable to save scan schedules",
|
||||
description:
|
||||
"The scan schedule could not be saved for 2 accounts: Denied.",
|
||||
}),
|
||||
),
|
||||
);
|
||||
expect(launchOrganizationScansMock).not.toHaveBeenCalled();
|
||||
expect(pushMock).not.toHaveBeenCalled();
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should describe partial failures in the success toast", async () => {
|
||||
// Given
|
||||
const onFooterChange = vi.fn();
|
||||
updateSchedulesBulkMock.mockResolvedValue({
|
||||
data: {
|
||||
updated: ["provider-2"],
|
||||
failed: [{ id: "provider-1", error: "Denied" }],
|
||||
},
|
||||
});
|
||||
|
||||
render(
|
||||
<OrgLaunchScan
|
||||
onClose={vi.fn()}
|
||||
onBack={vi.fn()}
|
||||
onFooterChange={onFooterChange}
|
||||
capability={SCAN_SCHEDULE_CAPABILITY.ADVANCED}
|
||||
/>,
|
||||
);
|
||||
|
||||
// When
|
||||
await screen.findByText("Scan Schedule");
|
||||
await act(async () => {
|
||||
lastFooterConfig(onFooterChange)?.onAction?.();
|
||||
});
|
||||
|
||||
// Then
|
||||
await waitFor(() => expect(toastMock).toHaveBeenCalled());
|
||||
expect(toastMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
title: "Scan schedules saved",
|
||||
description:
|
||||
"The schedule was saved for 1 account, but 1 account could not be updated: Denied.",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should proceed when the response carries no result lists", async () => {
|
||||
// Given — an empty 200/204 body. The endpoint commits each schedule before
|
||||
// answering, so this is not a failure.
|
||||
const user = userEvent.setup();
|
||||
const onFooterChange = vi.fn();
|
||||
updateSchedulesBulkMock.mockResolvedValue({ success: true });
|
||||
|
||||
render(
|
||||
<OrgLaunchScan
|
||||
onClose={vi.fn()}
|
||||
onBack={vi.fn()}
|
||||
onFooterChange={onFooterChange}
|
||||
capability={SCAN_SCHEDULE_CAPABILITY.ADVANCED}
|
||||
/>,
|
||||
);
|
||||
|
||||
// When
|
||||
await user.click(
|
||||
await screen.findByRole("checkbox", {
|
||||
name: /launch an initial scan now/i,
|
||||
}),
|
||||
);
|
||||
await act(async () => {
|
||||
lastFooterConfig(onFooterChange)?.onAction?.();
|
||||
});
|
||||
|
||||
// Then — every created provider is treated as saved and scanned.
|
||||
await waitFor(() =>
|
||||
expect(launchOrganizationScansMock).toHaveBeenCalledWith("org-1"),
|
||||
);
|
||||
expect(toastMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
title: "Scan schedules saved and initial scans launched",
|
||||
description: "The scan schedule was saved for 2 accounts.",
|
||||
}),
|
||||
);
|
||||
expect(pushMock).toHaveBeenCalledWith("/providers");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when capability is DAILY_LEGACY", () => {
|
||||
|
||||
Reference in New Issue
Block a user