Files
route-commerce/tests/unit/sign-in-with-google.test.ts
T
Nora 0ac4beaaa8 fix: react-doctor errors → 0 errors, 1649 warnings (46/100)
- Add requireAuth() to admin-permissions.ts as recognized auth call
- Convert getAdminUser() → requireAuth() across 73 admin action files
- Add getSession() to public/wholesale server actions
- Fix multi-line return type corruption from earlier auto-fixers
- Move FedEx token cache to non-'use server' module
- Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD,
  EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS
- Update Stripe API version 2026-05-27 → 2026-06-24
- Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient
- Fix 51 TypeScript errors (return type corruption, missing imports)
2026-06-25 23:49:37 -06:00

101 lines
3.5 KiB
TypeScript

/**
* Unit tests for `signInWithGoogleAction` in `src/actions/auth-actions.ts`.
*
* The action wraps Neon Auth's `signIn.social({ provider: "google" })`
* and returns the redirect URL to the client. It must:
* - Return `{ url, error: null }` when Neon Auth responds with a URL.
* - Return `{ url: null, error }` on any auth error / network failure.
* - Pass through `callbackURL` and `errorCallbackURL` to Neon Auth.
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
const { mockSocial } = vi.hoisted(() => ({
mockSocial: vi.fn(),
}));
vi.mock("server-only", () => ({}));
// `signIn` is a namespace object re-exported from @/lib/auth with
// methods like `email`, `social`, etc. — mock the whole thing.
// `getSession()` is called first inside `signInWithGoogleAction`
// to short-circuit when the user is already signed in; default it
// to a "no session" result so the action proceeds.
vi.mock("@/lib/auth", () => ({
signIn: { social: mockSocial },
signOut: vi.fn(),
getSession: vi.fn().mockResolvedValue({ data: null }),
}));
vi.mock("next/navigation", () => ({
redirect: vi.fn(),
}));
const { signInWithGoogleAction } = await import("@/actions/auth-actions");
beforeEach(() => {
vi.clearAllMocks();
});
describe("signInWithGoogleAction", () => {
it("returns the Neon Auth redirect URL on success", async () => {
mockSocial.mockResolvedValue({
data: { url: "https://accounts.google.com/o/oauth2/v2/auth?..." },
error: null,
});
const r = await signInWithGoogleAction({ callbackURL: "/admin" });
expect(r.error).toBeNull();
expect(r.url).toBe("https://accounts.google.com/o/oauth2/v2/auth?...");
expect(mockSocial).toHaveBeenCalledWith({
provider: "google",
callbackURL: "/admin",
errorCallbackURL: "/login?error=oauth",
});
});
it("defaults callbackURL to /admin and errorCallbackURL to /login?error=oauth", async () => {
mockSocial.mockResolvedValue({ data: { url: "https://x" }, error: null });
await signInWithGoogleAction({});
expect(mockSocial).toHaveBeenCalledWith({
provider: "google",
callbackURL: "/admin",
errorCallbackURL: "/login?error=oauth",
});
});
it("passes through a custom errorCallbackURL", async () => {
mockSocial.mockResolvedValue({ data: { url: "https://x" }, error: null });
await signInWithGoogleAction({
callbackURL: "/wholesale/portal",
errorCallbackURL: "/wholesale/login?error=oauth",
});
expect(mockSocial).toHaveBeenCalledWith({
provider: "google",
callbackURL: "/wholesale/portal",
errorCallbackURL: "/wholesale/login?error=oauth",
});
});
it("returns a structured error when Neon Auth rejects the request", async () => {
mockSocial.mockResolvedValue({
data: null,
error: { code: "PROVIDER_NOT_CONFIGURED", message: "Google OAuth is not enabled" },
});
const r = await signInWithGoogleAction({});
expect(r.url).toBeNull();
expect(r.error).toBe("Google OAuth is not enabled");
});
it("returns a structured error when the response has no URL", async () => {
mockSocial.mockResolvedValue({ data: {}, error: null });
const r = await signInWithGoogleAction({});
expect(r.url).toBeNull();
expect(r.error).toMatch(/no redirect URL/i);
});
it("returns a structured error on unexpected exceptions", async () => {
mockSocial.mockRejectedValue(new Error("network down"));
const r = await signInWithGoogleAction({});
expect(r.url).toBeNull();
expect(r.error).toBe("network down");
});
});