// @vitest-environment jsdom import { beforeEach, describe, expect, it, vi } from "vitest"; const { axiosMock } = vi.hoisted(() => ({ axiosMock: vi.fn(), })); vi.mock("axios", () => ({ default: axiosMock, })); import { clearReleaseManagerControlApiUrl, getReleaseManagerControlApiUrl, getReleaseSummary, RELEASE_MANAGER_CONTROL_API_STORAGE_KEY, releaseManagerControlApiCandidates, setReleaseManagerControlApiUrl, } from "@/services/superuserReleases.js"; import { configureReleaseRuntime } from "@/services/releaseTimeline.js"; import { deployCoolifyGatewayRoutes } from "@/services/superuserCoolify.js"; import { __configureRequestQueueForTests, __resetRequestQueueForTests } from "@/services/requestQueue.js"; describe("superuser release manager service", () => { beforeEach(() => { axiosMock.mockReset(); localStorage.clear(); sessionStorage.clear(); localStorage.setItem("token", "release-token"); configureReleaseRuntime({ source: "deployment", api_base_url: "https://api.truckwash.io" }); __resetRequestQueueForTests(); __configureRequestQueueForTests({ maxConcurrentGet: 1, maxConcurrentOther: 1, spacingMs: 0, retryByStatusCode: {}, }); }); it("uses the runtime release API before legacy control API overrides", async () => { configureReleaseRuntime({ api_base_url: "https://api-v2.truckwash.io/master/api" }); setReleaseManagerControlApiUrl("https://control.example.test/"); axiosMock.mockResolvedValueOnce({ status: 200, data: { data: { channels: [] } } }); await getReleaseSummary(); expect(getReleaseManagerControlApiUrl()).toBe("https://api-v2.truckwash.io/master/api"); expect(axiosMock).toHaveBeenCalledWith( expect.objectContaining({ url: "https://api-v2.truckwash.io/master/api/superuser/releases", method: "GET", headers: expect.objectContaining({ Authorization: "Bearer release-token" }), }) ); }); it("falls back when the selected API does not have release manager endpoints", async () => { localStorage.setItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY, "https://api.truckwash.io"); axiosMock .mockRejectedValueOnce({ response: { status: 404 } }) .mockResolvedValueOnce({ status: 200, data: { data: { channels: [] } } }); await getReleaseSummary(); expect(axiosMock.mock.calls[0][0].url).toBe("https://api.truckwash.io/superuser/releases"); expect(axiosMock.mock.calls[1][0].url).toBe("https://api-v2.truckwash.io/master/api/superuser/releases"); expect(releaseManagerControlApiCandidates()).toContain("https://api-v2.truckwash.io/master/api"); }); it("uses local runtime API only when the release source is local", async () => { configureReleaseRuntime({ source: "local", api_base_url: "/api" }); localStorage.setItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY, "https://api.truckwash.io"); axiosMock.mockResolvedValueOnce({ status: 200, data: { data: { channels: [] } } }); await getReleaseSummary(); expect(releaseManagerControlApiCandidates()).toEqual(["/api"]); expect(getReleaseManagerControlApiUrl()).toBe("/api"); expect(axiosMock).toHaveBeenCalledWith( expect.objectContaining({ url: "/api/superuser/releases", method: "GET", }) ); }); it("can clear the explicit control API override", () => { setReleaseManagerControlApiUrl("https://control.example.test"); clearReleaseManagerControlApiUrl(); expect(localStorage.getItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY)).toBeNull(); expect(releaseManagerControlApiCandidates()).toContain("https://api-v2.truckwash.io/master/api"); }); it("keeps Coolify route deployment on the runtime release API", async () => { configureReleaseRuntime({ api_base_url: "https://api-v2.truckwash.io/master/api" }); setReleaseManagerControlApiUrl("https://control.example.test"); axiosMock.mockResolvedValueOnce({ status: 200, data: { success: true } }); await deployCoolifyGatewayRoutes({ dry_run: true }); expect(axiosMock).toHaveBeenCalledWith( expect.objectContaining({ url: "https://api-v2.truckwash.io/master/api/superuser/coolify/load-balancer/routes/deploy", method: "POST", data: { dry_run: true }, __skipReleaseApiRewrite: true, }) ); }); });