Resumen de lo entregado
Some checks failed
/ Deploy to Cloudflare Pages (push) Has been cancelled

This commit is contained in:
2026-07-07 09:47:31 -03:00
parent 2922d4c2f3
commit bc9f70419c
26 changed files with 4102 additions and 23 deletions

View 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()
})
})
})