diff --git a/frontend/__tests__/api/github.test.ts b/frontend/__tests__/api/github.test.ts new file mode 100644 index 000000000000..5a659d4b71e6 --- /dev/null +++ b/frontend/__tests__/api/github.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it, vi } from "vitest"; +import { retrieveLatestGitHubCommit } from "../../src/api/github"; + +describe("retrieveLatestGitHubCommit", () => { + const { githubGetMock } = vi.hoisted(() => ({ + githubGetMock: vi.fn(), + })); + + vi.mock("../../src/api/github-axios-instance", () => ({ + github: { + get: githubGetMock, + }, + })); + + it("should return the latest commit when repository has commits", async () => { + const mockCommit = { + sha: "123abc", + commit: { + message: "Initial commit", + }, + }; + + githubGetMock.mockResolvedValueOnce({ + data: [mockCommit], + }); + + const result = await retrieveLatestGitHubCommit("user/repo"); + expect(result).toEqual(mockCommit); + }); + + it("should return null when repository is empty", async () => { + const error = new Error("Repository is empty"); + (error as any).response = { status: 409 }; + githubGetMock.mockRejectedValueOnce(error); + + const result = await retrieveLatestGitHubCommit("user/empty-repo"); + expect(result).toBeNull(); + }); + + it("should throw error for other error cases", async () => { + const error = new Error("Network error"); + (error as any).response = { status: 500 }; + githubGetMock.mockRejectedValueOnce(error); + + await expect(retrieveLatestGitHubCommit("user/repo")).rejects.toThrow(); + }); +}); diff --git a/frontend/src/api/github.ts b/frontend/src/api/github.ts index b315e2d930a7..492955ae69d9 100644 --- a/frontend/src/api/github.ts +++ b/frontend/src/api/github.ts @@ -106,15 +106,28 @@ export const retrieveGitHubUser = async () => { export const retrieveLatestGitHubCommit = async ( repository: string, -): Promise => { - const response = await github.get( - `/repos/${repository}/commits`, - { - params: { - per_page: 1, +): Promise => { + try { + const response = await github.get( + `/repos/${repository}/commits`, + { + params: { + per_page: 1, + }, }, - }, - ); - - return response.data[0]; + ); + return response.data[0] || null; + } catch (error) { + if (!error || typeof error !== "object") { + throw new Error("Unknown error occurred"); + } + const axiosError = error as { response?: { status: number } }; + if (axiosError.response?.status === 409) { + // Repository is empty, no commits yet + return null; + } + throw new Error( + error instanceof Error ? error.message : "Unknown error occurred", + ); + } }; diff --git a/frontend/src/services/settings.ts b/frontend/src/services/settings.ts index 6b72750a27e8..a0dbb775f563 100644 --- a/frontend/src/services/settings.ts +++ b/frontend/src/services/settings.ts @@ -87,6 +87,7 @@ export const saveSettings = async ( const { data } = await openHands.post("/api/settings", apiSettings); return data; } catch (error) { + // Error handled by returning false return false; } };