Files
pleno-vue/tests/unit/superuser-releases.spec.js
T

130 lines
5.0 KiB
JavaScript

// @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("rejects arbitrary control API overrides before storing or requesting them", async () => {
const attackerUrl = "https://attacker.example.test";
expect(setReleaseManagerControlApiUrl(attackerUrl)).toBe("");
expect(localStorage.getItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY)).toBeNull();
localStorage.setItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY, attackerUrl);
sessionStorage.setItem("release_manager_last_working_control_api_url", attackerUrl);
axiosMock.mockResolvedValueOnce({ status: 200, data: { data: { channels: [] } } });
await getReleaseSummary();
expect(releaseManagerControlApiCandidates()).not.toContain(attackerUrl);
expect(axiosMock).toHaveBeenCalledTimes(1);
expect(axiosMock.mock.calls[0][0].url).not.toContain(attackerUrl);
});
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,
})
);
});
});