This commit is contained in:
142
tests/features/auth/auth-role-sync.test.ts
Normal file
142
tests/features/auth/auth-role-sync.test.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Tests de sincronización de rol desde DB.
|
||||
* Verifica que buildAuthUser retorna el valor optimista correcto y que
|
||||
* syncProfileFromDB sobrescribe la heurística de email con el rol real de DB.
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { SupabaseAuthService } from '@/auth/SupabaseAuthService'
|
||||
|
||||
// ─── Mocks con vi.hoisted para evitar el TDZ al ser elevados al tope del archivo ──
|
||||
|
||||
const { mockGetSession, mockMaybeSingle } = vi.hoisted(() => ({
|
||||
mockGetSession: vi.fn(),
|
||||
mockMaybeSingle: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/supabase', () => {
|
||||
const buildChain = () => {
|
||||
const chain: Record<string, unknown> = {}
|
||||
chain.select = vi.fn().mockReturnValue(chain)
|
||||
chain.eq = vi.fn().mockReturnValue(chain)
|
||||
chain.abortSignal = vi.fn().mockReturnValue(chain)
|
||||
chain.maybeSingle = mockMaybeSingle
|
||||
return chain
|
||||
}
|
||||
|
||||
return {
|
||||
supabase: {
|
||||
auth: {
|
||||
getSession: mockGetSession,
|
||||
onAuthStateChange: vi.fn(() => ({ data: { subscription: { unsubscribe: vi.fn() } } })),
|
||||
signInWithPassword: vi.fn(),
|
||||
},
|
||||
from: vi.fn().mockImplementation(() => buildChain()),
|
||||
},
|
||||
SUPABASE_URL: 'https://test.supabase.co',
|
||||
SUPABASE_ANON_KEY: 'test-key',
|
||||
}
|
||||
})
|
||||
|
||||
// ─── Tests ───────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('SupabaseAuthService', () => {
|
||||
const service = new SupabaseAuthService()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockMaybeSingle.mockResolvedValue({ data: null })
|
||||
})
|
||||
|
||||
describe('buildAuthUser (vía getCurrentUser)', () => {
|
||||
it('devuelve platform_admin optimista para email @inquality.com.py', async () => {
|
||||
mockGetSession.mockResolvedValue({
|
||||
data: {
|
||||
session: {
|
||||
user: {
|
||||
id: 'user-1',
|
||||
email: 'test@inquality.com.py',
|
||||
user_metadata: { full_name: 'Test InQ' },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const user = await service.getCurrentUser()
|
||||
expect(user?.platformRole).toBe('platform_admin')
|
||||
expect(user?.email).toBe('test@inquality.com.py')
|
||||
})
|
||||
|
||||
it('devuelve guest optimista para email externo', async () => {
|
||||
mockGetSession.mockResolvedValue({
|
||||
data: {
|
||||
session: {
|
||||
user: {
|
||||
id: 'user-2',
|
||||
email: 'cliente@empresa.com',
|
||||
user_metadata: { full_name: 'Cliente Externo' },
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const user = await service.getCurrentUser()
|
||||
expect(user?.platformRole).toBe('guest')
|
||||
})
|
||||
})
|
||||
|
||||
describe('syncProfileFromDB', () => {
|
||||
it('retorna client_editor desde DB (sobrescribe heurística guest para email externo)', async () => {
|
||||
mockMaybeSingle.mockResolvedValue({
|
||||
data: {
|
||||
name: 'Lucia García',
|
||||
company: 'Banco Solar',
|
||||
platform_role: 'client_editor',
|
||||
org_id: 'org-abc-123',
|
||||
},
|
||||
})
|
||||
|
||||
const profile = await service.syncProfileFromDB('user-2')
|
||||
expect(profile?.platformRole).toBe('client_editor')
|
||||
expect(profile?.orgId).toBe('org-abc-123')
|
||||
expect(profile?.company).toBe('Banco Solar')
|
||||
})
|
||||
|
||||
it('retorna platform_admin desde DB incluso para email sin dominio @inquality', async () => {
|
||||
mockMaybeSingle.mockResolvedValue({
|
||||
data: {
|
||||
name: 'Admin Externo',
|
||||
company: null,
|
||||
platform_role: 'platform_admin',
|
||||
org_id: 'org-inq-456',
|
||||
},
|
||||
})
|
||||
|
||||
const profile = await service.syncProfileFromDB('user-3')
|
||||
expect(profile?.platformRole).toBe('platform_admin')
|
||||
expect(profile?.orgId).toBe('org-inq-456')
|
||||
})
|
||||
|
||||
it('retiene orgId correctamente tras sync con client_viewer', async () => {
|
||||
const orgId = 'b1c2d3e4-f5a6-7890-abcd-ef1234567890'
|
||||
mockMaybeSingle.mockResolvedValue({
|
||||
data: {
|
||||
name: 'Viewer Test',
|
||||
company: 'Empresa SA',
|
||||
platform_role: 'client_viewer',
|
||||
org_id: orgId,
|
||||
},
|
||||
})
|
||||
|
||||
const profile = await service.syncProfileFromDB('user-4')
|
||||
expect(profile?.orgId).toBe(orgId)
|
||||
expect(profile?.platformRole).toBe('client_viewer')
|
||||
})
|
||||
|
||||
it('retorna null cuando el usuario no existe en DB', async () => {
|
||||
mockMaybeSingle.mockResolvedValue({ data: null })
|
||||
|
||||
const profile = await service.syncProfileFromDB('nonexistent-user')
|
||||
expect(profile).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user