Download __tests__/api/cloud-conversation-service.test.ts from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 9.77 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/__tests__/api/cloud-conversation-service.test.ts
- Command line
-
hf download hf://SaylorTwift/openhands/__tests__/api/cloud-conversation-service.test.ts
-
curl -L -o cloud-conversation-service.test.ts https://huggingface.co/SaylorTwift/openhands/resolve/main/__tests__/api/cloud-conversation-service.test.ts
9.77 kB
| import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; | |
| import { | |
| __resetActiveStoreForTests, | |
| setActiveSelection, | |
| setRegisteredBackends, | |
| } from "#/api/backend-registry/active-store"; | |
| import type { Backend } from "#/api/backend-registry/types"; | |
| import { setStoredConversationMetadata } from "#/api/conversation-metadata-store"; | |
| import { | |
| batchGetCloudConversations, | |
| createCloudAppConversation, | |
| pickCloudBackendForLaunch, | |
| searchCloudConversations, | |
| } from "#/api/cloud/conversation-service.api"; | |
| import { AGENT_CANVAS_CLIENT_HEADERS } from "#/api/client-source"; | |
| const { mockCallCloudProxy } = vi.hoisted(() => ({ | |
| mockCallCloudProxy: vi.fn(), | |
| })); | |
| vi.mock("#/api/cloud/proxy", () => ({ | |
| callCloudProxy: (...args: unknown[]) => mockCallCloudProxy(...args), | |
| })); | |
| const cloudBackend: Backend = { | |
| id: "cloud-1", | |
| kind: "cloud", | |
| host: "https://app.all-hands.dev", | |
| apiKey: "secret", | |
| name: "Cloud", | |
| }; | |
| describe("cloud conversation-service overlay", () => { | |
| beforeEach(() => { | |
| window.localStorage.clear(); | |
| __resetActiveStoreForTests(); | |
| setRegisteredBackends([cloudBackend]); | |
| setActiveSelection({ backendId: cloudBackend.id, orgId: null }); | |
| mockCallCloudProxy.mockReset(); | |
| }); | |
| afterEach(() => { | |
| window.localStorage.clear(); | |
| __resetActiveStoreForTests(); | |
| mockCallCloudProxy.mockReset(); | |
| }); | |
| it("marks Cloud conversation starts as originating from Agent Canvas", async () => { | |
| mockCallCloudProxy.mockResolvedValueOnce({ id: "start-task" }); | |
| await createCloudAppConversation({ | |
| initial_message: null, | |
| selected_repository: null, | |
| selected_branch: null, | |
| git_provider: null, | |
| plugins: null, | |
| parent_conversation_id: null, | |
| }); | |
| expect(mockCallCloudProxy).toHaveBeenCalledWith( | |
| expect.objectContaining({ | |
| path: "/api/v1/app-conversations", | |
| headers: AGENT_CANVAS_CLIENT_HEADERS, | |
| }), | |
| ); | |
| }); | |
| it("starts a Cloud conversation on a registered backend while a local one is active", async () => { | |
| const localBackend: Backend = { | |
| id: "local-1", | |
| kind: "local", | |
| host: "http://localhost:8000", | |
| apiKey: "local-key", | |
| name: "Local", | |
| }; | |
| setRegisteredBackends([localBackend, cloudBackend]); | |
| setActiveSelection({ backendId: localBackend.id, orgId: null }); | |
| mockCallCloudProxy.mockResolvedValueOnce({ id: "start-task" }); | |
| await createCloudAppConversation( | |
| { | |
| initial_message: null, | |
| selected_repository: null, | |
| selected_branch: null, | |
| git_provider: null, | |
| parent_conversation_id: null, | |
| }, | |
| pickCloudBackendForLaunch()!, | |
| ); | |
| expect(mockCallCloudProxy).toHaveBeenCalledWith( | |
| expect.objectContaining({ | |
| backend: cloudBackend, | |
| path: "/api/v1/app-conversations", | |
| headers: AGENT_CANVAS_CLIENT_HEADERS, | |
| }), | |
| ); | |
| }); | |
| it("has no cloud backend to launch on when only local backends are registered", () => { | |
| const localBackend: Backend = { | |
| id: "local-1", | |
| kind: "local", | |
| host: "http://localhost:8000", | |
| apiKey: "local-key", | |
| name: "Local", | |
| }; | |
| setRegisteredBackends([localBackend]); | |
| setActiveSelection({ backendId: localBackend.id, orgId: null }); | |
| expect(pickCloudBackendForLaunch()).toBeNull(); | |
| }); | |
| it("overlays locally-stored repo selection onto batchGetCloudConversations results when the server returns nulls", async () => { | |
| setStoredConversationMetadata("conv-1", { | |
| selected_repository: "octocat/hello-world", | |
| selected_branch: "main", | |
| git_provider: "github", | |
| }); | |
| mockCallCloudProxy.mockResolvedValueOnce([ | |
| { | |
| id: "conv-1", | |
| title: "Hello", | |
| selected_repository: null, | |
| selected_branch: null, | |
| git_provider: null, | |
| }, | |
| ]); | |
| const [conversation] = await batchGetCloudConversations(["conv-1"]); | |
| expect(conversation).not.toBeNull(); | |
| expect(conversation?.selected_repository).toBe("octocat/hello-world"); | |
| expect(conversation?.selected_branch).toBe("main"); | |
| expect(conversation?.git_provider).toBe("github"); | |
| }); | |
| it("prefers the cloud server values over locally-stored selections when present", async () => { | |
| setStoredConversationMetadata("conv-1", { | |
| selected_repository: "stale/local", | |
| selected_branch: "stale-branch", | |
| git_provider: "gitlab", | |
| }); | |
| mockCallCloudProxy.mockResolvedValueOnce([ | |
| { | |
| id: "conv-1", | |
| title: "Hello", | |
| selected_repository: "octocat/hello-world", | |
| selected_branch: "main", | |
| git_provider: "github", | |
| }, | |
| ]); | |
| const [conversation] = await batchGetCloudConversations(["conv-1"]); | |
| expect(conversation?.selected_repository).toBe("octocat/hello-world"); | |
| expect(conversation?.selected_branch).toBe("main"); | |
| expect(conversation?.git_provider).toBe("github"); | |
| }); | |
| it("leaves null entries untouched when the cloud server returns null for a missing conversation", async () => { | |
| mockCallCloudProxy.mockResolvedValueOnce([null]); | |
| const result = await batchGetCloudConversations(["missing"]); | |
| expect(result).toEqual([null]); | |
| }); | |
| it("returns an empty array without calling the proxy when no ids are provided", async () => { | |
| const result = await batchGetCloudConversations([]); | |
| expect(result).toEqual([]); | |
| expect(mockCallCloudProxy).not.toHaveBeenCalled(); | |
| }); | |
| it("only fills server-side nulls — server-populated fields are preserved", async () => { | |
| setStoredConversationMetadata("conv-1", { | |
| selected_repository: "local/repo", | |
| selected_branch: "local-branch", | |
| git_provider: "gitlab", | |
| }); | |
| mockCallCloudProxy.mockResolvedValueOnce([ | |
| { | |
| id: "conv-1", | |
| title: "Hello", | |
| selected_repository: "server/repo", | |
| selected_branch: null, | |
| git_provider: null, | |
| }, | |
| ]); | |
| const [conversation] = await batchGetCloudConversations(["conv-1"]); | |
| // Server-populated field wins… | |
| expect(conversation?.selected_repository).toBe("server/repo"); | |
| // …but the two nulls are filled in from local storage. | |
| expect(conversation?.selected_branch).toBe("local-branch"); | |
| expect(conversation?.git_provider).toBe("gitlab"); | |
| }); | |
| it("leaves server-side nulls null when local storage only has a partial selection", async () => { | |
| setStoredConversationMetadata("conv-1", { | |
| selected_repository: "local/repo", | |
| selected_branch: null, | |
| git_provider: null, | |
| }); | |
| mockCallCloudProxy.mockResolvedValueOnce([ | |
| { | |
| id: "conv-1", | |
| title: "Hello", | |
| selected_repository: null, | |
| selected_branch: null, | |
| git_provider: null, | |
| }, | |
| ]); | |
| const [conversation] = await batchGetCloudConversations(["conv-1"]); | |
| expect(conversation?.selected_repository).toBe("local/repo"); | |
| expect(conversation?.selected_branch).toBeNull(); | |
| expect(conversation?.git_provider).toBeNull(); | |
| }); | |
| it("applies the overlay independently per conversation when batching multiple ids", async () => { | |
| setStoredConversationMetadata("conv-1", { | |
| selected_repository: "octocat/hello-world", | |
| selected_branch: "main", | |
| git_provider: "github", | |
| }); | |
| setStoredConversationMetadata("conv-2", { | |
| selected_repository: "octocat/other", | |
| selected_branch: "dev", | |
| git_provider: "github", | |
| }); | |
| mockCallCloudProxy.mockResolvedValueOnce([ | |
| { | |
| id: "conv-1", | |
| title: "First", | |
| selected_repository: null, | |
| selected_branch: null, | |
| git_provider: null, | |
| }, | |
| { | |
| id: "conv-2", | |
| title: "Second", | |
| // Server returns the repo already — overlay should NOT override. | |
| selected_repository: "server/two", | |
| selected_branch: null, | |
| git_provider: null, | |
| }, | |
| { | |
| id: "conv-3", | |
| title: "Third (no local data)", | |
| selected_repository: null, | |
| selected_branch: null, | |
| git_provider: null, | |
| }, | |
| ]); | |
| const conversations = await batchGetCloudConversations([ | |
| "conv-1", | |
| "conv-2", | |
| "conv-3", | |
| ]); | |
| expect(conversations[0]?.selected_repository).toBe("octocat/hello-world"); | |
| expect(conversations[0]?.selected_branch).toBe("main"); | |
| expect(conversations[0]?.git_provider).toBe("github"); | |
| expect(conversations[1]?.selected_repository).toBe("server/two"); | |
| expect(conversations[1]?.selected_branch).toBe("dev"); | |
| expect(conversations[1]?.git_provider).toBe("github"); | |
| expect(conversations[2]?.selected_repository).toBeNull(); | |
| expect(conversations[2]?.selected_branch).toBeNull(); | |
| expect(conversations[2]?.git_provider).toBeNull(); | |
| }); | |
| it("overlays repo selection on each item returned from searchCloudConversations", async () => { | |
| setStoredConversationMetadata("conv-1", { | |
| selected_repository: "octocat/hello-world", | |
| selected_branch: "main", | |
| git_provider: "github", | |
| }); | |
| mockCallCloudProxy.mockResolvedValueOnce({ | |
| items: [ | |
| { | |
| id: "conv-1", | |
| title: "Hello", | |
| selected_repository: null, | |
| selected_branch: null, | |
| git_provider: null, | |
| }, | |
| { | |
| id: "conv-2", | |
| title: "Other", | |
| selected_repository: null, | |
| selected_branch: null, | |
| git_provider: null, | |
| }, | |
| ], | |
| next_page_id: null, | |
| }); | |
| const page = await searchCloudConversations(20); | |
| expect(page.items[0].selected_repository).toBe("octocat/hello-world"); | |
| expect(page.items[0].selected_branch).toBe("main"); | |
| expect(page.items[0].git_provider).toBe("github"); | |
| expect(page.items[1].selected_repository).toBeNull(); | |
| }); | |
| }); | |