Update dependencies in package.json and package-lock.json; add testing scripts for Vitest, and include new packages such as supertest and vitest. Refactor Navigation component to improve event handling and cleanup, ensuring better performance and user experience. Enhance error handling in various API endpoints for PDF uploads and CSV saves, ensuring robust error propagation. Update nodemailer transport configuration for consistency across API handlers.
This commit is contained in:
142
tests/public-endpoints.spec.ts
Normal file
142
tests/public-endpoints.spec.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createEvent, mockSuccessReadBody } from './setup'
|
||||
import fsPromises from 'fs/promises'
|
||||
import { promises as fs } from 'fs'
|
||||
|
||||
vi.mock('nodemailer', () => {
|
||||
const sendMail = vi.fn().mockResolvedValue(true)
|
||||
const createTransport = vi.fn(() => ({ sendMail }))
|
||||
return {
|
||||
default: { createTransport },
|
||||
createTransport
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('../server/utils/news.js', () => ({
|
||||
readNews: vi.fn()
|
||||
}))
|
||||
|
||||
const nodemailer = await import('nodemailer')
|
||||
const newsUtils = await import('../server/utils/news.js')
|
||||
|
||||
import contactHandler from '../server/api/contact.post.js'
|
||||
import galerieHandler from '../server/api/galerie.get.js'
|
||||
import newsPublicHandler from '../server/api/news-public.get.js'
|
||||
import termineHandler from '../server/api/termine.get.js'
|
||||
import spielplaeneHandler from '../server/api/spielplaene.get.js'
|
||||
|
||||
describe('Öffentliche API-Endpunkte', () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('POST /api/contact', () => {
|
||||
it('validiert Pflichtfelder', async () => {
|
||||
const event = createEvent()
|
||||
mockSuccessReadBody({})
|
||||
|
||||
await expect(contactHandler(event)).rejects.toMatchObject({ statusCode: 400 })
|
||||
})
|
||||
|
||||
it('validiert E-Mail-Adresse', async () => {
|
||||
const event = createEvent()
|
||||
mockSuccessReadBody({ name: 'Max', email: 'invalid', subject: 'Hi', message: 'Test' })
|
||||
|
||||
await expect(contactHandler(event)).rejects.toMatchObject({ statusCode: 400 })
|
||||
})
|
||||
|
||||
it('sendet E-Mail bei gültigen Daten', async () => {
|
||||
const event = createEvent()
|
||||
mockSuccessReadBody({ name: 'Max', email: 'max@example.com', subject: 'Frage', message: 'Hallo' })
|
||||
|
||||
const response = await contactHandler(event)
|
||||
|
||||
expect(response.success).toBe(true)
|
||||
expect(nodemailer.default.createTransport).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/galerie', () => {
|
||||
it('liefert leeres Array, wenn Verzeichnis fehlt', async () => {
|
||||
vi.spyOn(fs, 'access').mockRejectedValue(new Error('not found'))
|
||||
|
||||
const result = await galerieHandler(createEvent())
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
|
||||
it('filtert nur Bilddateien', async () => {
|
||||
vi.spyOn(fs, 'access').mockResolvedValue(undefined)
|
||||
vi.spyOn(fs, 'readdir').mockResolvedValue(['foto-1.jpg', 'readme.md'])
|
||||
|
||||
const result = await galerieHandler(createEvent())
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].filename).toBe('foto-1.jpg')
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/news-public', () => {
|
||||
it('filtert nicht öffentliche News', async () => {
|
||||
const now = new Date()
|
||||
newsUtils.readNews.mockResolvedValue([
|
||||
{ id: 1, isPublic: true, isHidden: false, created: now.toISOString(), title: 'ok' },
|
||||
{ id: 2, isPublic: false, isHidden: false, created: now.toISOString() },
|
||||
{ id: 3, isPublic: true, isHidden: true, created: now.toISOString() }
|
||||
])
|
||||
|
||||
const response = await newsPublicHandler(createEvent())
|
||||
expect(response.success).toBe(true)
|
||||
expect(response.news).toHaveLength(1)
|
||||
expect(response.news[0].id).toBe(1)
|
||||
})
|
||||
|
||||
it('begrenzt Ergebnis auf drei Einträge', async () => {
|
||||
const now = Date.now()
|
||||
newsUtils.readNews.mockResolvedValue(
|
||||
Array.from({ length: 5 }).map((_, index) => ({
|
||||
id: index + 1,
|
||||
isPublic: true,
|
||||
isHidden: false,
|
||||
created: new Date(now - index * 1000).toISOString()
|
||||
}))
|
||||
)
|
||||
|
||||
const response = await newsPublicHandler(createEvent())
|
||||
expect(response.news).toHaveLength(3)
|
||||
expect(response.news[0].id).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/termine', () => {
|
||||
it('parst CSV-Dateien', async () => {
|
||||
const csv = 'Datum,Uhrzeit,Titel,Beschreibung,Kategorie\n2025-01-01,19:00,Training,Beschreibung,Sport'
|
||||
vi.spyOn(fsPromises, 'readFile').mockResolvedValue(csv)
|
||||
|
||||
const response = await termineHandler(createEvent())
|
||||
expect(response.success).toBe(true)
|
||||
expect(response.termine[0]).toMatchObject({ titel: 'Training', kategorie: 'Sport' })
|
||||
})
|
||||
|
||||
it('gibt leeres Array bei Fehlern zurück', async () => {
|
||||
vi.spyOn(fsPromises, 'readFile').mockRejectedValue(new Error('kaputt'))
|
||||
const response = await termineHandler(createEvent())
|
||||
expect(response.termine).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/spielplaene', () => {
|
||||
it('filtert nur erlaubte Dateitypen', async () => {
|
||||
vi.spyOn(fs, 'access').mockResolvedValue(undefined)
|
||||
vi.spyOn(fs, 'readdir').mockResolvedValue(['plan.pdf', 'notizen.txt'])
|
||||
|
||||
const result = await spielplaeneHandler(createEvent())
|
||||
expect(result).toEqual(['plan.pdf'])
|
||||
})
|
||||
|
||||
it('gibt leeres Array zurück, wenn Verzeichnis fehlt', async () => {
|
||||
vi.spyOn(fs, 'access').mockRejectedValue(new Error('not found'))
|
||||
const result = await spielplaeneHandler(createEvent())
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user