File size: 3,753 Bytes
3201ca6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { Backend } from "#/api/backend-registry/types";
import {
__resetActiveStoreForTests,
setActiveSelection,
setRegisteredBackends,
} from "#/api/backend-registry/active-store";
import { SecretsService } from "#/api/secrets-service";
const { mockListSecrets, mockGetSecret, mockUpsertSecret, mockDeleteSecret } =
vi.hoisted(() => ({
mockListSecrets: vi.fn(),
mockGetSecret: vi.fn(),
mockUpsertSecret: vi.fn(),
mockDeleteSecret: vi.fn(),
}));
vi.mock("@openhands/typescript-client/clients", () => ({
SettingsClient: vi.fn(function SettingsClientMock() {
return {
listSecrets: mockListSecrets,
getSecret: mockGetSecret,
upsertSecret: mockUpsertSecret,
deleteSecret: mockDeleteSecret,
};
}),
}));
const localBackend: Backend = {
id: "local",
name: "Local",
host: "http://127.0.0.1:8000",
apiKey: "",
kind: "local",
};
describe("SecretsService", () => {
beforeEach(() => {
window.localStorage.clear();
__resetActiveStoreForTests();
setRegisteredBackends([localBackend]);
setActiveSelection({ backendId: localBackend.id });
mockListSecrets.mockReset();
mockGetSecret.mockReset();
mockUpsertSecret.mockReset();
mockDeleteSecret.mockReset();
});
afterEach(() => {
__resetActiveStoreForTests();
});
it("lists local secret metadata through the strict read", async () => {
mockListSecrets.mockResolvedValue({
secrets: [{ name: "OPENHANDS_URL", description: "Canvas origin" }],
});
await expect(SecretsService.getSecretsOrThrow()).resolves.toEqual([
{ name: "OPENHANDS_URL", description: "Canvas origin" },
]);
expect(mockListSecrets).toHaveBeenCalledTimes(1);
});
it("preserves the existing value when updating description", async () => {
// Arrange
mockGetSecret.mockResolvedValue("keep-this");
mockUpsertSecret.mockResolvedValue({
name: "OpenAI_API_Key",
description: "Updated description",
});
// Act
await SecretsService.updateSecret(
"OpenAI_API_Key",
"OpenAI_API_Key",
"Updated description",
);
// Assert
expect(mockGetSecret).toHaveBeenCalledWith("OpenAI_API_Key");
expect(mockUpsertSecret).toHaveBeenCalledWith({
name: "OpenAI_API_Key",
value: "keep-this",
description: "Updated description",
});
expect(mockDeleteSecret).not.toHaveBeenCalled();
});
it("overwrites the value without reading the existing one", async () => {
// Arrange
mockUpsertSecret.mockResolvedValue({
name: "OpenAI_API_Key",
description: "Demo secret",
});
// Act
await SecretsService.updateSecret(
"OpenAI_API_Key",
"OpenAI_API_Key",
"Demo secret",
"new-value",
);
// Assert
expect(mockGetSecret).not.toHaveBeenCalled();
expect(mockUpsertSecret).toHaveBeenCalledWith({
name: "OpenAI_API_Key",
value: "new-value",
description: "Demo secret",
});
});
it("renames secrets by re-upserting and removing the old entry", async () => {
// Arrange
mockGetSecret.mockResolvedValue("original-value");
mockUpsertSecret.mockResolvedValue({
name: "New_Key",
description: "Renamed",
});
mockDeleteSecret.mockResolvedValue({ deleted: true });
// Act
await SecretsService.updateSecret("Old_Key", "New_Key", "Renamed");
// Assert
expect(mockGetSecret).toHaveBeenCalledWith("Old_Key");
expect(mockUpsertSecret).toHaveBeenCalledWith({
name: "New_Key",
value: "original-value",
description: "Renamed",
});
expect(mockDeleteSecret).toHaveBeenCalledWith("Old_Key");
});
});
|