mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-07 15:58:03 +08:00
* Route agent-server calls through TypeScript client Co-authored-by: openhands <openhands@all-hands.dev> * Remove obsolete agent-server API wrappers Use @openhands/typescript-client directly where practical and remove thin src/api wrappers that no longer add domain-specific behavior. Keep app-specific adapter/config/cloud logic in place and update tests to mock typed clients directly. Co-authored-by: openhands <openhands@all-hands.dev> * Inline remaining trivial client wrappers Remove remaining one-line wrappers around @openhands/typescript-client calls in the local conversation and workspace paths. Keep domain-specific API adapters where they still perform cloud routing, response adaptation, caching, or app-specific defaults. Co-authored-by: openhands <openhands@all-hands.dev> * Remove more no-op API facades Delete additional short methods that only returned constants, no-ops, or delegated directly to another adapter. Keep the remaining services focused on paths that still perform cloud routing, response adaptation, client setup, or app-specific behavior. Co-authored-by: openhands <openhands@all-hands.dev> * Address review feedback after latest merge Co-authored-by: openhands <openhands@all-hands.dev> * Address follow-up TypeScript client review Co-authored-by: openhands <openhands@all-hands.dev> * Fix static dev frontend session auth Co-authored-by: openhands <openhands@all-hands.dev> * Handle unexpected conversation response shapes Co-authored-by: openhands <openhands@all-hands.dev> * Format conversation response handling Co-authored-by: openhands <openhands@all-hands.dev> * Sanitize conversation response fields Co-authored-by: openhands <openhands@all-hands.dev> * Show conversation load failures full-screen Co-authored-by: openhands <openhands@all-hands.dev> * Harden conversation file path handling Co-authored-by: openhands <openhands@all-hands.dev> * Guard SDK settings page against malformed schemas Co-authored-by: openhands <openhands@all-hands.dev> * Avoid using Vercel origin as default backend Co-authored-by: openhands <openhands@all-hands.dev> * Fix preview API fallback behavior Co-authored-by: openhands <openhands@all-hands.dev> * Refresh file previews after workspace edits Co-authored-by: openhands <openhands@all-hands.dev> * test: stabilize backend selector cleanup Co-authored-by: openhands <openhands@all-hands.dev> * Remove unrelated PR changes Keep the PR scoped to direct agent-server API removal and TypeScript SDK migration by reverting unrelated backend fallback, static dev auth, conversation error UI, SDK settings hardening, and test-only cleanup changes.\n\nCo-authored-by: openhands <openhands@all-hands.dev> * Add PR 278 agent run GIF Co-authored-by: openhands <openhands@all-hands.dev> --------- Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: hieptl <hieptl.developer@gmail.com>
152 lines
4.7 KiB
TypeScript
152 lines
4.7 KiB
TypeScript
import { RemoteWorkspace } from "@openhands/typescript-client/workspace/remote-workspace";
|
|
import { describe, test, expect, vi, beforeEach } from "vitest";
|
|
import AgentServerGitService from "../../src/api/git-service/agent-server-git-service.api";
|
|
|
|
const { mockGitChanges, mockGitDiff } = vi.hoisted(() => ({
|
|
mockGitChanges: vi.fn(),
|
|
mockGitDiff: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@openhands/typescript-client/workspace/remote-workspace", () => ({
|
|
RemoteWorkspace: vi.fn(function RemoteWorkspaceMock() {
|
|
return {
|
|
gitChanges: mockGitChanges,
|
|
gitDiff: mockGitDiff,
|
|
};
|
|
}),
|
|
}));
|
|
|
|
describe("AgentServerGitService", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockGitChanges.mockReset();
|
|
mockGitDiff.mockReset();
|
|
vi.mocked(RemoteWorkspace).mockClear();
|
|
});
|
|
|
|
describe("getGitChanges", () => {
|
|
test("throws when response is not an array (dead runtime returns HTML)", async () => {
|
|
mockGitChanges.mockResolvedValue("<!DOCTYPE html><html>...</html>");
|
|
|
|
await expect(
|
|
AgentServerGitService.getGitChanges(
|
|
"http://localhost:3000/api/conversations/123",
|
|
"test-api-key",
|
|
"/workspace",
|
|
),
|
|
).rejects.toThrow("Invalid response from runtime");
|
|
});
|
|
|
|
test("passes conversation URL and session key to the runtime workspace", async () => {
|
|
mockGitChanges.mockResolvedValue([]);
|
|
|
|
await AgentServerGitService.getGitChanges(
|
|
"http://localhost:3000/api/conversations/123",
|
|
"my-session-key",
|
|
"/workspace/project",
|
|
);
|
|
|
|
expect(RemoteWorkspace).toHaveBeenCalledWith({
|
|
host: "http://localhost:3000",
|
|
apiKey: "my-session-key",
|
|
workingDir: "workspace/project",
|
|
});
|
|
expect(mockGitChanges).toHaveBeenCalledWith("/workspace/project", {
|
|
ref: "HEAD",
|
|
});
|
|
});
|
|
|
|
test("preserves slashes in path when using workspace helper", async () => {
|
|
mockGitChanges.mockResolvedValue([]);
|
|
|
|
const pathWithSlashes = "/workspace/project/src/components";
|
|
await AgentServerGitService.getGitChanges(
|
|
"http://localhost:3000/api/conversations/123",
|
|
"test-api-key",
|
|
pathWithSlashes,
|
|
);
|
|
|
|
expect(mockGitChanges).toHaveBeenCalledWith(pathWithSlashes, {
|
|
ref: "HEAD",
|
|
});
|
|
});
|
|
|
|
test("maps V1 git statuses to V0 format", async () => {
|
|
mockGitChanges.mockResolvedValue([
|
|
{ status: "ADDED", path: "new-file.ts" },
|
|
{ status: "DELETED", path: "removed-file.ts" },
|
|
{ status: "UPDATED", path: "changed-file.ts" },
|
|
{ status: "MOVED", path: "renamed-file.ts" },
|
|
]);
|
|
|
|
const result = await AgentServerGitService.getGitChanges(
|
|
"http://localhost:3000/api/conversations/123",
|
|
"test-api-key",
|
|
"/workspace",
|
|
);
|
|
|
|
expect(result).toEqual([
|
|
{ status: "A", path: "new-file.ts" },
|
|
{ status: "D", path: "removed-file.ts" },
|
|
{ status: "M", path: "changed-file.ts" },
|
|
{ status: "R", path: "renamed-file.ts" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("getGitChangeDiff", () => {
|
|
test("passes conversation URL and path to runtime workspace diff helper", async () => {
|
|
mockGitDiff.mockResolvedValue({
|
|
diff: "--- a/file.ts\n+++ b/file.ts\n...",
|
|
});
|
|
|
|
await AgentServerGitService.getGitChangeDiff(
|
|
"http://localhost:3000/api/conversations/123",
|
|
"test-api-key",
|
|
"/workspace/project/file.ts",
|
|
);
|
|
|
|
expect(RemoteWorkspace).toHaveBeenCalledWith({
|
|
host: "http://localhost:3000",
|
|
apiKey: "test-api-key",
|
|
workingDir: "workspace/project",
|
|
});
|
|
expect(mockGitDiff).toHaveBeenCalledWith("/workspace/project/file.ts", {
|
|
ref: "HEAD",
|
|
});
|
|
});
|
|
|
|
test("preserves slashes in file path when using workspace helper", async () => {
|
|
mockGitDiff.mockResolvedValue({ diff: "diff content" });
|
|
|
|
const filePath = "/workspace/project/src/components/Button.tsx";
|
|
await AgentServerGitService.getGitChangeDiff(
|
|
"http://localhost:3000/api/conversations/123",
|
|
"test-api-key",
|
|
filePath,
|
|
);
|
|
|
|
expect(mockGitDiff).toHaveBeenCalledWith(filePath, { ref: "HEAD" });
|
|
});
|
|
|
|
test("returns the diff data from the response", async () => {
|
|
const expectedDiff = {
|
|
diff: "--- a/file.ts\n+++ b/file.ts\n@@ -1,3 +1,4 @@\n+new line",
|
|
};
|
|
mockGitDiff.mockResolvedValue(expectedDiff);
|
|
|
|
const result = await AgentServerGitService.getGitChangeDiff(
|
|
"http://localhost:3000/api/conversations/123",
|
|
"test-api-key",
|
|
"/workspace/file.ts",
|
|
);
|
|
|
|
expect(result).toEqual({
|
|
modified: "",
|
|
original: "",
|
|
diff: expectedDiff.diff,
|
|
});
|
|
});
|
|
});
|
|
});
|