File size: 5,061 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
137
138
139
140
141
142
143
144
145
146
147
import { describe, expect, it, vi } from "vitest";
import AgentProfilesService from "#/api/agent-profiles-service/agent-profiles-service.api";

const mocks = vi.hoisted(() => ({
  kind: "local" as "local" | "cloud",
  list: vi.fn(),
  get: vi.fn(),
  save: vi.fn(),
  remove: vi.fn(),
  rename: vi.fn(),
  activate: vi.fn(),
  cloudList: vi.fn(),
  cloudGet: vi.fn(),
  cloudSave: vi.fn(),
  cloudRemove: vi.fn(),
  cloudRename: vi.fn(),
  cloudActivate: vi.fn(),
  Client: vi.fn(),
  clientOptions: vi.fn(),
}));

vi.mock("#/api/backend-registry/active-store", () => ({
  getActiveBackend: () => ({ backend: { kind: mocks.kind } }),
}));

vi.mock("#/api/agent-server-client-options", () => ({
  getAgentServerClientOptions: mocks.clientOptions,
}));

vi.mock("@openhands/typescript-client/clients", () => ({
  AgentProfilesClient: mocks.Client,
}));

vi.mock("#/api/cloud/agent-profiles-service.api", () => ({
  listCloudAgentProfiles: mocks.cloudList,
  getCloudAgentProfile: mocks.cloudGet,
  saveCloudAgentProfile: mocks.cloudSave,
  deleteCloudAgentProfile: mocks.cloudRemove,
  renameCloudAgentProfile: mocks.cloudRename,
  activateCloudAgentProfile: mocks.cloudActivate,
}));

const setupMocks = () => {
  vi.clearAllMocks();
  mocks.kind = "local";
  mocks.clientOptions.mockReturnValue({ host: "http://localhost" });
  mocks.Client.mockImplementation(function AgentProfilesClientMock() {
    return {
      listAgentProfiles: mocks.list,
      getAgentProfile: mocks.get,
      saveAgentProfile: mocks.save,
      deleteAgentProfile: mocks.remove,
      renameAgentProfile: mocks.rename,
      activateAgentProfile: mocks.activate,
    };
  });
  for (const [mock, source] of [
    [mocks.list, "local-list"],
    [mocks.get, "local-get"],
    [mocks.save, "local-save"],
    [mocks.remove, "local-remove"],
    [mocks.rename, "local-rename"],
    [mocks.activate, "local-activate"],
    [mocks.cloudList, "cloud-list"],
    [mocks.cloudGet, "cloud-get"],
    [mocks.cloudSave, "cloud-save"],
    [mocks.cloudRemove, "cloud-remove"],
    [mocks.cloudRename, "cloud-rename"],
    [mocks.cloudActivate, "cloud-activate"],
  ] as const) {
    mock.mockResolvedValue({ source });
  }
};

describe("AgentProfilesService", () => {
  it("exports the seeded default profile name", async () => {
    vi.resetModules();
    const { WELL_KNOWN_DEFAULT_AGENT_PROFILE_NAME } = await import(
      "#/api/agent-profiles-service/agent-profiles-service.api"
    );
    expect(WELL_KNOWN_DEFAULT_AGENT_PROFILE_NAME).toBe("default");
  });

  it("routes every operation through a fresh local SDK client", async () => {
    setupMocks();
    const profile = { agent: "CodeActAgent" } as never;
    const results = await Promise.all([
      AgentProfilesService.listProfiles(),
      AgentProfilesService.getProfile("default", "encrypted"),
      AgentProfilesService.getProfile("plain"),
      AgentProfilesService.saveProfile("default", profile),
      AgentProfilesService.deleteProfile("old"),
      AgentProfilesService.renameProfile("old", "new"),
      AgentProfilesService.activateProfile("profile-id"),
    ]);

    expect(mocks.Client).toHaveBeenCalledTimes(7);
    expect(results).toEqual([
      { source: "local-list" },
      { source: "local-get" },
      { source: "local-get" },
      { source: "local-save" },
      { source: "local-remove" },
      { source: "local-rename" },
      { source: "local-activate" },
    ]);
    expect(mocks.get).toHaveBeenNthCalledWith(1, "default", {
      exposeSecrets: "encrypted",
    });
    expect(mocks.get).toHaveBeenNthCalledWith(2, "plain", {});
    expect(mocks.save).toHaveBeenCalledWith("default", profile);
    expect(mocks.remove).toHaveBeenCalledWith("old");
    expect(mocks.rename).toHaveBeenCalledWith("old", "new");
    expect(mocks.activate).toHaveBeenCalledWith("profile-id");
  });

  it("routes every operation to the cloud service", async () => {
    setupMocks();
    mocks.kind = "cloud";
    const profile = { agent: "CodeActAgent" } as never;
    const results = await Promise.all([
      AgentProfilesService.listProfiles(),
      AgentProfilesService.getProfile("default", "encrypted"),
      AgentProfilesService.saveProfile("default", profile),
      AgentProfilesService.deleteProfile("old"),
      AgentProfilesService.renameProfile("old", "new"),
      AgentProfilesService.activateProfile("profile-id"),
    ]);

    expect(mocks.cloudList).toHaveBeenCalledOnce();
    expect(results).toEqual([
      { source: "cloud-list" },
      { source: "cloud-get" },
      { source: "cloud-save" },
      { source: "cloud-remove" },
      { source: "cloud-rename" },
      { source: "cloud-activate" },
    ]);
    expect(mocks.cloudGet).toHaveBeenCalledWith("default");
    expect(mocks.cloudSave).toHaveBeenCalledWith("default", profile);
    expect(mocks.cloudRemove).toHaveBeenCalledWith("old");
    expect(mocks.cloudRename).toHaveBeenCalledWith("old", "new");
    expect(mocks.cloudActivate).toHaveBeenCalledWith("profile-id");
    expect(mocks.Client).not.toHaveBeenCalled();
  });
});