Files
harheimertc/tests/members-endpoints.spec.ts
Torsten Schulz (local) 530e544542
Some checks failed
Code Analysis and Production Deploy / analyze (push) Failing after 5m40s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Has been skipped
Implemented the possibility ofa hidden user for playstore tests
2026-06-09 11:32:00 +02:00

335 lines
13 KiB
TypeScript

// @ts-nocheck
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createEvent, mockSuccessReadBody } from './setup'
vi.mock('../server/utils/auth.js', () => ({
verifyToken: vi.fn(),
getUserById: vi.fn(),
getUserFromToken: vi.fn(),
readUsers: vi.fn(),
readMembers: vi.fn(),
writeUsers: vi.fn(),
readSessions: vi.fn(),
writeSessions: vi.fn(),
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
}),
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))
}),
hasRole: vi.fn((user, role) => {
if (!user) return false
const userRoles = Array.isArray(user.roles) ? user.roles : (user.role ? [user.role] : [])
return userRoles.includes(role)
}),
normalizeUserEmail: vi.fn(email => String(email || '').trim().toLowerCase()),
isHiddenUser: vi.fn(user => user?.hidden === true || user?.invisible === true || user?.isHidden === true || user?.systemAccount === true || user?.accountType === 'playstore_review')
}))
vi.mock('../server/utils/members.js', () => ({
readMembers: vi.fn(),
writeMembers: vi.fn(),
saveMember: vi.fn(),
deleteMember: vi.fn(),
normalizeDate: vi.fn((value) => value)
}))
const authUtils = await import('../server/utils/auth.js')
const memberUtils = await import('../server/utils/members.js')
import membersGetHandler from '../server/api/members.get.js'
import membersPostHandler from '../server/api/members.post.js'
import membersDeleteHandler from '../server/api/members.delete.js'
import membersBulkHandler from '../server/api/members/bulk.post.js'
import membersBulkHandler from '../server/api/members/bulk.post.js'
import toggleMannschaftsspielerHandler from '../server/api/members/toggle-mannschaftsspieler.post.js'
describe('Members API Endpoints', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('GET /api/members', () => {
it('verlangt Authentifizierung', async () => {
const event = createEvent()
await expect(membersGetHandler(event)).rejects.toMatchObject({ statusCode: 401 })
})
it('gibt zusammengeführte Mitglieder zurück', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
memberUtils.readMembers.mockResolvedValue([
{ id: 'm1', firstName: 'Anna', lastName: 'Muster', email: 'anna@club.de', hasHallKey: true }
])
authUtils.readUsers.mockResolvedValue([
{ id: 'u1', name: 'Ben Nutzer', email: 'ben@club.de', role: 'mitglied', active: true }
])
authUtils.getUserFromToken.mockResolvedValue({ id: '1', role: 'mitglied' })
const response = await membersGetHandler(event)
expect(response.success).toBe(true)
expect(response.members).toHaveLength(2)
expect(response.members[0]).toHaveProperty('hasHallKey', true)
})
it('zeigt Legacy-Mitglieder ohne active-Flag weiterhin an', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
memberUtils.readMembers.mockResolvedValue([
{ id: 'm1', firstName: 'Anna', lastName: 'Muster', geburtsdatum: '2000-01-01' },
{ id: 'm2', firstName: 'Offen', lastName: 'Antrag', geburtsdatum: '2001-01-01', status: 'pending' }
])
authUtils.readUsers.mockResolvedValue([])
authUtils.getUserFromToken.mockResolvedValue({ id: '1', role: 'mitglied' })
const response = await membersGetHandler(event)
expect(response.success).toBe(true)
expect(response.members).toHaveLength(1)
expect(response.members[0].name).toBe('Anna Muster')
})
it('blendet unsichtbare Playstore-Benutzer und passende manuelle Einträge aus', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
memberUtils.readMembers.mockResolvedValue([
{ id: 'm1', firstName: 'Anna', lastName: 'Muster', email: 'anna@club.de' },
{ id: 'm2', firstName: 'Play', lastName: 'Store', email: 'review@club.de' }
])
authUtils.readUsers.mockResolvedValue([
{ id: 'u1', name: 'Ben Nutzer', email: 'ben@club.de', role: 'mitglied', active: true },
{ id: 'u2', name: 'Playstore Review', email: 'review@club.de', roles: ['mitglied'], active: true, accountType: 'playstore_review' }
])
authUtils.getUserFromToken.mockResolvedValue({ id: '1', role: 'mitglied' })
const response = await membersGetHandler(event)
expect(response.members.map(member => member.email || member.name)).not.toContain('review@club.de')
expect(response.members.map(member => member.name)).not.toContain('Play Store')
expect(response.members.map(member => member.name)).not.toContain('Playstore Review')
expect(response.members).toHaveLength(2)
})
})
describe('POST /api/members', () => {
const baseBody = {
firstName: 'Lisa',
lastName: 'Beispiel',
geburtsdatum: '2000-01-01',
email: 'lisa@example.com',
hasHallKey: true
}
it('verweigert Zugriff ohne Token', async () => {
const event = createEvent()
mockSuccessReadBody(baseBody)
await expect(membersPostHandler(event)).rejects.toMatchObject({ statusCode: 401 })
})
it('verlangt Admin- oder Vorstand-Rolle', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody(baseBody)
authUtils.getUserFromToken.mockResolvedValue({ id: '2', role: 'mitglied' })
await expect(membersPostHandler(event)).rejects.toMatchObject({ statusCode: 403 })
})
it('gibt 409 bei Duplikaten zurück', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody(baseBody)
authUtils.getUserFromToken.mockResolvedValue({ id: '2', role: 'admin' })
memberUtils.saveMember.mockRejectedValue(new Error('existiert bereits'))
await expect(membersPostHandler(event)).rejects.toMatchObject({ statusCode: 409 })
})
it('speichert Mitglied erfolgreich', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody(baseBody)
authUtils.getUserFromToken.mockResolvedValue({ id: '2', role: 'admin' })
memberUtils.saveMember.mockResolvedValue(true)
const response = await membersPostHandler(event)
expect(response.success).toBe(true)
expect(memberUtils.saveMember).toHaveBeenCalledWith(expect.objectContaining({
hasHallKey: true
}))
})
it('erlaubt vorstand beim Speichern', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody(baseBody)
authUtils.getUserFromToken.mockResolvedValue({ id: '3', role: 'vorstand' })
memberUtils.saveMember.mockResolvedValue(true)
const response = await membersPostHandler(event)
expect(response.success).toBe(true)
})
it('erlaubt Updates von Altdaten ohne Geburtsdatum', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody({
id: 'legacy-1',
firstName: 'Lisa',
lastName: 'Beispiel',
email: 'lisa@example.com'
})
authUtils.getUserFromToken.mockResolvedValue({ id: '3', role: 'vorstand' })
memberUtils.saveMember.mockResolvedValue(true)
const response = await membersPostHandler(event)
expect(response.success).toBe(true)
expect(memberUtils.saveMember).toHaveBeenCalledWith(expect.objectContaining({
id: 'legacy-1',
geburtsdatum: ''
}))
})
})
describe('DELETE /api/members', () => {
it('validiert die ID', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody({})
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserById.mockResolvedValue({ id: '1', role: 'admin' })
await expect(membersDeleteHandler(event)).rejects.toMatchObject({ statusCode: 400 })
})
it('löscht Mitglied erfolgreich', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody({ id: 'm1' })
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserById.mockResolvedValue({ id: '1', role: 'vorstand' })
memberUtils.deleteMember.mockResolvedValue(true)
const response = await membersDeleteHandler(event)
expect(response.success).toBe(true)
expect(memberUtils.deleteMember).toHaveBeenCalledWith('m1')
})
})
describe('POST /api/members/bulk', () => {
const importBody = {
members: [
{ firstName: 'Paul', lastName: 'Team', geburtsdatum: '2001-02-03' }
]
}
it('validiert members-Array', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody({ members: [] })
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserById.mockResolvedValue({ id: '1', role: 'admin' })
await expect(membersBulkHandler(event)).rejects.toMatchObject({ statusCode: 400 })
})
it('importiert Mitglieder und schreibt Datei', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody(importBody)
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserById.mockResolvedValue({ id: '1', role: 'admin' })
memberUtils.readMembers.mockResolvedValue([])
memberUtils.writeMembers.mockResolvedValue(true)
const response = await membersBulkHandler(event)
expect(response.success).toBe(true)
expect(response.summary.imported).toBe(1)
expect(memberUtils.writeMembers).toHaveBeenCalled()
})
})
describe('POST /api/members/toggle-mannschaftsspieler', () => {
it('verlangt Authentifizierung', async () => {
const event = createEvent()
mockSuccessReadBody({ memberId: '1' })
await expect(toggleMannschaftsspielerHandler(event)).rejects.toMatchObject({ statusCode: 401 })
})
it('verlangt Admin- oder Vorstand-Rolle', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserById.mockResolvedValue({ id: '1', roles: ['mitglied'] })
authUtils.hasAnyRole.mockReturnValue(false)
mockSuccessReadBody({ memberId: '2' })
await expect(toggleMannschaftsspielerHandler(event)).rejects.toMatchObject({ statusCode: 403 })
})
it('gibt 400 wenn memberId fehlt', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserById.mockResolvedValue({ id: '1', roles: ['admin'] })
authUtils.hasAnyRole.mockReturnValue(true)
mockSuccessReadBody({})
await expect(toggleMannschaftsspielerHandler(event)).rejects.toMatchObject({ statusCode: 400 })
})
it('gibt 404 wenn Mitglied nicht gefunden', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserById.mockResolvedValue({ id: '1', roles: ['admin'] })
authUtils.hasAnyRole.mockReturnValue(true)
authUtils.readUsers.mockResolvedValue([])
memberUtils.readMembers.mockResolvedValue([])
mockSuccessReadBody({ memberId: 'unbekannt' })
await expect(toggleMannschaftsspielerHandler(event)).rejects.toMatchObject({ statusCode: 404 })
})
it('toggled isMannschaftsspieler für eingeloggten User', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserById.mockResolvedValue({ id: '1', roles: ['admin'] })
authUtils.hasAnyRole.mockReturnValue(true)
authUtils.readUsers.mockResolvedValue([
{ id: 'user-5', name: 'Max Muster', isMannschaftsspieler: false, roles: ['mitglied'] }
])
authUtils.writeUsers.mockResolvedValue(undefined)
memberUtils.readMembers.mockResolvedValue([])
mockSuccessReadBody({ memberId: 'user-5' })
const result = await toggleMannschaftsspielerHandler(event)
expect(result.success).toBe(true)
expect(result.isMannschaftsspieler).toBe(true)
expect(authUtils.writeUsers).toHaveBeenCalled()
})
it('toggled isMannschaftsspieler für manuelles Mitglied', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
authUtils.verifyToken.mockReturnValue({ id: '1' })
authUtils.getUserById.mockResolvedValue({ id: '1', roles: ['admin'] })
authUtils.hasAnyRole.mockReturnValue(true)
authUtils.readUsers.mockResolvedValue([])
memberUtils.readMembers.mockResolvedValue([
{ id: 'manual-1', firstName: 'Paul', lastName: 'Team', isMannschaftsspieler: true }
])
memberUtils.writeMembers.mockResolvedValue(undefined)
mockSuccessReadBody({ memberId: 'manual-1' })
const result = await toggleMannschaftsspielerHandler(event)
expect(result.success).toBe(true)
expect(result.isMannschaftsspieler).toBe(false)
expect(memberUtils.writeMembers).toHaveBeenCalled()
})
})
})