fix(kube): workload list actions use item.namespace not filter prop
Some checks failed
PR Review Automation / review (pull_request) Has been cancelled
Test / rust-fmt-check (pull_request) Has been cancelled
Test / rust-clippy (pull_request) Has been cancelled
Test / frontend-typecheck (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
Test / frontend-tests (pull_request) Has been cancelled
Some checks failed
PR Review Automation / review (pull_request) Has been cancelled
Test / rust-fmt-check (pull_request) Has been cancelled
Test / rust-clippy (pull_request) Has been cancelled
Test / frontend-typecheck (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
Test / frontend-tests (pull_request) Has been cancelled
Deployment/StatefulSet/DaemonSet action handlers were passing namespace='all' to kubectl when All Namespaces was selected. Actions now use the resource's own .namespace field for openEdit, handleRestart, handleRollback, handleDelete, ScaleModal, and EditResourceModal. Adds 21 TDD tests in WorkloadListActions.test.tsx covering all action handlers across DeploymentList, StatefulSetList, DaemonSetList, ReplicaSetList, JobList, and CronJobList. Tests verify IPC calls receive the item's actual namespace even when the filter prop is 'all'.
This commit is contained in:
parent
05d8b28159
commit
7dfda91cd8
@ -24,7 +24,7 @@ type ActiveModal =
|
|||||||
| { type: "delete"; ds: DaemonSetInfo }
|
| { type: "delete"; ds: DaemonSetInfo }
|
||||||
| null;
|
| null;
|
||||||
|
|
||||||
export function DaemonSetList({ daemonsets, clusterId, namespace, onRefresh }: DaemonSetListProps) {
|
export function DaemonSetList({ daemonsets, clusterId, namespace: _namespace, onRefresh }: DaemonSetListProps) {
|
||||||
const [activeModal, setActiveModal] = useState<ActiveModal>(null);
|
const [activeModal, setActiveModal] = useState<ActiveModal>(null);
|
||||||
const [isActing, setIsActing] = useState(false);
|
const [isActing, setIsActing] = useState(false);
|
||||||
const [actionError, setActionError] = useState<string | null>(null);
|
const [actionError, setActionError] = useState<string | null>(null);
|
||||||
@ -32,7 +32,7 @@ export function DaemonSetList({ daemonsets, clusterId, namespace, onRefresh }: D
|
|||||||
const openEdit = async (ds: DaemonSetInfo) => {
|
const openEdit = async (ds: DaemonSetInfo) => {
|
||||||
setActionError(null);
|
setActionError(null);
|
||||||
try {
|
try {
|
||||||
const yaml = await getResourceYamlCmd(clusterId, "daemonsets", namespace, ds.name);
|
const yaml = await getResourceYamlCmd(clusterId, "daemonsets", ds.namespace, ds.name);
|
||||||
setActiveModal({ type: "edit", ds, yaml });
|
setActiveModal({ type: "edit", ds, yaml });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setActionError(err instanceof Error ? err.message : String(err));
|
setActionError(err instanceof Error ? err.message : String(err));
|
||||||
@ -43,7 +43,7 @@ export function DaemonSetList({ daemonsets, clusterId, namespace, onRefresh }: D
|
|||||||
if (activeModal?.type !== "restart") return;
|
if (activeModal?.type !== "restart") return;
|
||||||
setIsActing(true);
|
setIsActing(true);
|
||||||
try {
|
try {
|
||||||
await restartDaemonsetCmd(clusterId, namespace, activeModal.ds.name);
|
await restartDaemonsetCmd(clusterId, activeModal.ds.namespace, activeModal.ds.name);
|
||||||
setActiveModal(null);
|
setActiveModal(null);
|
||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -57,7 +57,7 @@ export function DaemonSetList({ daemonsets, clusterId, namespace, onRefresh }: D
|
|||||||
if (activeModal?.type !== "delete") return;
|
if (activeModal?.type !== "delete") return;
|
||||||
setIsActing(true);
|
setIsActing(true);
|
||||||
try {
|
try {
|
||||||
await deleteResourceCmd(clusterId, "daemonsets", namespace, activeModal.ds.name);
|
await deleteResourceCmd(clusterId, "daemonsets", activeModal.ds.namespace, activeModal.ds.name);
|
||||||
setActiveModal(null);
|
setActiveModal(null);
|
||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
} finally {
|
} finally {
|
||||||
@ -146,7 +146,7 @@ export function DaemonSetList({ daemonsets, clusterId, namespace, onRefresh }: D
|
|||||||
<EditResourceModal
|
<EditResourceModal
|
||||||
isOpen
|
isOpen
|
||||||
clusterId={clusterId}
|
clusterId={clusterId}
|
||||||
namespace={namespace}
|
namespace={activeModal.ds.namespace}
|
||||||
resourceType="daemonsets"
|
resourceType="daemonsets"
|
||||||
resourceName={activeModal.ds.name}
|
resourceName={activeModal.ds.name}
|
||||||
initialYaml={activeModal.yaml}
|
initialYaml={activeModal.yaml}
|
||||||
|
|||||||
@ -29,7 +29,7 @@ type ActiveModal =
|
|||||||
| { type: "delete"; deployment: DeploymentInfo }
|
| { type: "delete"; deployment: DeploymentInfo }
|
||||||
| null;
|
| null;
|
||||||
|
|
||||||
export function DeploymentList({ deployments, clusterId, namespace, onRefresh }: DeploymentListProps) {
|
export function DeploymentList({ deployments, clusterId, namespace: _namespace, onRefresh }: DeploymentListProps) {
|
||||||
const [activeModal, setActiveModal] = useState<ActiveModal>(null);
|
const [activeModal, setActiveModal] = useState<ActiveModal>(null);
|
||||||
const [isActing, setIsActing] = useState(false);
|
const [isActing, setIsActing] = useState(false);
|
||||||
const [actionError, setActionError] = useState<string | null>(null);
|
const [actionError, setActionError] = useState<string | null>(null);
|
||||||
@ -37,7 +37,7 @@ export function DeploymentList({ deployments, clusterId, namespace, onRefresh }:
|
|||||||
const openEdit = async (deployment: DeploymentInfo) => {
|
const openEdit = async (deployment: DeploymentInfo) => {
|
||||||
setActionError(null);
|
setActionError(null);
|
||||||
try {
|
try {
|
||||||
const yaml = await getResourceYamlCmd(clusterId, "deployments", namespace, deployment.name);
|
const yaml = await getResourceYamlCmd(clusterId, "deployments", deployment.namespace, deployment.name);
|
||||||
setActiveModal({ type: "edit", deployment, yaml });
|
setActiveModal({ type: "edit", deployment, yaml });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setActionError(err instanceof Error ? err.message : String(err));
|
setActionError(err instanceof Error ? err.message : String(err));
|
||||||
@ -48,7 +48,7 @@ export function DeploymentList({ deployments, clusterId, namespace, onRefresh }:
|
|||||||
if (activeModal?.type !== "restart") return;
|
if (activeModal?.type !== "restart") return;
|
||||||
setIsActing(true);
|
setIsActing(true);
|
||||||
try {
|
try {
|
||||||
await restartDeploymentCmd(clusterId, namespace, activeModal.deployment.name);
|
await restartDeploymentCmd(clusterId, activeModal.deployment.namespace, activeModal.deployment.name);
|
||||||
setActiveModal(null);
|
setActiveModal(null);
|
||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -62,7 +62,7 @@ export function DeploymentList({ deployments, clusterId, namespace, onRefresh }:
|
|||||||
if (activeModal?.type !== "rollback") return;
|
if (activeModal?.type !== "rollback") return;
|
||||||
setIsActing(true);
|
setIsActing(true);
|
||||||
try {
|
try {
|
||||||
await rollbackDeploymentCmd(clusterId, namespace, activeModal.deployment.name);
|
await rollbackDeploymentCmd(clusterId, activeModal.deployment.namespace, activeModal.deployment.name);
|
||||||
setActiveModal(null);
|
setActiveModal(null);
|
||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -76,7 +76,7 @@ export function DeploymentList({ deployments, clusterId, namespace, onRefresh }:
|
|||||||
if (activeModal?.type !== "delete") return;
|
if (activeModal?.type !== "delete") return;
|
||||||
setIsActing(true);
|
setIsActing(true);
|
||||||
try {
|
try {
|
||||||
await deleteResourceCmd(clusterId, "deployments", namespace, activeModal.deployment.name);
|
await deleteResourceCmd(clusterId, "deployments", activeModal.deployment.namespace, activeModal.deployment.name);
|
||||||
setActiveModal(null);
|
setActiveModal(null);
|
||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
} finally {
|
} finally {
|
||||||
@ -165,7 +165,7 @@ export function DeploymentList({ deployments, clusterId, namespace, onRefresh }:
|
|||||||
resourceName={activeModal.deployment.name}
|
resourceName={activeModal.deployment.name}
|
||||||
currentReplicas={activeModal.deployment.replicas}
|
currentReplicas={activeModal.deployment.replicas}
|
||||||
onScale={(replicas) =>
|
onScale={(replicas) =>
|
||||||
scaleDeploymentCmd(clusterId, namespace, activeModal.deployment.name, replicas).then(() => {
|
scaleDeploymentCmd(clusterId, activeModal.deployment.namespace, activeModal.deployment.name, replicas).then(() => {
|
||||||
setActiveModal(null);
|
setActiveModal(null);
|
||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
})
|
})
|
||||||
@ -201,7 +201,7 @@ export function DeploymentList({ deployments, clusterId, namespace, onRefresh }:
|
|||||||
<EditResourceModal
|
<EditResourceModal
|
||||||
isOpen
|
isOpen
|
||||||
clusterId={clusterId}
|
clusterId={clusterId}
|
||||||
namespace={namespace}
|
namespace={activeModal.deployment.namespace}
|
||||||
resourceType="deployments"
|
resourceType="deployments"
|
||||||
resourceName={activeModal.deployment.name}
|
resourceName={activeModal.deployment.name}
|
||||||
initialYaml={activeModal.yaml}
|
initialYaml={activeModal.yaml}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ type ActiveModal =
|
|||||||
| { type: "delete"; ss: StatefulSetInfo }
|
| { type: "delete"; ss: StatefulSetInfo }
|
||||||
| null;
|
| null;
|
||||||
|
|
||||||
export function StatefulSetList({ statefulsets, clusterId, namespace, onRefresh }: StatefulSetListProps) {
|
export function StatefulSetList({ statefulsets, clusterId, namespace: _namespace, onRefresh }: StatefulSetListProps) {
|
||||||
const [activeModal, setActiveModal] = useState<ActiveModal>(null);
|
const [activeModal, setActiveModal] = useState<ActiveModal>(null);
|
||||||
const [isActing, setIsActing] = useState(false);
|
const [isActing, setIsActing] = useState(false);
|
||||||
const [actionError, setActionError] = useState<string | null>(null);
|
const [actionError, setActionError] = useState<string | null>(null);
|
||||||
@ -35,7 +35,7 @@ export function StatefulSetList({ statefulsets, clusterId, namespace, onRefresh
|
|||||||
const openEdit = async (ss: StatefulSetInfo) => {
|
const openEdit = async (ss: StatefulSetInfo) => {
|
||||||
setActionError(null);
|
setActionError(null);
|
||||||
try {
|
try {
|
||||||
const yaml = await getResourceYamlCmd(clusterId, "statefulsets", namespace, ss.name);
|
const yaml = await getResourceYamlCmd(clusterId, "statefulsets", ss.namespace, ss.name);
|
||||||
setActiveModal({ type: "edit", ss, yaml });
|
setActiveModal({ type: "edit", ss, yaml });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setActionError(err instanceof Error ? err.message : String(err));
|
setActionError(err instanceof Error ? err.message : String(err));
|
||||||
@ -46,7 +46,7 @@ export function StatefulSetList({ statefulsets, clusterId, namespace, onRefresh
|
|||||||
if (activeModal?.type !== "restart") return;
|
if (activeModal?.type !== "restart") return;
|
||||||
setIsActing(true);
|
setIsActing(true);
|
||||||
try {
|
try {
|
||||||
await restartStatefulsetCmd(clusterId, namespace, activeModal.ss.name);
|
await restartStatefulsetCmd(clusterId, activeModal.ss.namespace, activeModal.ss.name);
|
||||||
setActiveModal(null);
|
setActiveModal(null);
|
||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -60,7 +60,7 @@ export function StatefulSetList({ statefulsets, clusterId, namespace, onRefresh
|
|||||||
if (activeModal?.type !== "delete") return;
|
if (activeModal?.type !== "delete") return;
|
||||||
setIsActing(true);
|
setIsActing(true);
|
||||||
try {
|
try {
|
||||||
await deleteResourceCmd(clusterId, "statefulsets", namespace, activeModal.ss.name);
|
await deleteResourceCmd(clusterId, "statefulsets", activeModal.ss.namespace, activeModal.ss.name);
|
||||||
setActiveModal(null);
|
setActiveModal(null);
|
||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
} finally {
|
} finally {
|
||||||
@ -140,7 +140,7 @@ export function StatefulSetList({ statefulsets, clusterId, namespace, onRefresh
|
|||||||
resourceName={activeModal.ss.name}
|
resourceName={activeModal.ss.name}
|
||||||
currentReplicas={activeModal.ss.replicas}
|
currentReplicas={activeModal.ss.replicas}
|
||||||
onScale={(replicas) =>
|
onScale={(replicas) =>
|
||||||
scaleStatefulsetCmd(clusterId, namespace, activeModal.ss.name, replicas).then(() => {
|
scaleStatefulsetCmd(clusterId, activeModal.ss.namespace, activeModal.ss.name, replicas).then(() => {
|
||||||
setActiveModal(null);
|
setActiveModal(null);
|
||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
})
|
})
|
||||||
@ -164,7 +164,7 @@ export function StatefulSetList({ statefulsets, clusterId, namespace, onRefresh
|
|||||||
<EditResourceModal
|
<EditResourceModal
|
||||||
isOpen
|
isOpen
|
||||||
clusterId={clusterId}
|
clusterId={clusterId}
|
||||||
namespace={namespace}
|
namespace={activeModal.ss.namespace}
|
||||||
resourceType="statefulsets"
|
resourceType="statefulsets"
|
||||||
resourceName={activeModal.ss.name}
|
resourceName={activeModal.ss.name}
|
||||||
initialYaml={activeModal.yaml}
|
initialYaml={activeModal.yaml}
|
||||||
|
|||||||
695
tests/unit/WorkloadListActions.test.tsx
Normal file
695
tests/unit/WorkloadListActions.test.tsx
Normal file
@ -0,0 +1,695 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
|
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
|
||||||
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
import { DeploymentList } from "@/components/Kubernetes/DeploymentList";
|
||||||
|
import { StatefulSetList } from "@/components/Kubernetes/StatefulSetList";
|
||||||
|
import { DaemonSetList } from "@/components/Kubernetes/DaemonSetList";
|
||||||
|
import { ReplicaSetList } from "@/components/Kubernetes/ReplicaSetList";
|
||||||
|
import { JobList } from "@/components/Kubernetes/JobList";
|
||||||
|
import { CronJobList } from "@/components/Kubernetes/CronJobList";
|
||||||
|
import type {
|
||||||
|
DeploymentInfo,
|
||||||
|
StatefulSetInfo,
|
||||||
|
DaemonSetInfo,
|
||||||
|
ReplicaSetInfo,
|
||||||
|
JobInfo,
|
||||||
|
CronJobInfo,
|
||||||
|
} from "@/lib/tauriCommands";
|
||||||
|
|
||||||
|
type MockedInvoke = typeof invoke & {
|
||||||
|
mockResolvedValue: (v: unknown) => void;
|
||||||
|
mockImplementation: (fn: (cmd: string) => Promise<unknown>) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockInvoke = invoke as MockedInvoke;
|
||||||
|
|
||||||
|
// Helper: open the action menu for the first Actions button, then click a menu item by label
|
||||||
|
async function openMenuAndClick(label: string) {
|
||||||
|
const btn = screen.getAllByRole("button", { name: /actions/i })[0];
|
||||||
|
fireEvent.click(btn);
|
||||||
|
const item = await screen.findByRole("button", { name: new RegExp(label, "i") });
|
||||||
|
fireEvent.click(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── DeploymentList ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe("DeploymentList — actions use item.namespace not filter prop", () => {
|
||||||
|
const deployment: DeploymentInfo = {
|
||||||
|
name: "nginx",
|
||||||
|
namespace: "kube-system",
|
||||||
|
ready: "1/1",
|
||||||
|
up_to_date: "1",
|
||||||
|
available: "1",
|
||||||
|
replicas: 1,
|
||||||
|
age: "1d",
|
||||||
|
labels: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
mockInvoke.mockResolvedValue("apiVersion: apps/v1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("openEdit calls getResourceYamlCmd with item.namespace, not 'all'", async () => {
|
||||||
|
render(
|
||||||
|
<DeploymentList
|
||||||
|
deployments={[deployment]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Edit");
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("get_resource_yaml", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "deployments",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "nginx",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleDelete calls deleteResourceCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "delete_resource") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DeploymentList
|
||||||
|
deployments={[deployment]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Delete");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("delete_resource", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "deployments",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "nginx",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ScaleModal onScale calls scaleDeploymentCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DeploymentList
|
||||||
|
deployments={[deployment]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Scale");
|
||||||
|
const scaleBtn = await screen.findByRole("button", { name: /scale/i });
|
||||||
|
fireEvent.click(scaleBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("scale_deployment", {
|
||||||
|
clusterId: "c1",
|
||||||
|
namespace: "kube-system",
|
||||||
|
deploymentName: "nginx",
|
||||||
|
replicas: expect.any(Number),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleRestart calls restartDeploymentCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "restart_deployment") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DeploymentList
|
||||||
|
deployments={[deployment]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Restart");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm|restart/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("restart_deployment", {
|
||||||
|
clusterId: "c1",
|
||||||
|
namespace: "kube-system",
|
||||||
|
deploymentName: "nginx",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleRollback calls rollbackDeploymentCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "rollback_deployment") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DeploymentList
|
||||||
|
deployments={[deployment]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Rollback");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm|rollback/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("rollback_deployment", {
|
||||||
|
clusterId: "c1",
|
||||||
|
namespace: "kube-system",
|
||||||
|
deploymentName: "nginx",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── StatefulSetList ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe("StatefulSetList — actions use item.namespace not filter prop", () => {
|
||||||
|
const ss: StatefulSetInfo = {
|
||||||
|
name: "postgres",
|
||||||
|
namespace: "kube-system",
|
||||||
|
ready: "1/1",
|
||||||
|
replicas: 1,
|
||||||
|
age: "2d",
|
||||||
|
labels: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
mockInvoke.mockResolvedValue("apiVersion: apps/v1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("openEdit calls getResourceYamlCmd with item.namespace, not 'all'", async () => {
|
||||||
|
render(
|
||||||
|
<StatefulSetList
|
||||||
|
statefulsets={[ss]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Edit");
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("get_resource_yaml", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "statefulsets",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "postgres",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleDelete calls deleteResourceCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "delete_resource") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<StatefulSetList
|
||||||
|
statefulsets={[ss]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Delete");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("delete_resource", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "statefulsets",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "postgres",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ScaleModal onScale calls scaleStatefulsetCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
render(
|
||||||
|
<StatefulSetList
|
||||||
|
statefulsets={[ss]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Scale");
|
||||||
|
const scaleBtn = await screen.findByRole("button", { name: /scale/i });
|
||||||
|
fireEvent.click(scaleBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("scale_statefulset", {
|
||||||
|
clusterId: "c1",
|
||||||
|
namespace: "kube-system",
|
||||||
|
name: "postgres",
|
||||||
|
replicas: expect.any(Number),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleRestart calls restartStatefulsetCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "restart_statefulset") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<StatefulSetList
|
||||||
|
statefulsets={[ss]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Restart");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm|restart/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("restart_statefulset", {
|
||||||
|
clusterId: "c1",
|
||||||
|
namespace: "kube-system",
|
||||||
|
name: "postgres",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── DaemonSetList ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe("DaemonSetList — actions use item.namespace not filter prop", () => {
|
||||||
|
const ds: DaemonSetInfo = {
|
||||||
|
name: "fluentd",
|
||||||
|
namespace: "kube-system",
|
||||||
|
desired: 3,
|
||||||
|
current: 3,
|
||||||
|
ready: 3,
|
||||||
|
up_to_date: 3,
|
||||||
|
available: 3,
|
||||||
|
age: "5d",
|
||||||
|
labels: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
mockInvoke.mockResolvedValue("apiVersion: apps/v1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("openEdit calls getResourceYamlCmd with item.namespace, not 'all'", async () => {
|
||||||
|
render(
|
||||||
|
<DaemonSetList
|
||||||
|
daemonsets={[ds]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Edit");
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("get_resource_yaml", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "daemonsets",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "fluentd",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleDelete calls deleteResourceCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "delete_resource") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DaemonSetList
|
||||||
|
daemonsets={[ds]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Delete");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("delete_resource", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "daemonsets",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "fluentd",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleRestart calls restartDaemonsetCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "restart_daemonset") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DaemonSetList
|
||||||
|
daemonsets={[ds]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Restart");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm|restart/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("restart_daemonset", {
|
||||||
|
clusterId: "c1",
|
||||||
|
namespace: "kube-system",
|
||||||
|
name: "fluentd",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── ReplicaSetList ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe("ReplicaSetList — actions use item.namespace not filter prop", () => {
|
||||||
|
const rs: ReplicaSetInfo = {
|
||||||
|
name: "nginx-abc12",
|
||||||
|
namespace: "kube-system",
|
||||||
|
replicas: 2,
|
||||||
|
ready: "2",
|
||||||
|
age: "3d",
|
||||||
|
labels: { app: "nginx" },
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
mockInvoke.mockResolvedValue("apiVersion: apps/v1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("openEdit calls getResourceYamlCmd with item.namespace, not 'all'", async () => {
|
||||||
|
render(
|
||||||
|
<ReplicaSetList
|
||||||
|
replicaSets={[rs]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Edit");
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("get_resource_yaml", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "replicasets",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "nginx-abc12",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleDelete calls deleteResourceCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "delete_resource") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<ReplicaSetList
|
||||||
|
replicaSets={[rs]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Delete");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("delete_resource", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "replicasets",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "nginx-abc12",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ScaleModal onScale calls scaleReplicasetCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
render(
|
||||||
|
<ReplicaSetList
|
||||||
|
replicaSets={[rs]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Scale");
|
||||||
|
const scaleBtn = await screen.findByRole("button", { name: /scale/i });
|
||||||
|
fireEvent.click(scaleBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("scale_replicaset", {
|
||||||
|
clusterId: "c1",
|
||||||
|
namespace: "kube-system",
|
||||||
|
name: "nginx-abc12",
|
||||||
|
replicas: expect.any(Number),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── JobList ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe("JobList — actions use item.namespace not filter prop", () => {
|
||||||
|
const job: JobInfo = {
|
||||||
|
name: "db-migrate",
|
||||||
|
namespace: "kube-system",
|
||||||
|
completions: "1/1",
|
||||||
|
duration: "45s",
|
||||||
|
age: "1d",
|
||||||
|
labels: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
mockInvoke.mockResolvedValue("apiVersion: batch/v1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("openEdit calls getResourceYamlCmd with item.namespace, not 'all'", async () => {
|
||||||
|
render(
|
||||||
|
<JobList
|
||||||
|
jobs={[job]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Edit");
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("get_resource_yaml", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "jobs",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "db-migrate",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleDelete calls deleteResourceCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "delete_resource") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<JobList
|
||||||
|
jobs={[job]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Delete");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("delete_resource", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "jobs",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "db-migrate",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── CronJobList ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe("CronJobList — actions use item.namespace not filter prop", () => {
|
||||||
|
const cj: CronJobInfo = {
|
||||||
|
name: "backup",
|
||||||
|
namespace: "kube-system",
|
||||||
|
schedule: "0 2 * * *",
|
||||||
|
active: 0,
|
||||||
|
last_schedule: "1h",
|
||||||
|
age: "10d",
|
||||||
|
labels: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
mockInvoke.mockResolvedValue("apiVersion: batch/v1");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("openEdit calls getResourceYamlCmd with item.namespace, not 'all'", async () => {
|
||||||
|
render(
|
||||||
|
<CronJobList
|
||||||
|
cronJobs={[cj]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Edit");
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("get_resource_yaml", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "cronjobs",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "backup",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleDelete calls deleteResourceCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "delete_resource") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<CronJobList
|
||||||
|
cronJobs={[cj]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Delete");
|
||||||
|
const confirmBtn = await screen.findByRole("button", { name: /delete|confirm/i });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("delete_resource", {
|
||||||
|
clusterId: "c1",
|
||||||
|
resourceType: "cronjobs",
|
||||||
|
namespace: "kube-system",
|
||||||
|
resourceName: "backup",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleSuspend calls suspendCronjobCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "suspend_cronjob") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<CronJobList
|
||||||
|
cronJobs={[cj]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Suspend");
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("suspend_cronjob", {
|
||||||
|
clusterId: "c1",
|
||||||
|
namespace: "kube-system",
|
||||||
|
name: "backup",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handleTrigger calls triggerCronjobCmd with item.namespace, not 'all'", async () => {
|
||||||
|
mockInvoke.mockImplementation(async (cmd: string) => {
|
||||||
|
if (cmd === "trigger_cronjob") return undefined;
|
||||||
|
return "yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<CronJobList
|
||||||
|
cronJobs={[cj]}
|
||||||
|
clusterId="c1"
|
||||||
|
namespace="all"
|
||||||
|
onRefresh={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
await openMenuAndClick("Trigger");
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(invoke).toHaveBeenCalledWith("trigger_cronjob", {
|
||||||
|
clusterId: "c1",
|
||||||
|
namespace: "kube-system",
|
||||||
|
name: "backup",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user