import { beforeEach, describe, expect, it, vi } from 'vitest' import { createEvent, mockSuccessReadBody } from './setup' vi.mock('../server/utils/auth.js', () => ({ getUserFromToken: vi.fn(), readUsers: vi.fn(), writeUsers: vi.fn(), hasAnyRole: vi.fn((user, ...roles) => { if (!user) return false const userRoles = Array.isArray(user.roles) ? user.roles : (user.role ? [user.role] : []) return roles.some(r => userRoles.includes(r)) }), migrateUserRoles: vi.fn((user) => { if (!user) return user if (Array.isArray(user.roles)) return user if (user.role) { user.roles = [user.role] delete user.role } else { user.roles = ['mitglied'] } return user }) })) vi.mock('nodemailer', () => { const sendMail = vi.fn().mockResolvedValue(true) const createTransport = vi.fn(() => ({ sendMail })) return { default: { createTransport }, createTransport } }) const authUtils = await import('../server/utils/auth.js') const nodemailer = await import('nodemailer') import usersListHandler from '../server/api/cms/users/list.get.js' import usersApproveHandler from '../server/api/cms/users/approve.post.js' import usersDeactivateHandler from '../server/api/cms/users/deactivate.post.js' import usersRejectHandler from '../server/api/cms/users/reject.post.js' import usersUpdateRoleHandler from '../server/api/cms/users/update-role.post.js' describe('CMS User Management Endpoints', () => { beforeEach(() => { vi.clearAllMocks() }) const adminEvent = () => { const event = createEvent({ cookies: { auth_token: 'token' } }) authUtils.getUserFromToken.mockResolvedValue({ id: 'admin', roles: ['admin'] }) authUtils.hasAnyRole.mockReturnValue(true) return event } describe('GET /api/cms/users/list', () => { it('verweigert Zugriff für Nicht-Admins', async () => { const event = createEvent({ cookies: { auth_token: 'token' } }) authUtils.getUserFromToken.mockResolvedValue({ id: 'user', roles: ['mitglied'] }) authUtils.hasAnyRole.mockReturnValue(false) await expect(usersListHandler(event)).rejects.toMatchObject({ statusCode: 403 }) }) it('liefert Benutzerliste ohne Passwörter', async () => { const event = adminEvent() authUtils.readUsers.mockResolvedValue([{ id: '1', email: 'a@b.de', name: 'Anna', roles: ['mitglied'], phone: '1', active: true, created: 'now', lastLogin: null, password: 'secret' }]) authUtils.migrateUserRoles.mockImplementation((user) => { if (!user) return user if (Array.isArray(user.roles)) return user if (user.role) { user.roles = [user.role] delete user.role } else { user.roles = ['mitglied'] } return user }) const response = await usersListHandler(event) expect(response.users[0]).not.toHaveProperty('password') expect(response.users).toHaveLength(1) }) }) describe('POST /api/cms/users/approve', () => { it('erfordert administrative Rolle', async () => { const event = createEvent({ cookies: { auth_token: 'token' } }) authUtils.getUserFromToken.mockResolvedValue({ id: 'user', roles: ['mitglied'] }) authUtils.hasAnyRole.mockReturnValue(false) mockSuccessReadBody({ userId: '1' }) await expect(usersApproveHandler(event)).rejects.toMatchObject({ statusCode: 403 }) }) it('meldet 404 bei unbekanntem Benutzer', async () => { const event = adminEvent() mockSuccessReadBody({ userId: '1' }) authUtils.readUsers.mockResolvedValue([]) await expect(usersApproveHandler(event)).rejects.toMatchObject({ statusCode: 404 }) }) it('aktiviert Benutzer und sendet Mail', async () => { const event = adminEvent() mockSuccessReadBody({ userId: '1', roles: ['vorstand'] }) authUtils.readUsers.mockResolvedValue([{ id: '1', email: 'user@test.de', name: 'Udo', active: false }]) authUtils.writeUsers.mockResolvedValue(true) // Setze SMTP-Credentials für Tests process.env.SMTP_USER = 'test@example.com' process.env.SMTP_PASS = 'test-password' const response = await usersApproveHandler(event) expect(response.success).toBe(true) expect(authUtils.writeUsers).toHaveBeenCalled() expect(nodemailer.default.createTransport).toHaveBeenCalled() }) }) describe('POST /api/cms/users/deactivate', () => { it('verhindert Selbst-Deaktivierung', async () => { const event = adminEvent() mockSuccessReadBody({ userId: 'admin' }) await expect(usersDeactivateHandler(event)).rejects.toMatchObject({ statusCode: 400 }) }) it('deaktiviert Benutzer', async () => { const event = adminEvent() mockSuccessReadBody({ userId: '1' }) authUtils.readUsers.mockResolvedValue([{ id: '1', active: true }]) authUtils.writeUsers.mockResolvedValue(true) const response = await usersDeactivateHandler(event) expect(response.success).toBe(true) expect(authUtils.writeUsers).toHaveBeenCalled() }) }) describe('POST /api/cms/users/reject', () => { it('löscht Benutzer aus der Liste', async () => { const event = adminEvent() mockSuccessReadBody({ userId: '2' }) authUtils.readUsers.mockResolvedValue([{ id: '1' }, { id: '2' }]) authUtils.writeUsers.mockResolvedValue(true) const response = await usersRejectHandler(event) expect(response.success).toBe(true) expect(authUtils.writeUsers).toHaveBeenCalledWith([{ id: '1' }]) }) }) describe('POST /api/cms/users/update-role', () => { it('validiert Rolle', async () => { const event = adminEvent() mockSuccessReadBody({ userId: '1', roles: ['invalid'] }) await expect(usersUpdateRoleHandler(event)).rejects.toMatchObject({ statusCode: 400 }) }) it('aktualisiert Rolle', async () => { const event = adminEvent() mockSuccessReadBody({ userId: '1', roles: ['vorstand'] }) authUtils.readUsers.mockResolvedValue([{ id: '1', roles: ['mitglied'] }]) authUtils.writeUsers.mockResolvedValue(true) authUtils.migrateUserRoles.mockImplementation((user) => { if (!user) return user if (Array.isArray(user.roles)) return user if (user.role) { user.roles = [user.role] delete user.role } else { user.roles = ['mitglied'] } return user }) const response = await usersUpdateRoleHandler(event) expect(response.success).toBe(true) expect(authUtils.writeUsers).toHaveBeenCalled() }) }) })