Ctrl K
ComunidadGuías…

lib/auth/__tests__/provider.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { afterEach, beforeEach, describe, expect, it } from 'vitest'

import {
  getAuthProvider,
  isAnonymousMode,
  resolveAuthProviderName,
  withShareOptIn
} from '@/lib/auth/provider'

const AUTH_ENV_KEYS = [
  'AUTH_PROVIDER',
  'ENABLE_AUTH',
  'MORPHIC_CLOUD_DEPLOYMENT',
  'NEXT_PUBLIC_SUPABASE_URL',
  'NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY'
] as const

function clearAuthEnv() {
  for (const key of AUTH_ENV_KEYS) {
    delete process.env[key]
  }
}

function configureSupabase() {
  process.env.NEXT_PUBLIC_SUPABASE_URL = 'https://example.supabase.co'
  process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY = 'sb_publishable_key'
}

describe('auth provider dispatch', () => {
  const originalEnv: Record<string, string | undefined> = {}

  beforeEach(() => {
    for (const key of AUTH_ENV_KEYS) {
      originalEnv[key] = process.env[key]
    }
    clearAuthEnv()
  })

  afterEach(() => {
    for (const key of AUTH_ENV_KEYS) {
      if (originalEnv[key] === undefined) {
        delete process.env[key]
      } else {
        process.env[key] = originalEnv[key]
      }
    }
  })

  describe('explicit AUTH_PROVIDER configuration', () => {
    it('resolves supabase', () => {
      process.env.AUTH_PROVIDER = 'supabase'
      expect(resolveAuthProviderName()).toBe('supabase')
      expect(getAuthProvider().name).toBe('supabase')
    })

    it('resolves none', () => {
      process.env.AUTH_PROVIDER = 'none'
      expect(resolveAuthProviderName()).toBe('none')
      expect(getAuthProvider().name).toBe('none')
    })

    it('trims surrounding whitespace', () => {
      process.env.AUTH_PROVIDER = '  none  '
      expect(resolveAuthProviderName()).toBe('none')
    })

    it('rejects unknown values', () => {
      process.env.AUTH_PROVIDER = 'auth0'
      expect(() => resolveAuthProviderName()).toThrow(
        'Invalid AUTH_PROVIDER "auth0"'
      )
    })

    it('resolves better-auth to the better-auth provider', () => {
      process.env.AUTH_PROVIDER = 'better-auth'
      expect(resolveAuthProviderName()).toBe('better-auth')
      expect(getAuthProvider().name).toBe('better-auth')
    })
  })

  describe('isAnonymousMode', () => {
    it('returns true when AUTH_PROVIDER=none and ENABLE_AUTH is unset', () => {
      process.env.AUTH_PROVIDER = 'none'
      expect(isAnonymousMode()).toBe(true)
    })

    it('returns true when AUTH_PROVIDER=none even if ENABLE_AUTH=true', () => {
      process.env.AUTH_PROVIDER = 'none'
      process.env.ENABLE_AUTH = 'true'
      expect(isAnonymousMode()).toBe(true)
    })

    it('returns true when ENABLE_AUTH=false', () => {
      process.env.ENABLE_AUTH = 'false'
      expect(isAnonymousMode()).toBe(true)
    })

    it('returns false when unset (supabase derived)', () => {
      expect(isAnonymousMode()).toBe(false)
    })
  })

  describe('backward-compatible derivation when AUTH_PROVIDER is unset', () => {
    it('derives none when ENABLE_AUTH=false', () => {
      process.env.ENABLE_AUTH = 'false'
      expect(resolveAuthProviderName()).toBe('none')
    })

    it('derives supabase when ENABLE_AUTH=true and Supabase is configured', () => {
      process.env.ENABLE_AUTH = 'true'
      configureSupabase()
      expect(resolveAuthProviderName()).toBe('supabase')
    })

    it('derives supabase when ENABLE_AUTH is unset and Supabase is configured', () => {
      configureSupabase()
      expect(resolveAuthProviderName()).toBe('supabase')
    })

    it('derives supabase when ENABLE_AUTH is unset and Supabase is not configured', () => {
      // Guest behavior: the supabase provider resolves no user when unconfigured
      expect(resolveAuthProviderName()).toBe('supabase')
    })

    it('derives supabase when ENABLE_AUTH=true and Supabase is not configured', () => {
      process.env.ENABLE_AUTH = 'true'
      expect(resolveAuthProviderName()).toBe('supabase')
    })

    it('prefers none over Supabase configuration when ENABLE_AUTH=false', () => {
      process.env.ENABLE_AUTH = 'false'
      configureSupabase()
      expect(resolveAuthProviderName()).toBe('none')
    })
  })

  describe('Morphic Cloud deployment guard', () => {
    beforeEach(() => {
      process.env.MORPHIC_CLOUD_DEPLOYMENT = 'true'
    })

    it('allows supabase', () => {
      configureSupabase()
      expect(resolveAuthProviderName()).toBe('supabase')
    })

    it('refuses derived none (ENABLE_AUTH=false) with the historical error', () => {
      process.env.ENABLE_AUTH = 'false'
      expect(() => resolveAuthProviderName()).toThrow(
        'ENABLE_AUTH=false is not allowed in MORPHIC_CLOUD_DEPLOYMENT'
      )
    })

    it('refuses explicit AUTH_PROVIDER=none', () => {
      process.env.AUTH_PROVIDER = 'none'
      expect(() => resolveAuthProviderName()).toThrow(
        'AUTH_PROVIDER=none is not allowed in MORPHIC_CLOUD_DEPLOYMENT'
      )
    })

    it('refuses AUTH_PROVIDER=better-auth', () => {
      process.env.AUTH_PROVIDER = 'better-auth'
      expect(() => resolveAuthProviderName()).toThrow(
        'AUTH_PROVIDER=better-auth is not allowed in MORPHIC_CLOUD_DEPLOYMENT'
      )
    })
  })
})

describe('supabase provider without configuration', () => {
  beforeEach(() => {
    delete process.env.NEXT_PUBLIC_SUPABASE_URL
    delete process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
    delete process.env.AUTH_PROVIDER
    delete process.env.ENABLE_AUTH
  })

  it('resolves no user when Supabase is not configured', async () => {
    const provider = getAuthProvider()
    expect(provider.name).toBe('supabase')
    await expect(provider.getCurrentUser()).resolves.toBeNull()
  })

  it('passes the request through when Supabase is not configured', async () => {
    const { NextRequest } = await import('next/server')
    const provider = getAuthProvider()
    const request = new NextRequest('http://localhost:3000/search/abc')
    const response = await provider.handleSession(request)
    expect(response.status).toBe(200)
    expect(response.headers.get('location')).toBeNull()
  })
})

describe('withShareOptIn', () => {
  const base = {
    signUp: true,
    passwordReset: true,
    deleteUser: true,
    oauth: false,
    emailVerification: false,
    share: true
  }

  it('keeps sharing only when an opt-in flag is true (better-auth)', () => {
    const originalPublic = process.env.NEXT_PUBLIC_ENABLE_SHARE
    const originalRuntime = process.env.ENABLE_SHARE
    try {
      delete process.env.NEXT_PUBLIC_ENABLE_SHARE
      delete process.env.ENABLE_SHARE
      expect(withShareOptIn(base, 'better-auth').share).toBe(false)

      // The runtime flag is the one that works in prebuilt images, where
      // the public flag was inlined at build time.
      process.env.ENABLE_SHARE = 'true'
      expect(withShareOptIn(base, 'better-auth').share).toBe(true)
      process.env.ENABLE_SHARE = 'false'
      expect(withShareOptIn(base, 'better-auth').share).toBe(false)

      process.env.NEXT_PUBLIC_ENABLE_SHARE = 'true'
      expect(withShareOptIn(base, 'better-auth').share).toBe(true)

      process.env.NEXT_PUBLIC_ENABLE_SHARE = 'false'
      expect(withShareOptIn(base, 'better-auth').share).toBe(false)
    } finally {
      if (originalPublic === undefined) {
        delete process.env.NEXT_PUBLIC_ENABLE_SHARE
      } else {
        process.env.NEXT_PUBLIC_ENABLE_SHARE = originalPublic
      }
      if (originalRuntime === undefined) {
        delete process.env.ENABLE_SHARE
      } else {
        process.env.ENABLE_SHARE = originalRuntime
      }
    }
  })

  it('leaves supabase sharing untouched by the opt-in flag', () => {
    // The default Supabase path must behave as it always has: sharing
    // with no env var configured.
    const originalPublic = process.env.NEXT_PUBLIC_ENABLE_SHARE
    const originalRuntime = process.env.ENABLE_SHARE
    try {
      delete process.env.NEXT_PUBLIC_ENABLE_SHARE
      delete process.env.ENABLE_SHARE
      expect(withShareOptIn(base, 'supabase').share).toBe(true)
    } finally {
      if (originalPublic === undefined) {
        delete process.env.NEXT_PUBLIC_ENABLE_SHARE
      } else {
        process.env.NEXT_PUBLIC_ENABLE_SHARE = originalPublic
      }
      if (originalRuntime === undefined) {
        delete process.env.ENABLE_SHARE
      } else {
        process.env.ENABLE_SHARE = originalRuntime
      }
    }
  })

  it('never enables sharing for a provider without the capability', () => {
    process.env.NEXT_PUBLIC_ENABLE_SHARE = 'true'
    expect(withShareOptIn({ ...base, share: false }, 'better-auth').share).toBe(
      false
    )
  })
})