import { beforeEach, describe, expect, it, vi } from 'vitest' import { createPinia, setActivePinia } from 'pinia' import { useAuthStore, type AuthUser } from '../app/stores/auth' type AuthPlugin = typeof import('../app/plugins/auth').default function createRuntimeConfig(authMode: string | undefined = undefined) { return { public: { authMode, authLoginUrl: '/login', authLogoutUrl: '/logout', authUserinfoUrl: '/api/auth/me' } } } async function loadPlugin() { vi.resetModules() vi.stubGlobal('defineNuxtPlugin', (setup: AuthPlugin) => setup) return (await import('../app/plugins/auth')).default } describe('auth plugin', () => { beforeEach(() => { setActivePinia(createPinia()) vi.restoreAllMocks() vi.unstubAllGlobals() }) it('initializes a disabled auth client and keeps auth turned off', async () => { vi.stubGlobal('useRuntimeConfig', () => createRuntimeConfig()) const plugin = await loadPlugin() const { provide } = plugin() const auth = provide.auth expect(auth.mode).toBe('disabled') expect(auth.isEnabled).toBe(false) await auth.ensureInitialized() const store = useAuthStore() expect(auth.isReady.value).toBe(true) expect(auth.isAuthenticated.value).toBe(false) expect(store.isInitialized).toBe(true) expect(store.user).toBeNull() }) it('uses a mock user in mock mode and supports login and logout locally', async () => { vi.stubGlobal('useRuntimeConfig', () => createRuntimeConfig('mock')) const plugin = await loadPlugin() const { provide } = plugin() const auth = provide.auth await auth.ensureInitialized() const store = useAuthStore() expect(auth.mode).toBe('mock') expect(auth.isEnabled).toBe(true) expect(auth.isAuthenticated.value).toBe(true) expect(store.user).toMatchObject({ id: 'mock-user', email: 'mock@example.com', roles: ['USER'] }) auth.logout() expect(store.user).toBeNull() auth.login() expect(store.user).toMatchObject({ id: 'mock-user', email: 'mock@example.com' }) }) it('loads the current user from the userinfo endpoint in userinfo mode', async () => { const fetchMock = vi.fn().mockResolvedValue({ id: 'user-1', email: 'john@example.com', roles: ['USER'] } satisfies AuthUser) vi.stubGlobal('useRuntimeConfig', () => createRuntimeConfig('userinfo')) vi.stubGlobal('$fetch', fetchMock) const plugin = await loadPlugin() const { provide } = plugin() const auth = provide.auth await auth.ensureInitialized() const store = useAuthStore() expect(fetchMock).toHaveBeenCalledWith('/api/auth/me', { credentials: 'include' }) expect(auth.isAuthenticated.value).toBe(true) expect(store.user).toMatchObject({ id: 'user-1', email: 'john@example.com' }) }) it('falls back to an anonymous session when userinfo loading fails', async () => { const fetchMock = vi.fn().mockRejectedValue(new Error('network error')) vi.stubGlobal('useRuntimeConfig', () => createRuntimeConfig('userinfo')) vi.stubGlobal('$fetch', fetchMock) const plugin = await loadPlugin() const { provide } = plugin() const auth = provide.auth await auth.ensureInitialized() const store = useAuthStore() expect(auth.isReady.value).toBe(true) expect(auth.isAuthenticated.value).toBe(false) expect(store.isInitialized).toBe(true) expect(store.user).toBeNull() }) it('redirects login and logout in userinfo mode', async () => { vi.stubGlobal('useRuntimeConfig', () => createRuntimeConfig('userinfo')) vi.stubGlobal('$fetch', vi.fn().mockResolvedValue(null)) vi.stubGlobal('window', { location: { href: '' } }) const plugin = await loadPlugin() const { provide } = plugin() const auth = provide.auth auth.login() expect(window.location.href).toBe('/login') const store = useAuthStore() store.setUser({ id: 'user-2', roles: ['USER'] }) auth.logout() expect(window.location.href).toBe('/logout') expect(store.user).toBeNull() }) })