import { describe, expect, it, vi } from 'vitest'; import { renderHook } from '@testing-library/react'; import { usePermissions } from './usePermissions'; import type { CurrentUser } from '@/shared/types/auth'; const mockUser: CurrentUser = { id: 'user-1', email: 'test@example.com', firstName: 'Test', lastName: 'User', permissions: ['READ_CAMPUSES', 'READ_DASHBOARD', 'READ_FRAME'], app_role: { name: 'teacher', globalAccess: false }, }; const mockGlobalUser: CurrentUser = { id: 'admin-1', email: 'admin@example.com', firstName: 'Admin', lastName: 'User', permissions: [], app_role: { name: 'super_admin', globalAccess: true }, }; // Mock the auth context vi.mock('@/contexts/useAuth', () => ({ useAuth: vi.fn(), })); import { useAuth } from '@/contexts/useAuth'; describe('usePermissions', () => { it('returns empty permissions array for null user', () => { vi.mocked(useAuth).mockReturnValue({ user: null, profile: null, loading: false, isAuthenticated: false, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.permissions).toEqual([]); }); it('returns user permissions array when user exists', () => { vi.mocked(useAuth).mockReturnValue({ user: mockUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.permissions).toEqual(['READ_CAMPUSES', 'READ_DASHBOARD', 'READ_FRAME']); }); describe('has()', () => { it('returns true for granted permission', () => { vi.mocked(useAuth).mockReturnValue({ user: mockUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.has('READ_CAMPUSES')).toBe(true); }); it('returns false for missing permission', () => { vi.mocked(useAuth).mockReturnValue({ user: mockUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.has('DELETE_ORGANIZATIONS')).toBe(false); }); it('returns true for any permission when user has globalAccess', () => { vi.mocked(useAuth).mockReturnValue({ user: mockGlobalUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.has('DELETE_ORGANIZATIONS')).toBe(true); expect(result.current.has('UPDATE_USERS')).toBe(true); }); it('returns false for null user', () => { vi.mocked(useAuth).mockReturnValue({ user: null, profile: null, loading: false, isAuthenticated: false, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.has('READ_CAMPUSES')).toBe(false); }); }); describe('hasAny()', () => { it('returns true if any permission matches', () => { vi.mocked(useAuth).mockReturnValue({ user: mockUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.hasAny(['READ_CAMPUSES', 'DELETE_ORGANIZATIONS'])).toBe(true); }); it('returns false if no permissions match', () => { vi.mocked(useAuth).mockReturnValue({ user: mockUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.hasAny(['DELETE_ORGANIZATIONS', 'UPDATE_USERS'])).toBe(false); }); it('returns true for any permissions when user has globalAccess', () => { vi.mocked(useAuth).mockReturnValue({ user: mockGlobalUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.hasAny(['DELETE_ORGANIZATIONS', 'UPDATE_USERS'])).toBe(true); }); it('returns false for null user', () => { vi.mocked(useAuth).mockReturnValue({ user: null, profile: null, loading: false, isAuthenticated: false, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.hasAny(['READ_CAMPUSES'])).toBe(false); }); }); describe('hasAll()', () => { it('returns true if all permissions match', () => { vi.mocked(useAuth).mockReturnValue({ user: mockUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.hasAll(['READ_CAMPUSES', 'READ_DASHBOARD'])).toBe(true); }); it('returns false if any permission is missing', () => { vi.mocked(useAuth).mockReturnValue({ user: mockUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.hasAll(['READ_CAMPUSES', 'DELETE_ORGANIZATIONS'])).toBe(false); }); it('returns true for all permissions when user has globalAccess', () => { vi.mocked(useAuth).mockReturnValue({ user: mockGlobalUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.hasAll(['DELETE_ORGANIZATIONS', 'UPDATE_USERS'])).toBe(true); }); it('returns false for null user', () => { vi.mocked(useAuth).mockReturnValue({ user: null, profile: null, loading: false, isAuthenticated: false, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result } = renderHook(() => usePermissions()); expect(result.current.hasAll(['READ_CAMPUSES'])).toBe(false); }); }); describe('memoization', () => { it('returns same object reference when user does not change', () => { vi.mocked(useAuth).mockReturnValue({ user: mockUser, profile: null, loading: false, isAuthenticated: true, signIn: vi.fn(), signUp: vi.fn(), signOut: vi.fn(), updateProfile: vi.fn(), }); const { result, rerender } = renderHook(() => usePermissions()); const firstResult = result.current; rerender(); const secondResult = result.current; expect(firstResult).toBe(secondResult); }); }); });