192 lines
7.1 KiB
TypeScript
192 lines
7.1 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import fs from 'fs/promises'
|
|
|
|
vi.mock('../server/utils/auth.js', () => ({
|
|
readUsers: vi.fn(),
|
|
isHiddenUser: vi.fn(user => user?.hidden === true || user?.invisible === true || user?.isHidden === true || user?.systemAccount === true)
|
|
}))
|
|
|
|
vi.mock('../server/utils/members.js', () => ({
|
|
readMembers: vi.fn()
|
|
}))
|
|
|
|
vi.mock('../server/utils/termine.js', () => ({
|
|
readTermine: vi.fn().mockResolvedValue([])
|
|
}))
|
|
|
|
vi.mock('../server/utils/news.js', () => ({
|
|
readNews: vi.fn().mockResolvedValue([])
|
|
}))
|
|
|
|
vi.mock('../server/utils/spielplan-data.js', () => ({
|
|
getDefaultSpielplanSeason: vi.fn().mockResolvedValue('25--26'),
|
|
readSpielplanData: vi.fn().mockResolvedValue({ data: [] })
|
|
}))
|
|
|
|
vi.mock('../server/utils/push-notifications.js', () => ({
|
|
sendPushToUsers: vi.fn().mockResolvedValue({ sent: 1, failed: 0, removed: 0, recipients: 1, tokenCount: 1, skipped: false })
|
|
}))
|
|
|
|
vi.mock('../server/utils/logger.js', () => ({
|
|
error: vi.fn(),
|
|
info: vi.fn(),
|
|
warn: vi.fn()
|
|
}))
|
|
|
|
const authUtils = await import('../server/utils/auth.js')
|
|
const memberUtils = await import('../server/utils/members.js')
|
|
const pushUtils = await import('../server/utils/push-notifications.js')
|
|
const spielplanUtils = await import('../server/utils/spielplan-data.js')
|
|
|
|
const { runNotificationSchedulerTick } = await import('../server/utils/notification-scheduler.js')
|
|
|
|
const schedulerNow = new Date('2026-06-14T07:00:00.000Z')
|
|
const recipient = {
|
|
id: 'recipient',
|
|
name: 'Push Empfaenger',
|
|
active: true,
|
|
notificationSettings: {
|
|
birthdays: true,
|
|
notificationTime: '09:00'
|
|
}
|
|
}
|
|
|
|
describe('Notification Scheduler', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
vi.spyOn(fs, 'readFile').mockImplementation(async (filePath) => {
|
|
if (String(filePath).includes('mannschaften_25--26.csv')) {
|
|
return [
|
|
'Mannschaft,Liga,Staffelleiter,Telefon,Heimspieltag,Spielsystem,Mannschaftsführer,Spieler,Weitere Informationen Link,Letzte Aktualisierung',
|
|
'Erwachsene 1,,,,,,Mannschaftsfuehrer,Max Spieler,,',
|
|
'Erwachsene 2,,,,,,Andere Person,Andere Spieler,,'
|
|
].join('\n')
|
|
}
|
|
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' })
|
|
})
|
|
vi.spyOn(fs, 'mkdir').mockResolvedValue(undefined)
|
|
vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
|
|
memberUtils.readMembers.mockResolvedValue([])
|
|
authUtils.readUsers.mockResolvedValue([recipient])
|
|
spielplanUtils.getDefaultSpielplanSeason.mockResolvedValue('25--26')
|
|
spielplanUtils.readSpielplanData.mockResolvedValue({ data: [] })
|
|
})
|
|
|
|
it('sendet Geburtstags-Push nur fuer Mitglieder mit expliziter Geburtstagsfreigabe', async () => {
|
|
memberUtils.readMembers.mockResolvedValue([
|
|
{ firstName: 'Erlaubt', lastName: 'Person', active: true, geburtsdatum: '1990-06-14', visibility: { showBirthday: true } },
|
|
{ firstName: 'Privat', lastName: 'Person', active: true, geburtsdatum: '1990-06-14', visibility: { showBirthday: false } },
|
|
{ firstName: 'Unklar', lastName: 'Person', active: true, geburtsdatum: '1990-06-14' }
|
|
])
|
|
|
|
await runNotificationSchedulerTick(schedulerNow)
|
|
|
|
expect(pushUtils.sendPushToUsers).toHaveBeenCalledTimes(1)
|
|
expect(pushUtils.sendPushToUsers).toHaveBeenCalledWith(expect.objectContaining({
|
|
title: 'Geburtstage heute',
|
|
body: 'Erlaubt Person hat heute Geburtstag.',
|
|
data: { type: 'birthdays', date: '2026-06-14' }
|
|
}))
|
|
})
|
|
|
|
it('nennt bei mehreren Geburtstags-Pushes nur Namen und kein Alter', async () => {
|
|
memberUtils.readMembers.mockResolvedValue([
|
|
{ firstName: 'Anna', lastName: 'Beispiel', active: true, geburtsdatum: '1980-06-14', visibility: { showBirthday: true } },
|
|
{ firstName: 'Bert', lastName: 'Beispiel', active: true, geburtsdatum: '2010-06-14', visibility: { showBirthday: true } }
|
|
])
|
|
|
|
await runNotificationSchedulerTick(schedulerNow)
|
|
|
|
const payload = pushUtils.sendPushToUsers.mock.calls[0][0]
|
|
expect(payload.body).toBe('Geburtstage heute: Anna Beispiel, Bert Beispiel.')
|
|
expect(payload.body).not.toMatch(/\b\d+\b/)
|
|
expect(payload.body).not.toContain('Jahre')
|
|
})
|
|
|
|
it('sendet Punktspiel-Push nur einmal, wenn alle, eigene und ausgewaehlte Mannschaft dasselbe Spiel treffen', async () => {
|
|
const matchUser = {
|
|
id: 'match-user',
|
|
name: 'Max Spieler',
|
|
active: true,
|
|
notificationSettings: {
|
|
allTeamMatches: true,
|
|
ownTeamMatches: true,
|
|
selectedTeamSlugs: ['erwachsene-1'],
|
|
selectedTeamSeason: '25--26',
|
|
notificationTime: '09:00'
|
|
}
|
|
}
|
|
authUtils.readUsers.mockResolvedValue([matchUser])
|
|
spielplanUtils.readSpielplanData.mockResolvedValue({
|
|
data: [{
|
|
Termin: '14.06.2026 20:15',
|
|
BegegnungNr: 'spiel-1',
|
|
Altersklasse: 'Erwachsene',
|
|
HeimVereinName: 'Harheimer TC',
|
|
HeimMannschaftAltersklasse: 'Erwachsene',
|
|
HeimMannschaftNr: '1',
|
|
HeimMannschaft: 'Harheimer TC',
|
|
GastVereinName: 'Gastverein',
|
|
GastMannschaftAltersklasse: 'Erwachsene',
|
|
GastMannschaftNr: '1',
|
|
GastMannschaft: 'Gastverein'
|
|
}]
|
|
})
|
|
|
|
await runNotificationSchedulerTick(schedulerNow)
|
|
|
|
expect(pushUtils.sendPushToUsers).toHaveBeenCalledTimes(1)
|
|
const payload = pushUtils.sendPushToUsers.mock.calls[0][0]
|
|
expect(payload.title).toBe('Punktspiele')
|
|
expect(payload.predicate(matchUser, matchUser.notificationSettings)).toBe(true)
|
|
expect(payload.bodyForUser(matchUser, matchUser.notificationSettings)).toContain('Harheimer TC - Gastverein')
|
|
})
|
|
|
|
it('fasst eigene und ausgewaehlte Punktspiele in einer Benachrichtigung zusammen', async () => {
|
|
const matchUser = {
|
|
id: 'match-user',
|
|
name: 'Max Spieler',
|
|
active: true,
|
|
notificationSettings: {
|
|
allTeamMatches: false,
|
|
ownTeamMatches: true,
|
|
selectedTeamSlugs: ['erwachsene-2'],
|
|
selectedTeamSeason: '25--26',
|
|
notificationTime: '09:00'
|
|
}
|
|
}
|
|
authUtils.readUsers.mockResolvedValue([matchUser])
|
|
spielplanUtils.readSpielplanData.mockResolvedValue({
|
|
data: [
|
|
{
|
|
Termin: '14.06.2026 20:15',
|
|
BegegnungNr: 'spiel-1',
|
|
Altersklasse: 'Erwachsene',
|
|
HeimVereinName: 'Harheimer TC',
|
|
HeimMannschaftAltersklasse: 'Erwachsene',
|
|
HeimMannschaftNr: '1',
|
|
HeimMannschaft: 'Harheimer TC',
|
|
GastMannschaft: 'Gastverein'
|
|
},
|
|
{
|
|
Termin: '14.06.2026 20:30',
|
|
BegegnungNr: 'spiel-2',
|
|
Altersklasse: 'Erwachsene',
|
|
HeimMannschaft: 'Gastverein II',
|
|
GastVereinName: 'Harheimer TC',
|
|
GastMannschaftAltersklasse: 'Erwachsene',
|
|
GastMannschaftNr: '2',
|
|
GastMannschaft: 'Harheimer TC II'
|
|
}
|
|
]
|
|
})
|
|
|
|
await runNotificationSchedulerTick(schedulerNow)
|
|
|
|
expect(pushUtils.sendPushToUsers).toHaveBeenCalledTimes(1)
|
|
const payload = pushUtils.sendPushToUsers.mock.calls[0][0]
|
|
expect(payload.predicate(matchUser, matchUser.notificationSettings)).toBe(true)
|
|
expect(payload.bodyForUser(matchUser, matchUser.notificationSettings)).toBe('2 Punktspiele am 14.06.2026')
|
|
})
|
|
})
|