import { beforeEach, describe, expect, it, vi } from 'vitest' import { createEvent, mockSuccessReadBody } from './setup' import fs from 'fs/promises' import sharp from 'sharp' vi.mock('../server/utils/auth.js', () => ({ getUserFromToken: vi.fn(), verifyToken: vi.fn(), readUsers: vi.fn(), writeUsers: vi.fn() })) vi.mock('sharp', () => ({ default: vi.fn(() => ({ resize: vi.fn().mockReturnThis(), toFile: vi.fn().mockResolvedValue({}) })) })) vi.mock('multer', () => { const single = vi.fn((field) => (req, _res, cb) => { if (req.__mockMulterError) { cb(req.__mockMulterError) return } req.file = req.__mockFile || null req.body = req.body || {} cb(null) }) const multerFn = vi.fn(() => ({ single })) const diskStorage = vi.fn(() => ({})) multerFn.diskStorage = diskStorage return { default: multerFn, diskStorage } }) const authUtils = await import('../server/utils/auth.js') import uploadHandler from '../server/api/galerie/upload.post.js' import listHandler from '../server/api/galerie/list.get.js' import imageHandler from '../server/api/galerie/[id].get.js' describe('Galerie API Endpoints', () => { beforeEach(() => { vi.clearAllMocks() vi.spyOn(fs, 'readFile').mockResolvedValue('[]') vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined) vi.spyOn(fs, 'mkdir').mockResolvedValue(undefined) vi.spyOn(fs, 'access').mockResolvedValue(undefined) }) describe('POST /api/galerie/upload', () => { it('erfordert Authentifizierung', async () => { const event = createEvent({ method: 'POST' }) event.node.req.__mockFile = { filename: 'test.jpg', path: 'tmp/test.jpg', originalname: 'test.jpg', mimetype: 'image/jpeg' } event.node.req.body = { title: 'Test', isPublic: 'true' } await expect(uploadHandler(event)).rejects.toMatchObject({ statusCode: 401 }) }) it('erfordert Admin- oder Vorstand-Rolle', async () => { const event = createEvent({ method: 'POST', cookies: { auth_token: 'token' } }) event.node.req.__mockFile = { filename: 'test.jpg', path: 'tmp/test.jpg', originalname: 'test.jpg', mimetype: 'image/jpeg' } event.node.req.body = { title: 'Test', isPublic: 'true' } authUtils.verifyToken.mockReturnValue({ id: '1' }) authUtils.getUserFromToken.mockResolvedValue({ id: '1', role: 'mitglied', active: true }) await expect(uploadHandler(event)).rejects.toMatchObject({ statusCode: 403 }) }) it('lädt Bild hoch und erstellt Thumbnail', async () => { const event = createEvent({ method: 'POST', cookies: { auth_token: 'token' } }) event.node.req.__mockFile = { filename: 'test.jpg', path: 'tmp/test.jpg', originalname: 'test.jpg', mimetype: 'image/jpeg' } event.node.req.body = { title: 'Test Bild', description: 'Beschreibung', isPublic: 'true' } authUtils.verifyToken.mockReturnValue({ id: '1' }) authUtils.getUserFromToken.mockResolvedValue({ id: '1', role: 'admin', active: true }) const response = await uploadHandler(event) expect(response.success).toBe(true) expect(sharp).toHaveBeenCalled() expect(fs.writeFile).toHaveBeenCalled() }) }) describe('GET /api/galerie/list', () => { it('zeigt öffentliche Bilder für alle', async () => { const event = createEvent() vi.spyOn(fs, 'readFile').mockResolvedValue(JSON.stringify([ { id: '1', title: 'Öffentlich', isPublic: true, previewFilename: 'preview_1.jpg', uploadedAt: '2025-01-01' }, { id: '2', title: 'Privat', isPublic: false, previewFilename: 'preview_2.jpg', uploadedAt: '2025-01-02' } ])) const response = await listHandler(event) expect(response.success).toBe(true) expect(response.images).toHaveLength(1) expect(response.images[0].id).toBe('1') }) it('zeigt alle Bilder für eingeloggte Mitglieder', async () => { const event = createEvent({ cookies: { auth_token: 'token' } }) vi.spyOn(fs, 'readFile').mockResolvedValue(JSON.stringify([ { id: '1', title: 'Öffentlich', isPublic: true, previewFilename: 'preview_1.jpg', uploadedAt: '2025-01-01' }, { id: '2', title: 'Privat', isPublic: false, previewFilename: 'preview_2.jpg', uploadedAt: '2025-01-02' } ])) authUtils.verifyToken.mockReturnValue({ id: '1' }) authUtils.getUserFromToken.mockResolvedValue({ id: '1', active: true }) const response = await listHandler(event) expect(response.images).toHaveLength(2) }) }) describe('GET /api/galerie/[id]', () => { it('verweigert Zugriff auf private Bilder für nicht eingeloggte Benutzer', async () => { const event = createEvent() event.context.params = { id: '1' } vi.spyOn(fs, 'readFile') .mockResolvedValueOnce(JSON.stringify([ { id: '1', filename: 'test.jpg', previewFilename: 'preview_test.jpg', isPublic: false } ])) await expect(imageHandler(event)).rejects.toMatchObject({ statusCode: 403 }) }) it('liefert Bild für eingeloggte Mitglieder', async () => { const event = createEvent({ cookies: { auth_token: 'token' } }) event.context.params = { id: '1' } vi.spyOn(fs, 'readFile') .mockResolvedValueOnce(JSON.stringify([ { id: '1', filename: 'test.jpg', previewFilename: 'preview_test.jpg', isPublic: false } ])) .mockResolvedValueOnce(Buffer.from('image data')) authUtils.verifyToken.mockReturnValue({ id: '1' }) authUtils.getUserFromToken.mockResolvedValue({ id: '1', active: true }) const response = await imageHandler(event) expect(response).toBeInstanceOf(Buffer) }) it('liefert Preview-Bild bei preview=true', async () => { const event = createEvent({ query: { preview: 'true' } }) event.context.params = { id: '1' } vi.spyOn(fs, 'readFile') .mockResolvedValueOnce(JSON.stringify([ { id: '1', filename: 'test.jpg', previewFilename: 'preview_test.jpg', isPublic: true } ])) .mockResolvedValueOnce(Buffer.from('preview data')) const response = await imageHandler(event) expect(response).toBeInstanceOf(Buffer) }) }) })