import { describe, it, expect, beforeEach, vi } from 'vitest' import { setActivePinia, createPinia } from 'pinia' import { useAuthStore } from '@/stores/authStore' function mockFetchResponse(status: number, body: unknown) { return Promise.resolve({ ok: status >= 200 && status < 300, status, json: () => Promise.resolve(body), }) } describe('authStore', () => { beforeEach(() => { setActivePinia(createPinia()) localStorage.clear() globalThis.fetch = vi.fn() vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(201, { token: 'test-token' }), ) }) it('starts unauthenticated', () => { const store = useAuthStore() expect(store.isAuthenticated).toBe(false) expect(store.token).toBeNull() }) it('restores token from localStorage', () => { localStorage.setItem('auth_token', 'saved-token') const store = useAuthStore() expect(store.token).toBe('saved-token') expect(store.isAuthenticated).toBe(true) }) it('sets token on registerUser success', async () => { const store = useAuthStore() await store.registerUser('test@example.com', 'password123') expect(store.token).toBe('test-token') expect(store.isAuthenticated).toBe(true) expect(localStorage.getItem('auth_token')).toBe('test-token') }) it('rejects when register API fails', async () => { vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(409, { message: 'E-postadressen är redan registrerad', }), ) const store = useAuthStore() await expect( store.registerUser('taken@example.com', 'password123'), ).rejects.toThrow('E-postadressen är redan registrerad') expect(store.isAuthenticated).toBe(false) expect(store.token).toBeNull() }) it('clears token on logout', () => { localStorage.setItem('auth_token', 'some-token') const store = useAuthStore() store.logout() expect(store.token).toBeNull() expect(store.isAuthenticated).toBe(false) expect(localStorage.getItem('auth_token')).toBeNull() }) it('sets token on loginUser success', async () => { vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(200, { token: 'login-token' }), ) const store = useAuthStore() await store.loginUser('user@example.com', 'password123') expect(store.token).toBe('login-token') expect(store.isAuthenticated).toBe(true) expect(localStorage.getItem('auth_token')).toBe('login-token') }) it('rejects when login API fails', async () => { vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(401, { message: 'Felaktig e-post eller lösenord' }), ) const store = useAuthStore() await expect( store.loginUser('user@example.com', 'wrongpassword'), ).rejects.toThrow('Felaktig e-post eller lösenord') expect(store.isAuthenticated).toBe(false) expect(store.token).toBeNull() }) it('calls login endpoint with correct credentials', async () => { vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(200, { token: 'login-token' }), ) const store = useAuthStore() await store.loginUser('user@example.com', 'password123') expect(globalThis.fetch).toHaveBeenCalledWith( '/api/auth/login', expect.objectContaining({ method: 'POST', body: JSON.stringify({ email: 'user@example.com', password: 'password123', }), }), ) }) it('does not call register endpoint on login', async () => { vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(200, { token: 'login-token' }), ) const store = useAuthStore() await store.loginUser('user@example.com', 'password123') const calls = vi.mocked(globalThis.fetch).mock.calls const registerCall = calls.find((call) => call[0] === '/api/auth/register') expect(registerCall).toBeUndefined() }) it('extracts role from JWT token', async () => { const jwt = makeJwt({ role: 'admin' }) vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(200, { token: jwt }), ) const store = useAuthStore() await store.loginUser('admin@example.com', 'password123') expect(store.role).toBe('admin') expect(store.isAdmin).toBe(true) }) it('defaults to null role for user role', async () => { const jwt = makeJwt({ role: 'user' }) vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(200, { token: jwt }), ) const store = useAuthStore() await store.loginUser('user@example.com', 'password123') expect(store.role).toBe('user') expect(store.isAdmin).toBe(false) }) it('clears role on logout', async () => { const jwt = makeJwt({ role: 'admin' }) vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(200, { token: jwt }), ) const store = useAuthStore() await store.loginUser('admin@example.com', 'password123') expect(store.isAdmin).toBe(true) store.logout() expect(store.role).toBeNull() expect(store.isAdmin).toBe(false) }) it('restores role from localStorage on init', () => { const jwt = makeJwt({ role: 'admin' }) localStorage.setItem('auth_token', jwt) const store = useAuthStore() expect(store.role).toBe('admin') expect(store.isAdmin).toBe(true) }) it('extracts email from JWT sub claim', async () => { const jwt = makeJwt({ sub: 'test@bilhej.se', role: 'user' }) vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(200, { token: jwt }), ) const store = useAuthStore() await store.loginUser('test@bilhej.se', 'test1234') expect(store.email).toBe('test@bilhej.se') }) it('returns null email when not authenticated', () => { const store = useAuthStore() expect(store.email).toBeNull() }) it('clears email on logout', async () => { const jwt = makeJwt({ sub: 'test@bilhej.se', role: 'user' }) vi.mocked(globalThis.fetch).mockResolvedValue( mockFetchResponse(200, { token: jwt }), ) const store = useAuthStore() await store.loginUser('test@bilhej.se', 'test1234') expect(store.email).toBe('test@bilhej.se') store.logout() expect(store.email).toBeNull() }) }) function makeJwt(payload: Record): string { const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' })) const body = btoa(JSON.stringify(payload)) const signature = 'test-sig' return `${header}.${body}.${signature}` }