40227-vm/frontend/src/hooks/usePermissions.test.tsx
2026-06-12 06:55:35 +02:00

300 lines
8.0 KiB
TypeScript

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);
});
});
});