Download __tests__/api/runtime-service/agent-server-runtime-service.test.ts from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/__tests__/api/runtime-service/agent-server-runtime-service.test.ts
- Command line
-
hf download hf://SaylorTwift/openhands/__tests__/api/runtime-service/agent-server-runtime-service.test.ts
-
curl -L -o agent-server-runtime-service.test.ts https://huggingface.co/SaylorTwift/openhands/resolve/main/__tests__/api/runtime-service/agent-server-runtime-service.test.ts
10.3 kB
| import { FileClient } from "@openhands/typescript-client/clients"; | |
| import { RemoteWorkspace } from "@openhands/typescript-client/workspace/remote-workspace"; | |
| import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; | |
| import { | |
| __resetActiveStoreForTests, | |
| setActiveSelection, | |
| setRegisteredBackends, | |
| } from "#/api/backend-registry/active-store"; | |
| import AgentServerRuntimeService from "#/api/runtime-service/agent-server-runtime-service"; | |
| import { callCloudProxy } from "#/api/cloud/proxy"; | |
| import type { Backend } from "#/api/backend-registry/types"; | |
| // βββ SDK client mocks βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const { executeCommandMock, downloadFileMock } = vi.hoisted(() => ({ | |
| executeCommandMock: vi.fn(), | |
| downloadFileMock: vi.fn(), | |
| })); | |
| vi.mock("@openhands/typescript-client/workspace/remote-workspace", () => ({ | |
| RemoteWorkspace: vi.fn(function RemoteWorkspaceMock() { | |
| return { executeCommand: executeCommandMock }; | |
| }), | |
| })); | |
| vi.mock("@openhands/typescript-client/clients", () => ({ | |
| FileClient: vi.fn(function FileClientMock() { | |
| return { downloadFile: downloadFileMock }; | |
| }), | |
| })); | |
| vi.mock("#/api/agent-server-client-options", () => ({ | |
| getAgentServerClientOptions: vi.fn(() => ({ | |
| host: "http://local-agent.example.com", | |
| apiKey: "local-key", | |
| workingDir: "/workspace/project", | |
| })), | |
| })); | |
| vi.mock("#/api/cloud/proxy", () => ({ | |
| callCloudProxy: vi.fn(), | |
| })); | |
| // βββ Backend fixtures ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const cloudBackend: Backend = { | |
| id: "cloud-1", | |
| name: "Production", | |
| host: "https://app.all-hands.dev", | |
| apiKey: "cloud-api-key", | |
| kind: "cloud", | |
| }; | |
| const CLOUD_CONVERSATION_URL = | |
| "https://runtime.example.com/api/conversations/conv-1"; | |
| const SESSION_KEY = "session-key-abc"; | |
| // βββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function activateCloud() { | |
| setRegisteredBackends([cloudBackend]); | |
| setActiveSelection({ backendId: cloudBackend.id, orgId: null }); | |
| } | |
| // βββ Setup βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| beforeEach(() => { | |
| window.localStorage.clear(); | |
| __resetActiveStoreForTests(); | |
| vi.mocked(RemoteWorkspace).mockClear(); | |
| vi.mocked(FileClient).mockClear(); | |
| executeCommandMock.mockReset(); | |
| downloadFileMock.mockReset(); | |
| vi.mocked(callCloudProxy).mockReset(); | |
| }); | |
| afterEach(() => { | |
| window.localStorage.clear(); | |
| __resetActiveStoreForTests(); | |
| }); | |
| // βββ executeCommand ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| describe("AgentServerRuntimeService.executeCommand", () => { | |
| describe("local backend", () => { | |
| it("creates RemoteWorkspace with resolved options and delegates", async () => { | |
| executeCommandMock.mockResolvedValue({ | |
| exit_code: 0, | |
| stdout: "main\n", | |
| stderr: "", | |
| }); | |
| const result = await AgentServerRuntimeService.executeCommand( | |
| "http://local-agent.example.com/api/conversations/conv-1", | |
| SESSION_KEY, | |
| "git rev-parse --abbrev-ref HEAD", | |
| "/workspace/project", | |
| 10, | |
| ); | |
| expect(RemoteWorkspace).toHaveBeenCalledTimes(1); | |
| expect(executeCommandMock).toHaveBeenCalledWith( | |
| "git rev-parse --abbrev-ref HEAD", | |
| "/workspace/project", | |
| 10, | |
| ); | |
| expect(result).toEqual({ exit_code: 0, stdout: "main\n", stderr: "" }); | |
| }); | |
| it("does not call callCloudProxy for local backends", async () => { | |
| executeCommandMock.mockResolvedValue({ | |
| exit_code: 0, | |
| stdout: "", | |
| stderr: "", | |
| }); | |
| await AgentServerRuntimeService.executeCommand(null, null, "ls", "/", 5); | |
| expect(callCloudProxy).not.toHaveBeenCalled(); | |
| }); | |
| }); | |
| describe("cloud backend", () => { | |
| beforeEach(activateCloud); | |
| it("routes through callCloudProxy with correct path, body, and auth", async () => { | |
| vi.mocked(callCloudProxy).mockResolvedValue({ | |
| exit_code: 0, | |
| stdout: "src/index.ts\n", | |
| stderr: "", | |
| }); | |
| const result = await AgentServerRuntimeService.executeCommand( | |
| CLOUD_CONVERSATION_URL, | |
| SESSION_KEY, | |
| "find . -type f", | |
| "/workspace/project", | |
| 30, | |
| ); | |
| const proxyCall = vi.mocked(callCloudProxy).mock.calls[0][0]; | |
| expect(proxyCall.method).toBe("POST"); | |
| expect(proxyCall.path).toBe("/api/bash/execute_bash_command"); | |
| expect(proxyCall.hostOverride).toBe("https://runtime.example.com"); | |
| expect(proxyCall.body).toEqual({ | |
| command: "find . -type f", | |
| cwd: "/workspace/project", | |
| timeout: 30, | |
| }); | |
| expect(proxyCall.authMode).toBe("session-api-key"); | |
| expect(proxyCall.sessionApiKey).toBe(SESSION_KEY); | |
| expect(proxyCall.timeoutSeconds).toBe(40); | |
| expect(result).toEqual({ | |
| exit_code: 0, | |
| stdout: "src/index.ts\n", | |
| stderr: "", | |
| }); | |
| }); | |
| it("omits cwd from proxy body when not provided", async () => { | |
| vi.mocked(callCloudProxy).mockResolvedValue({ exit_code: 0 }); | |
| await AgentServerRuntimeService.executeCommand( | |
| CLOUD_CONVERSATION_URL, | |
| SESSION_KEY, | |
| "echo hi", | |
| undefined, | |
| 10, | |
| ); | |
| const proxyBody = vi.mocked(callCloudProxy).mock.calls[0][0].body as Record<string, unknown>; | |
| expect(proxyBody).not.toHaveProperty("cwd"); | |
| expect(proxyBody.command).toBe("echo hi"); | |
| }); | |
| it("normalises missing stdout/stderr fields to empty strings", async () => { | |
| vi.mocked(callCloudProxy).mockResolvedValue({ exit_code: 1 }); | |
| const result = await AgentServerRuntimeService.executeCommand( | |
| CLOUD_CONVERSATION_URL, | |
| SESSION_KEY, | |
| "false", | |
| undefined, | |
| 5, | |
| ); | |
| expect(result).toEqual({ exit_code: 1, stdout: "", stderr: "" }); | |
| }); | |
| it("does not create a RemoteWorkspace for cloud calls", async () => { | |
| vi.mocked(callCloudProxy).mockResolvedValue({ exit_code: 0 }); | |
| await AgentServerRuntimeService.executeCommand( | |
| CLOUD_CONVERSATION_URL, | |
| SESSION_KEY, | |
| "echo ok", | |
| ); | |
| expect(RemoteWorkspace).not.toHaveBeenCalled(); | |
| }); | |
| it("falls back to local path when conversationUrl is null", async () => { | |
| executeCommandMock.mockResolvedValue({ | |
| exit_code: 0, | |
| stdout: "", | |
| stderr: "", | |
| }); | |
| await AgentServerRuntimeService.executeCommand( | |
| null, | |
| SESSION_KEY, | |
| "echo ok", | |
| ); | |
| expect(callCloudProxy).not.toHaveBeenCalled(); | |
| expect(RemoteWorkspace).toHaveBeenCalledTimes(1); | |
| }); | |
| }); | |
| }); | |
| // βββ downloadFile βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| describe("AgentServerRuntimeService.downloadFile", () => { | |
| describe("local backend", () => { | |
| it("creates FileClient with resolved options and returns the ArrayBuffer", async () => { | |
| const fileBytes = new TextEncoder().encode("# README"); | |
| downloadFileMock.mockResolvedValue(fileBytes.buffer); | |
| const result = await AgentServerRuntimeService.downloadFile( | |
| "http://local-agent.example.com/api/conversations/conv-1", | |
| SESSION_KEY, | |
| "/workspace/project/README.md", | |
| ); | |
| expect(FileClient).toHaveBeenCalledTimes(1); | |
| expect(downloadFileMock).toHaveBeenCalledWith( | |
| "/workspace/project/README.md", | |
| ); | |
| expect(result).toBe(fileBytes.buffer); | |
| }); | |
| it("does not call callCloudProxy for local backends", async () => { | |
| downloadFileMock.mockResolvedValue(new ArrayBuffer(0)); | |
| await AgentServerRuntimeService.downloadFile( | |
| null, | |
| null, | |
| "/workspace/file.txt", | |
| ); | |
| expect(callCloudProxy).not.toHaveBeenCalled(); | |
| }); | |
| }); | |
| describe("cloud backend", () => { | |
| beforeEach(activateCloud); | |
| it("routes through callCloudProxy with GET and URL-encoded path", async () => { | |
| const blob = new Blob([new TextEncoder().encode("file content")]); | |
| vi.mocked(callCloudProxy).mockResolvedValue(blob); | |
| const result = await AgentServerRuntimeService.downloadFile( | |
| CLOUD_CONVERSATION_URL, | |
| SESSION_KEY, | |
| "/workspace/project/src/main.ts", | |
| ); | |
| const proxyCall = vi.mocked(callCloudProxy).mock.calls[0][0]; | |
| expect(proxyCall.method).toBe("GET"); | |
| expect(proxyCall.path).toBe( | |
| "/api/file/download?path=%2Fworkspace%2Fproject%2Fsrc%2Fmain.ts", | |
| ); | |
| expect(proxyCall.hostOverride).toBe("https://runtime.example.com"); | |
| expect(proxyCall.authMode).toBe("session-api-key"); | |
| expect(proxyCall.sessionApiKey).toBe(SESSION_KEY); | |
| expect(proxyCall.responseType).toBe("blob"); | |
| // Blob.arrayBuffer() round-trip: decoded text should match the original. | |
| expect(new TextDecoder().decode(result)).toBe("file content"); | |
| }); | |
| it("does not create a FileClient for cloud calls", async () => { | |
| vi.mocked(callCloudProxy).mockResolvedValue(new Blob()); | |
| await AgentServerRuntimeService.downloadFile( | |
| CLOUD_CONVERSATION_URL, | |
| SESSION_KEY, | |
| "/workspace/file.txt", | |
| ); | |
| expect(FileClient).not.toHaveBeenCalled(); | |
| }); | |
| it("falls back to local path when conversationUrl is null", async () => { | |
| downloadFileMock.mockResolvedValue(new ArrayBuffer(0)); | |
| await AgentServerRuntimeService.downloadFile( | |
| null, | |
| SESSION_KEY, | |
| "/workspace/file.txt", | |
| ); | |
| expect(callCloudProxy).not.toHaveBeenCalled(); | |
| expect(FileClient).toHaveBeenCalledTimes(1); | |
| }); | |
| }); | |
| }); | |