bilhej/frontend/src/__tests__/authStore.spec.ts
Joakim Mörling 93ece8128a Use bilhej.se domain for dev test user email.
Aligns seeded and test login addresses with production branding while
keeping admin@bilhalsning.se for local docker admin seed only.

- Change test@bilhalsning.se to test@bilhej.se in dev migration and all tests
2026-05-21 15:14:11 +02:00

220 lines
6.4 KiB
TypeScript

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, unknown>): string {
const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }))
const body = btoa(JSON.stringify(payload))
const signature = 'test-sig'
return `${header}.${body}.${signature}`
}