✨ feat: add Google and Apple OAuth via better-auth socialProviders
This commit is contained in:
196
tests/auth.test.ts
Normal file
196
tests/auth.test.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Slice 1: buildSocialProviders — conditional provider registration
|
||||
//
|
||||
// Public interface under test: buildSocialProviders(env) → object
|
||||
// Behaviour: only registers providers whose env vars are all present.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
import { buildSocialProviders } from "@/lib/build-social-providers";
|
||||
|
||||
describe("buildSocialProviders", () => {
|
||||
test("returns empty object when no OAuth env vars are set", () => {
|
||||
const result = buildSocialProviders({});
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
test("registers google when both Google vars are present", () => {
|
||||
const result = buildSocialProviders({
|
||||
AUTH_GOOGLE_CLIENT_ID: "gid",
|
||||
AUTH_GOOGLE_CLIENT_SECRET: "gsecret",
|
||||
});
|
||||
expect(result).toHaveProperty("google");
|
||||
expect(result.google).toMatchObject({
|
||||
clientId: "gid",
|
||||
clientSecret: "gsecret",
|
||||
});
|
||||
});
|
||||
|
||||
test("does NOT register google when only clientId is present", () => {
|
||||
const result = buildSocialProviders({
|
||||
AUTH_GOOGLE_CLIENT_ID: "gid",
|
||||
});
|
||||
expect(result).not.toHaveProperty("google");
|
||||
});
|
||||
|
||||
test("does NOT register google when only clientSecret is present", () => {
|
||||
const result = buildSocialProviders({
|
||||
AUTH_GOOGLE_CLIENT_SECRET: "gsecret",
|
||||
});
|
||||
expect(result).not.toHaveProperty("google");
|
||||
});
|
||||
|
||||
test("registers apple when all four Apple vars are present", () => {
|
||||
const result = buildSocialProviders({
|
||||
AUTH_APPLE_CLIENT_ID: "aid",
|
||||
AUTH_APPLE_CLIENT_SECRET: "asecret",
|
||||
AUTH_APPLE_TEAM_ID: "TEAM1",
|
||||
AUTH_APPLE_KEY_ID: "KEY1",
|
||||
AUTH_APPLE_PRIVATE_KEY: "-----BEGIN PRIVATE KEY-----\nfake\n-----END PRIVATE KEY-----",
|
||||
});
|
||||
expect(result).toHaveProperty("apple");
|
||||
expect(result.apple).toMatchObject({
|
||||
clientId: "aid",
|
||||
clientSecret: "asecret",
|
||||
});
|
||||
});
|
||||
|
||||
test("does NOT register apple when any Apple var is missing", () => {
|
||||
const result = buildSocialProviders({
|
||||
AUTH_APPLE_CLIENT_ID: "aid",
|
||||
AUTH_APPLE_CLIENT_SECRET: "asecret",
|
||||
AUTH_APPLE_TEAM_ID: "TEAM1",
|
||||
// AUTH_APPLE_KEY_ID missing
|
||||
AUTH_APPLE_PRIVATE_KEY: "-----BEGIN PRIVATE KEY-----\nfake\n-----END PRIVATE KEY-----",
|
||||
});
|
||||
expect(result).not.toHaveProperty("apple");
|
||||
});
|
||||
|
||||
test("registers both google and apple when all vars are present", () => {
|
||||
const result = buildSocialProviders({
|
||||
AUTH_GOOGLE_CLIENT_ID: "gid",
|
||||
AUTH_GOOGLE_CLIENT_SECRET: "gsecret",
|
||||
AUTH_APPLE_CLIENT_ID: "aid",
|
||||
AUTH_APPLE_CLIENT_SECRET: "asecret",
|
||||
AUTH_APPLE_TEAM_ID: "TEAM1",
|
||||
AUTH_APPLE_KEY_ID: "KEY1",
|
||||
AUTH_APPLE_PRIVATE_KEY: "-----BEGIN PRIVATE KEY-----\nfake\n-----END PRIVATE KEY-----",
|
||||
});
|
||||
expect(result).toHaveProperty("google");
|
||||
expect(result).toHaveProperty("apple");
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Slice 2: auth-client — signIn.social is available
|
||||
//
|
||||
// Public interface under test: authClient.signIn.social
|
||||
// Behaviour: the function exists and is callable (no plugin wiring needed
|
||||
// for socialProviders on the client — it's built into createAuthClient).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
|
||||
describe("authClient", () => {
|
||||
test("exposes signIn.social as a function", () => {
|
||||
expect(typeof authClient.signIn.social).toBe("function");
|
||||
});
|
||||
|
||||
test("exposes useSession hook", () => {
|
||||
expect(typeof authClient.useSession).toBe("function");
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Slice 3: getSignInProviders — sign-in page provider list
|
||||
//
|
||||
// Public interface under test: getSignInProviders()
|
||||
// Behaviour: returns the ordered list of available sign-in providers with
|
||||
// their labels and ids, based on which env vars are configured.
|
||||
// The component consumes this list to render buttons — testing the list
|
||||
// verifies the button-to-provider mapping without needing a DOM.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
import { getSignInProviders } from "@/lib/get-sign-in-providers";
|
||||
|
||||
describe("getSignInProviders", () => {
|
||||
test("returns authentik when Authentik vars are set", () => {
|
||||
const providers = getSignInProviders({
|
||||
AUTH_AUTHENTIK_CLIENT_ID: "id",
|
||||
AUTH_AUTHENTIK_CLIENT_SECRET: "secret",
|
||||
AUTH_AUTHENTIK_ISSUER: "https://auth.example.com",
|
||||
});
|
||||
const ids = providers.map((p) => p.id);
|
||||
expect(ids).toContain("authentik");
|
||||
});
|
||||
|
||||
test("returns google when Google vars are set", () => {
|
||||
const providers = getSignInProviders({
|
||||
AUTH_GOOGLE_CLIENT_ID: "gid",
|
||||
AUTH_GOOGLE_CLIENT_SECRET: "gsecret",
|
||||
});
|
||||
const ids = providers.map((p) => p.id);
|
||||
expect(ids).toContain("google");
|
||||
});
|
||||
|
||||
test("returns apple when Apple vars are set", () => {
|
||||
const providers = getSignInProviders({
|
||||
AUTH_APPLE_CLIENT_ID: "aid",
|
||||
AUTH_APPLE_CLIENT_SECRET: "asecret",
|
||||
AUTH_APPLE_TEAM_ID: "TEAM1",
|
||||
AUTH_APPLE_KEY_ID: "KEY1",
|
||||
AUTH_APPLE_PRIVATE_KEY: "-----BEGIN PRIVATE KEY-----\nfake\n-----END PRIVATE KEY-----",
|
||||
});
|
||||
const ids = providers.map((p) => p.id);
|
||||
expect(ids).toContain("apple");
|
||||
});
|
||||
|
||||
test("returns empty list when no vars are set", () => {
|
||||
const providers = getSignInProviders({});
|
||||
expect(providers).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("each provider has id, label, and signInMethod", () => {
|
||||
const providers = getSignInProviders({
|
||||
AUTH_GOOGLE_CLIENT_ID: "gid",
|
||||
AUTH_GOOGLE_CLIENT_SECRET: "gsecret",
|
||||
});
|
||||
expect(providers[0]).toMatchObject({
|
||||
id: expect.any(String),
|
||||
label: expect.any(String),
|
||||
signInMethod: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
test("google provider uses 'social' signInMethod", () => {
|
||||
const providers = getSignInProviders({
|
||||
AUTH_GOOGLE_CLIENT_ID: "gid",
|
||||
AUTH_GOOGLE_CLIENT_SECRET: "gsecret",
|
||||
});
|
||||
const google = providers.find((p) => p.id === "google");
|
||||
expect(google?.signInMethod).toBe("social");
|
||||
});
|
||||
|
||||
test("apple provider uses 'social' signInMethod", () => {
|
||||
const providers = getSignInProviders({
|
||||
AUTH_APPLE_CLIENT_ID: "aid",
|
||||
AUTH_APPLE_CLIENT_SECRET: "asecret",
|
||||
AUTH_APPLE_TEAM_ID: "TEAM1",
|
||||
AUTH_APPLE_KEY_ID: "KEY1",
|
||||
AUTH_APPLE_PRIVATE_KEY: "-----BEGIN PRIVATE KEY-----\nfake\n-----END PRIVATE KEY-----",
|
||||
});
|
||||
const apple = providers.find((p) => p.id === "apple");
|
||||
expect(apple?.signInMethod).toBe("social");
|
||||
});
|
||||
|
||||
test("authentik provider uses 'oauth2' signInMethod", () => {
|
||||
const providers = getSignInProviders({
|
||||
AUTH_AUTHENTIK_CLIENT_ID: "id",
|
||||
AUTH_AUTHENTIK_CLIENT_SECRET: "secret",
|
||||
AUTH_AUTHENTIK_ISSUER: "https://auth.example.com",
|
||||
});
|
||||
const authentik = providers.find((p) => p.id === "authentik");
|
||||
expect(authentik?.signInMethod).toBe("oauth2");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user