189 lines
6.5 KiB
TypeScript
189 lines
6.5 KiB
TypeScript
import { cleanup, fireEvent, render, screen, within } from "@testing-library/react";
|
|
import type { ComponentProps } from "react";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { KubernetesConnectionCard } from "./kubernetes-connection-card";
|
|
import { KubernetesDiagnosticsCard } from "./kubernetes-diagnostics-card";
|
|
import { KubernetesSessionsCard } from "./kubernetes-sessions-card";
|
|
import { KubernetesWorkspaceCard } from "./kubernetes-workspace-card";
|
|
import {
|
|
createDefaultKubernetesExecutorForm,
|
|
createDefaultKubernetesProfileConfig,
|
|
} from "./kubernetes-config";
|
|
|
|
const responsive = vi.hoisted(() => ({ isMobile: false }));
|
|
|
|
vi.mock("@/hooks/use-responsive-breakpoint", () => ({
|
|
useResponsiveBreakpoint: () => ({ isMobile: responsive.isMobile }),
|
|
}));
|
|
|
|
afterEach(cleanup);
|
|
|
|
describe("Kubernetes settings cards", () => {
|
|
it("hides kubeconfig-only fields for in-cluster auth and disables member edits", () => {
|
|
const form = { ...createDefaultKubernetesExecutorForm(), authMode: "in_cluster" as const };
|
|
|
|
render(<KubernetesConnectionCard form={form} baseline={form} onChange={vi.fn()} readOnly />);
|
|
|
|
expect(screen.queryByLabelText("Kubeconfig path")).toBeNull();
|
|
expect(
|
|
(screen.getByLabelText("Fixed namespace").closest("fieldset") as HTMLFieldSetElement)
|
|
.disabled,
|
|
).toBe(true);
|
|
});
|
|
|
|
it("shows only fields belonging to the selected workspace mode", () => {
|
|
const baseline = createDefaultKubernetesProfileConfig();
|
|
const { rerender } = render(
|
|
<KubernetesWorkspaceCard
|
|
form={{ ...baseline, workspaceMode: "empty_dir" }}
|
|
baseline={baseline}
|
|
onChange={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByTestId("kubernetes-empty-dir-warning")).toBeTruthy();
|
|
expect(screen.queryByLabelText("Claim size")).toBeNull();
|
|
rerender(
|
|
<KubernetesWorkspaceCard
|
|
form={{ ...baseline, workspaceMode: "existing_claim", claimName: "shared" }}
|
|
baseline={baseline}
|
|
onChange={vi.fn()}
|
|
/>,
|
|
);
|
|
expect(screen.getByLabelText("Existing claim name")).toBeTruthy();
|
|
expect(screen.queryByLabelText("Claim size")).toBeNull();
|
|
});
|
|
|
|
it("keeps member testing visible and disabled with an explanation", () => {
|
|
render(
|
|
<KubernetesDiagnosticsCard
|
|
testing={false}
|
|
result={null}
|
|
error={null}
|
|
onTest={vi.fn()}
|
|
canManage={false}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByRole("button", { name: "Test Kubernetes" }).hasAttribute("disabled")).toBe(
|
|
true,
|
|
);
|
|
expect(screen.getByText("Only an administrator can run this Kubernetes test.")).toBeTruthy();
|
|
});
|
|
|
|
it("renders causal diagnostic steps and PodTemplate warnings", () => {
|
|
render(
|
|
<KubernetesDiagnosticsCard
|
|
testing={false}
|
|
result={{
|
|
success: false,
|
|
steps: [
|
|
{
|
|
key: "rbac",
|
|
success: false,
|
|
duration_ms: 12,
|
|
detail: "Denied permission",
|
|
},
|
|
],
|
|
warnings: [{ path: "template.spec", message: "Privileged container" }],
|
|
}}
|
|
error={null}
|
|
onTest={vi.fn()}
|
|
canManage
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText("Required permissions")).toBeTruthy();
|
|
expect(screen.getByText("Denied permission")).toBeTruthy();
|
|
expect(screen.getByText("template.spec")).toBeTruthy();
|
|
expect(screen.getByText("Privileged container")).toBeTruthy();
|
|
});
|
|
|
|
it("shows complete task and session identities on mobile session cards", () => {
|
|
responsive.isMobile = true;
|
|
render(<KubernetesSessionsCard state={sessionsState()} />);
|
|
|
|
const card = screen.getByTestId("kubernetes-mobile-session-list");
|
|
expect(within(card).getByText("task-123456789")).toBeTruthy();
|
|
expect(within(card).getByText("session-987654321")).toBeTruthy();
|
|
expect(within(card).getByText(/^Task:/)).toBeTruthy();
|
|
expect(within(card).getByText(/^Session:/)).toBeTruthy();
|
|
});
|
|
|
|
it("shows retained requests, guidance, and task navigation on mobile", () => {
|
|
responsive.isMobile = true;
|
|
const state = sessionsState();
|
|
state.sessions[0] = {
|
|
...state.sessions[0],
|
|
session_state: "CANCELLED",
|
|
retention_state: "retained",
|
|
main_container_requests: { cpu: "0", memory: "512Mi" },
|
|
};
|
|
|
|
render(<KubernetesSessionsCard state={state} />);
|
|
|
|
const card = screen.getByTestId("kubernetes-mobile-session-list");
|
|
expect(within(card).getByText("Retained")).toBeTruthy();
|
|
expect(within(card).getByTestId("kubernetes-session-request-summary").textContent).toContain(
|
|
"0 CPU · 512Mi memory",
|
|
);
|
|
expect(within(card).getByRole("link").getAttribute("href")).toBe("/t/task-123456789");
|
|
expect(screen.getByText(/Stop preserves Kubernetes resources/)).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe("Kubernetes session status cards", () => {
|
|
it("shows the sanitized failure reason beneath desktop session status", () => {
|
|
responsive.isMobile = false;
|
|
render(<KubernetesSessionsCard state={sessionsState()} />);
|
|
|
|
const row = screen.getByText("task-123").closest("tr");
|
|
expect(row).not.toBeNull();
|
|
fireEvent.click(within(row as HTMLTableRowElement).getByLabelText("Show session details"));
|
|
expect(screen.getByText("pods is forbidden: RBAC denied")).toBeTruthy();
|
|
});
|
|
|
|
it("labels a successfully completed Pod as succeeded instead of terminated", () => {
|
|
responsive.isMobile = false;
|
|
const state = sessionsState();
|
|
state.sessions[0] = {
|
|
...state.sessions[0],
|
|
pod_phase: "Succeeded",
|
|
container_state: "terminated",
|
|
failure_reason: undefined,
|
|
};
|
|
|
|
render(<KubernetesSessionsCard state={state} />);
|
|
|
|
const status = screen.getByTestId("kubernetes-session-status-summary");
|
|
expect(within(status).getByText("Pod: Succeeded")).toBeTruthy();
|
|
expect(within(status).getByText("Container: Terminated")).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
type KubernetesSessionsState = ComponentProps<typeof KubernetesSessionsCard>["state"];
|
|
|
|
function sessionsState(): KubernetesSessionsState {
|
|
return {
|
|
sessions: [
|
|
{
|
|
task_id: "task-123456789",
|
|
session_id: "session-987654321",
|
|
pod_name: "kandev-session-1",
|
|
pod_phase: "Failed",
|
|
container_state: "terminated",
|
|
restarts: 2,
|
|
workspace_kind: "managed_pvc",
|
|
created_at: "2026-08-24T10:00:00Z",
|
|
failure_reason: "pods is forbidden: RBAC denied",
|
|
session_state: "RUNNING",
|
|
retention_state: "active",
|
|
main_container_requests: { cpu: "250m", memory: "1Gi" },
|
|
},
|
|
],
|
|
loading: false,
|
|
error: null,
|
|
refresh: vi.fn(async () => []),
|
|
};
|
|
}
|