-
Notifications
You must be signed in to change notification settings - Fork 0
π§ͺ Add dashboard stats API tests #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| import { describe, expect, it, vi, beforeEach } from "vitest"; | ||
| import { NextRequest } from "next/server"; | ||
| import { GET } from "./route"; | ||
| import { getServerSession } from "next-auth"; | ||
| import { fetchViewerLogin } from "@/lib/githubViewer"; | ||
| import { fetchCommitActivityHeatmap } from "@/lib/githubYearInReview"; | ||
|
|
||
| // Mock dependencies | ||
| vi.mock("next-auth", () => ({ | ||
| getServerSession: vi.fn(), | ||
| })); | ||
|
|
||
| vi.mock("@/lib/githubViewer", () => ({ | ||
| fetchViewerLogin: vi.fn(), | ||
| })); | ||
|
|
||
| vi.mock("@/lib/githubYearInReview", () => ({ | ||
| fetchCommitActivityHeatmap: vi.fn(), | ||
| })); | ||
|
|
||
| describe("GET /api/dashboard/stats", () => { | ||
| beforeEach(() => { | ||
| vi.resetAllMocks(); | ||
| }); | ||
|
|
||
| const createRequest = (url: string) => new NextRequest(new URL(url)); | ||
|
|
||
| it("should return 401 if unauthorized (no session)", async () => { | ||
| vi.mocked(getServerSession).mockResolvedValueOnce(null); | ||
|
|
||
| const req = createRequest("http://localhost/api/dashboard/stats"); | ||
| const res = await GET(req); | ||
|
|
||
| expect(res.status).toBe(401); | ||
| expect(await res.json()).toEqual({ error: "Unauthorized" }); | ||
| }); | ||
|
|
||
| it("should return 401 if unauthorized (no token)", async () => { | ||
| vi.mocked(getServerSession).mockResolvedValueOnce({ | ||
| user: { name: "Test User" }, | ||
| expires: "1", | ||
| }); | ||
|
|
||
| const req = createRequest("http://localhost/api/dashboard/stats"); | ||
| const res = await GET(req); | ||
|
|
||
| expect(res.status).toBe(401); | ||
| expect(await res.json()).toEqual({ error: "Unauthorized" }); | ||
| }); | ||
|
|
||
| it("should return 400 for invalid year (too old)", async () => { | ||
| vi.mocked(getServerSession).mockResolvedValueOnce({ | ||
| user: { name: "Test User" }, | ||
| accessToken: "mock-token", | ||
| expires: "1", | ||
| }); | ||
|
Comment on lines
+52
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the suggested constants for session mocks defined, you can replace this inline object with vi.mocked(getServerSession).mockResolvedValueOnce(mockSessionWithToken); |
||
|
|
||
| const req = createRequest("http://localhost/api/dashboard/stats?year=2000"); | ||
| const res = await GET(req); | ||
|
|
||
| expect(res.status).toBe(400); | ||
| expect(await res.json()).toEqual({ error: "Invalid year" }); | ||
| }); | ||
|
|
||
| it("should return 400 for invalid year (future)", async () => { | ||
| vi.mocked(getServerSession).mockResolvedValueOnce({ | ||
| user: { name: "Test User" }, | ||
| accessToken: "mock-token", | ||
| expires: "1", | ||
| }); | ||
|
|
||
| const futureYear = new Date().getUTCFullYear() + 1; | ||
| const req = createRequest(`http://localhost/api/dashboard/stats?year=${futureYear}`); | ||
| const res = await GET(req); | ||
|
|
||
| expect(res.status).toBe(400); | ||
| expect(await res.json()).toEqual({ error: "Invalid year" }); | ||
| }); | ||
|
|
||
| it("should handle error in catch block and return 500", async () => { | ||
| vi.mocked(getServerSession).mockResolvedValueOnce({ | ||
| user: { login: "testuser" }, | ||
| accessToken: "mock-token", | ||
| expires: "1", | ||
| }); | ||
|
Comment on lines
+81
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| const errorMessage = "Failed to fetch heatmap"; | ||
| vi.mocked(fetchCommitActivityHeatmap).mockRejectedValueOnce(new Error(errorMessage)); | ||
|
|
||
| const req = createRequest("http://localhost/api/dashboard/stats"); | ||
| const res = await GET(req); | ||
|
|
||
| expect(res.status).toBe(500); | ||
| expect(await res.json()).toEqual({ error: errorMessage }); | ||
| }); | ||
|
|
||
| it("should handle unknown error in catch block and return 500", async () => { | ||
| vi.mocked(getServerSession).mockResolvedValueOnce({ | ||
| user: { login: "testuser" }, | ||
| accessToken: "mock-token", | ||
| expires: "1", | ||
| }); | ||
|
|
||
| vi.mocked(fetchCommitActivityHeatmap).mockRejectedValueOnce("String error, not an Error instance"); | ||
|
|
||
| const req = createRequest("http://localhost/api/dashboard/stats"); | ||
| const res = await GET(req); | ||
|
|
||
| expect(res.status).toBe(500); | ||
| expect(await res.json()).toEqual({ error: "Unknown error" }); | ||
| }); | ||
|
|
||
| it("should return 200 and heatmap data on success", async () => { | ||
| vi.mocked(getServerSession).mockResolvedValueOnce({ | ||
| user: { login: "testuser" }, | ||
| accessToken: "mock-token", | ||
| expires: "1", | ||
| }); | ||
|
|
||
| const mockHeatmap: number[][] = []; | ||
| vi.mocked(fetchCommitActivityHeatmap).mockResolvedValueOnce(mockHeatmap); | ||
|
|
||
| const currentYear = new Date().getUTCFullYear(); | ||
| const req = createRequest("http://localhost/api/dashboard/stats"); | ||
| const res = await GET(req); | ||
|
|
||
| expect(res.status).toBe(200); | ||
| expect(await res.json()).toEqual({ year: currentYear, heatmap: mockHeatmap }); | ||
| }); | ||
|
|
||
| it("should use fetchViewerLogin when user.login is missing", async () => { | ||
| vi.mocked(getServerSession).mockResolvedValueOnce({ | ||
| user: { name: "Test User" }, // no login | ||
| accessToken: "mock-token", | ||
| expires: "1", | ||
| }); | ||
|
|
||
| vi.mocked(fetchViewerLogin).mockResolvedValueOnce("fetcheduser"); | ||
| const mockHeatmap: number[][] = []; | ||
| vi.mocked(fetchCommitActivityHeatmap).mockResolvedValueOnce(mockHeatmap); | ||
|
|
||
| const req = createRequest("http://localhost/api/dashboard/stats"); | ||
| const res = await GET(req); | ||
|
|
||
| expect(fetchViewerLogin).toHaveBeenCalledWith("mock-token"); | ||
| expect(fetchCommitActivityHeatmap).toHaveBeenCalledWith("fetcheduser", expect.any(Number), "mock-token"); | ||
| expect(res.status).toBe(200); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To improve maintainability and reduce code duplication, you can define the common session mock objects as constants within the
describeblock. This will make the tests cleaner, more readable, and easier to maintain.