diff --git a/e2e/dashboard-widgets.spec.js b/e2e/dashboard-widgets.spec.js index 40ab6562..7ec3ee59 100644 --- a/e2e/dashboard-widgets.spec.js +++ b/e2e/dashboard-widgets.spec.js @@ -15,6 +15,7 @@ test.beforeEach(async ({ page }) => { accessToken: "test-token", }, maxAge: 60 * 60, + cookieName: "next-auth.session-token", }); await page.context().addCookies([ @@ -93,6 +94,45 @@ test.beforeEach(async ({ page }) => { }); }); + await page.route("**/api/goals/sync", async (route) => { + await route.fulfill({ + contentType: "application/json", + body: JSON.stringify({ updated: 1, commitCount: 4 }), + }); + }); + + await page.route("**/api/ai-insights**", async (route) => { + await route.fulfill({ + contentType: "application/json", + body: JSON.stringify({ + data: { + insights: [ + { + id: "insight-1", + type: "productivity", + title: "High Consistency", + description: "You have coded 5 days this week!", + severity: "positive", + }, + ], + trend: { direction: "up", percentage: 15 }, + aiSummary: "Great job shipping features this week. Keep up the high standard!", + generatedAt: "2026-05-18T12:00:00.000Z", + }, + }), + }); + }); + + await page.route("**/api/notifications**", async (route) => { + await route.fulfill({ + contentType: "application/json", + body: JSON.stringify({ + notifications: [], + unreadCount: 0, + }), + }); + }); + const metricRoutes = [ "**/api/metrics/prs**", "**/api/metrics/pr-breakdown**", diff --git a/test/resolve-user.test.ts b/test/resolve-user.test.ts new file mode 100644 index 00000000..a842b1d5 --- /dev/null +++ b/test/resolve-user.test.ts @@ -0,0 +1,60 @@ +import { vi, describe, it, expect, beforeEach } from "vitest"; + +// Mock Supabase admin client +const mockSingle = vi.fn(); +const mockEq = vi.fn(() => ({ single: mockSingle })); +const mockSelect = vi.fn(() => ({ + eq: mockEq, + single: mockSingle, +})); +const mockUpsert = vi.fn(() => ({ select: mockSelect })); + +vi.mock("@/lib/supabase", () => ({ + supabaseAdmin: { + from: vi.fn((table) => { + if (table === "users") { + return { + select: mockSelect, + upsert: mockUpsert, + }; + } + }), + }, +})); + +import { resolveAppUser } from "@/lib/resolve-user"; + +describe("resolveAppUser function", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("returns existing user when found", async () => { + mockSingle.mockResolvedValueOnce({ data: { id: "user-123" } }); + + const result = await resolveAppUser("github-id-1"); + + expect(result).toEqual({ id: "user-123" }); + expect(mockSelect).toHaveBeenCalledWith("id"); + expect(mockEq).toHaveBeenCalledWith("github_id", "github-id-1"); + expect(mockUpsert).not.toHaveBeenCalled(); + }); + + it("upserts and returns user when existing is not found", async () => { + // First call (find existing) returns null data + mockSingle.mockResolvedValueOnce({ data: null }); + // Second call (select after upsert) returns upserted user + mockSingle.mockResolvedValueOnce({ data: { id: "user-new" } }); + + const result = await resolveAppUser("github-id-2", "octocat"); + + expect(result).toEqual({ id: "user-new" }); + expect(mockUpsert).toHaveBeenCalledWith( + expect.objectContaining({ + github_id: "github-id-2", + github_login: "octocat", + }), + { onConflict: "github_id" } + ); + }); +});