From 21de6bf167b3bebabe7a6953f6eeefcabc5fd74e Mon Sep 17 00:00:00 2001 From: Lucas Manchine Date: Wed, 24 Sep 2025 10:26:36 -0300 Subject: [PATCH] test: add missing test --- .../server/mechanizeDockerContainer.test.ts | 105 ++++++++++++++++++ .../cluster/modify-swarm-settings.tsx | 18 ++- 2 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 apps/dokploy/__test__/server/mechanizeDockerContainer.test.ts diff --git a/apps/dokploy/__test__/server/mechanizeDockerContainer.test.ts b/apps/dokploy/__test__/server/mechanizeDockerContainer.test.ts new file mode 100644 index 000000000..96fa796df --- /dev/null +++ b/apps/dokploy/__test__/server/mechanizeDockerContainer.test.ts @@ -0,0 +1,105 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import type { ApplicationNested } from "@dokploy/server/utils/builders"; +import { mechanizeDockerContainer } from "@dokploy/server/utils/builders"; + +type MockCreateServiceOptions = { + StopGracePeriod?: number; + [key: string]: unknown; +}; + +const { + inspectMock, + getServiceMock, + createServiceMock, + getRemoteDockerMock, +} = vi.hoisted(() => { + const inspect = vi.fn<[], Promise>(); + const getService = vi.fn(() => ({ inspect })); + const createService = vi.fn<[MockCreateServiceOptions], Promise>(async () => + undefined, + ); + const getRemoteDocker = vi.fn(async () => ({ + getService, + createService, + })); + return { + inspectMock: inspect, + getServiceMock: getService, + createServiceMock: createService, + getRemoteDockerMock: getRemoteDocker, + }; +}); + +vi.mock("@dokploy/server/utils/servers/remote-docker", () => ({ + getRemoteDocker: getRemoteDockerMock, +})); + +const createApplication = ( + overrides: Partial = {}, +): ApplicationNested => ({ + appName: "test-app", + buildType: "dockerfile", + env: null, + mounts: [], + cpuLimit: null, + memoryLimit: null, + memoryReservation: null, + cpuReservation: null, + command: null, + ports: [], + sourceType: "docker", + dockerImage: "example:latest", + registry: null, + environment: { + project: { env: null }, + env: null, + }, + replicas: 1, + stopGracePeriodSwarm: 0n, + serverId: "server-id", + ...overrides, +} as unknown as ApplicationNested); + +describe("mechanizeDockerContainer", () => { + beforeEach(() => { + inspectMock.mockReset(); + inspectMock.mockRejectedValue(new Error("service not found")); + getServiceMock.mockClear(); + createServiceMock.mockClear(); + getRemoteDockerMock.mockClear(); + getRemoteDockerMock.mockResolvedValue({ + getService: getServiceMock, + createService: createServiceMock, + }); + }); + + it("converts bigint stopGracePeriodSwarm to a number and keeps zero values", async () => { + const application = createApplication({ stopGracePeriodSwarm: 0n }); + + await mechanizeDockerContainer(application); + + expect(createServiceMock).toHaveBeenCalledTimes(1); + const call = createServiceMock.mock.calls[0]; + if (!call) { + throw new Error("createServiceMock should have been called once"); + } + const [settings] = call; + expect(settings.StopGracePeriod).toBe(0); + expect(typeof settings.StopGracePeriod).toBe("number"); + }); + + it("omits StopGracePeriod when stopGracePeriodSwarm is null", async () => { + const application = createApplication({ stopGracePeriodSwarm: null }); + + await mechanizeDockerContainer(application); + + expect(createServiceMock).toHaveBeenCalledTimes(1); + const call = createServiceMock.mock.calls[0]; + if (!call) { + throw new Error("createServiceMock should have been called once"); + } + const [settings] = call; + expect(settings).not.toHaveProperty("StopGracePeriod"); + }); +}); diff --git a/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx b/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx index 344cd4910..b179502aa 100644 --- a/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx @@ -182,6 +182,11 @@ const addSwarmSettings = z.object({ type AddSwarmSettings = z.infer; +const hasStopGracePeriodSwarm = ( + value: unknown, +): value is { stopGracePeriodSwarm: bigint | number | string | null } => + typeof value === "object" && value !== null && "stopGracePeriodSwarm" in value; + interface Props { id: string; type: "postgres" | "mariadb" | "mongo" | "mysql" | "redis" | "application"; @@ -233,6 +238,15 @@ export const AddSwarmSettings = ({ id, type }: Props) => { useEffect(() => { if (data) { + const stopGracePeriodValue = hasStopGracePeriodSwarm(data) + ? data.stopGracePeriodSwarm + : null; + const normalizedStopGracePeriod = + stopGracePeriodValue === null || stopGracePeriodValue === undefined + ? null + : typeof stopGracePeriodValue === "bigint" + ? stopGracePeriodValue + : BigInt(stopGracePeriodValue); form.reset({ healthCheckSwarm: data.healthCheckSwarm ? JSON.stringify(data.healthCheckSwarm, null, 2) @@ -258,9 +272,7 @@ export const AddSwarmSettings = ({ id, type }: Props) => { networkSwarm: data.networkSwarm ? JSON.stringify(data.networkSwarm, null, 2) : null, - stopGracePeriodSwarm: data.stopGracePeriodSwarm - ? BigInt(data.stopGracePeriodSwarm) - : null, + stopGracePeriodSwarm: normalizedStopGracePeriod, }); } }, [form, form.reset, data]);