mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-09-28 14:13:26 +08:00
Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: Engel Nyst <engel.nyst@gmail.com> Co-authored-by: allhands-bot <allhands-bot@users.noreply.github.com>
144 lines
4.6 KiB
TypeScript
144 lines
4.6 KiB
TypeScript
import { http, HttpResponse } from "msw";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { server } from "#/mocks/node";
|
|
|
|
const SECRETS_URL = "http://localhost:3000/api/settings/secrets";
|
|
const UNEXPECTED_REQUEST_HANDLER = http.all(
|
|
"*",
|
|
() => new HttpResponse(null, { status: 599 }),
|
|
);
|
|
|
|
const secretUrl = (name: string) =>
|
|
`${SECRETS_URL}/${encodeURIComponent(name)}`;
|
|
|
|
beforeEach(async () => {
|
|
vi.resetModules();
|
|
const { SECRETS_HANDLERS } = await import("#/mocks/secrets-handlers");
|
|
server.resetHandlers(...SECRETS_HANDLERS, UNEXPECTED_REQUEST_HANDLER);
|
|
});
|
|
|
|
describe("mock secrets API", () => {
|
|
it("declines a secret-list request outside the supported API root", async () => {
|
|
const response = await fetch(SECRETS_URL.replace("/api/", "/extra/api/"));
|
|
expect(response.status).toBe(599);
|
|
});
|
|
|
|
it("lists secret metadata without exposing stored values", async () => {
|
|
const response = await fetch(SECRETS_URL);
|
|
const body = (await response.json()) as {
|
|
secrets: Array<Record<string, unknown>>;
|
|
};
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(body).toEqual({
|
|
secrets: [
|
|
{ name: "OpenAI_API_Key", description: "OpenAI API Key" },
|
|
{
|
|
name: "Google_Maps_API_Key",
|
|
description: "Google Maps API Key",
|
|
},
|
|
],
|
|
});
|
|
expect(body.secrets.every((secret) => !("value" in secret))).toBe(true);
|
|
});
|
|
|
|
it("returns a stored value as plain text and reports an unknown name", async () => {
|
|
const found = await fetch(secretUrl("OpenAI_API_Key"));
|
|
|
|
expect(found.status).toBe(200);
|
|
expect(found.headers.get("content-type")).toBe("text/plain");
|
|
await expect(found.text()).resolves.toBe("test-123");
|
|
|
|
const secondSeed = await fetch(secretUrl("Google_Maps_API_Key"));
|
|
|
|
expect(secondSeed.status).toBe(200);
|
|
await expect(secondSeed.text()).resolves.toBe("test-123");
|
|
|
|
const missing = await fetch(secretUrl("unknown-secret"));
|
|
|
|
expect(missing.status).toBe(404);
|
|
await expect(missing.json()).resolves.toEqual({
|
|
detail: "Secret not found",
|
|
});
|
|
});
|
|
|
|
it("persists create, update, list, read, and delete transitions", async () => {
|
|
const name = "Coverage_API_Key";
|
|
|
|
try {
|
|
const created = await fetch(SECRETS_URL, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
name,
|
|
value: "initial-value",
|
|
description: "Coverage secret",
|
|
}),
|
|
});
|
|
|
|
expect(created.status).toBe(200);
|
|
await expect(created.json()).resolves.toEqual({
|
|
name,
|
|
description: "Coverage secret",
|
|
});
|
|
|
|
const listed = await fetch(SECRETS_URL);
|
|
const listedBody = (await listed.json()) as {
|
|
secrets: Array<Record<string, unknown>>;
|
|
};
|
|
expect(listedBody.secrets).toContainEqual({
|
|
name,
|
|
description: "Coverage secret",
|
|
});
|
|
expect(
|
|
listedBody.secrets.find((secret) => secret.name === name),
|
|
).not.toHaveProperty("value");
|
|
|
|
const firstRead = await fetch(secretUrl(name));
|
|
await expect(firstRead.text()).resolves.toBe("initial-value");
|
|
|
|
const updated = await fetch(SECRETS_URL, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ name, value: "updated-value" }),
|
|
});
|
|
await expect(updated.json()).resolves.toEqual({ name });
|
|
|
|
const secondRead = await fetch(secretUrl(name));
|
|
await expect(secondRead.text()).resolves.toBe("updated-value");
|
|
|
|
const deleted = await fetch(secretUrl(name), { method: "DELETE" });
|
|
expect(deleted.status).toBe(200);
|
|
await expect(deleted.json()).resolves.toEqual({ deleted: true });
|
|
|
|
const afterDelete = await fetch(secretUrl(name));
|
|
expect(afterDelete.status).toBe(404);
|
|
|
|
const deleteAgain = await fetch(secretUrl(name), { method: "DELETE" });
|
|
expect(deleteAgain.status).toBe(404);
|
|
await expect(deleteAgain.json()).resolves.toEqual({
|
|
detail: "Secret not found",
|
|
});
|
|
} finally {
|
|
await fetch(secretUrl(name), { method: "DELETE" });
|
|
}
|
|
});
|
|
|
|
it.each([
|
|
["null body", null],
|
|
["missing name", { value: "secret-value" }],
|
|
["missing value", { name: "Incomplete_Secret" }],
|
|
])("rejects an upsert with a %s", async (_case, payload) => {
|
|
const response = await fetch(SECRETS_URL, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
expect(response.status).toBe(400);
|
|
await expect(response.json()).resolves.toEqual({
|
|
detail: "name and value are required",
|
|
});
|
|
});
|
|
});
|