Erlaube das Hinzufügen von Teilnehmern ohne Klasse und normalisiere die Anzahl der Gruppen auf mindestens 1 in der Turnierverwaltung

This commit is contained in:
Torsten Schulz (local)
2025-12-13 12:25:17 +01:00
parent 0c28b12978
commit e83bc250a8
5 changed files with 316 additions and 83 deletions

View File

@@ -9,6 +9,7 @@ vi.mock('../utils/userUtils.js', async () => {
});
import sequelize from '../database.js';
import { Op } from 'sequelize';
import '../models/index.js';
import tournamentService from '../services/tournamentService.js';
@@ -73,4 +74,93 @@ describe('tournamentService', () => {
const matches = await tournamentService.getTournamentMatches('token', club.id, tournament.id);
expect(matches.length).toBeGreaterThan(0);
});
it('erlaubt Teilnehmer ohne Klasse', async () => {
const club = await Club.create({ name: 'Tournament Club' });
const memberA = await createMember(club.id, {
firstName: 'Clara',
lastName: 'C',
email: 'clara@example.com',
gender: 'female',
});
const tournament = await tournamentService.addTournament('token', club.id, 'Sommercup', '2025-06-01');
// ohne Klasse: legacy-Aufruf (3. Argument = tournamentId)
await tournamentService.addParticipant('token', club.id, tournament.id, memberA.id);
await expect(
tournamentService.addParticipant('token', club.id, tournament.id, memberA.id)
).rejects.toThrow('Teilnehmer bereits hinzugefügt');
const participantsNoClass = await tournamentService.getParticipants('token', club.id, tournament.id, null);
expect(participantsNoClass).toHaveLength(1);
expect(participantsNoClass[0].classId).toBe(null);
expect(participantsNoClass[0].clubMemberId).toBe(memberA.id);
});
it('normalisiert numberOfGroups=0 auf mindestens 1 Gruppe', async () => {
const club = await Club.create({ name: 'Tournament Club' });
const tournament = await tournamentService.addTournament('token', club.id, 'Gruppen-Test', '2025-07-01');
await tournamentService.createGroups('token', club.id, tournament.id, 0);
const groups = await TournamentGroup.findAll({ where: { tournamentId: tournament.id } });
expect(groups).toHaveLength(1);
});
it('legt bei numberOfGroups=4 genau 4 Gruppen an (ohne Klassen)', async () => {
const club = await Club.create({ name: 'Tournament Club' });
const tournament = await tournamentService.addTournament('token', club.id, 'Gruppen-4er', '2025-08-01');
await tournamentService.createGroups('token', club.id, tournament.id, 4);
const groups = await TournamentGroup.findAll({ where: { tournamentId: tournament.id } });
expect(groups).toHaveLength(4);
});
it('verteilt bei "zufällig verteilen" möglichst gleichmäßig (Differenz <= 1)', async () => {
const club = await Club.create({ name: 'Tournament Club' });
const tournament = await tournamentService.addTournament('token', club.id, 'Fill-Groups-Balanced', '2025-10-01');
// 10 Teilnehmer, 4 Gruppen => erwartete Größen: 3/3/2/2 (beliebige Reihenfolge)
const members = [];
for (let i = 0; i < 10; i++) {
// createMember Factory braucht eindeutige Emails
members.push(
await createMember(club.id, {
firstName: `P${i}`,
lastName: 'T',
email: `p${i}@example.com`,
gender: i % 2 === 0 ? 'male' : 'female',
})
);
}
for (const m of members) {
await tournamentService.addParticipant('token', club.id, tournament.id, m.id);
}
// Seeded-Balancing triggern: markiere mehrere als gesetzt
// (wir testen hier explizit, dass diese Optimierung die Größen-Balance NICHT kaputt machen darf)
const tmRows = await TournamentMember.findAll({ where: { tournamentId: tournament.id } });
const seededIds = tmRows.slice(0, 5).map(r => r.id);
await TournamentMember.update({ seeded: true }, { where: { id: { [Op.in]: seededIds } } });
await tournamentService.setModus('token', club.id, tournament.id, 'groups', 4, 1);
await tournamentService.createGroups('token', club.id, tournament.id, 4);
await tournamentService.fillGroups('token', club.id, tournament.id);
const groups = await TournamentGroup.findAll({ where: { tournamentId: tournament.id } });
expect(groups).toHaveLength(4);
const membersWithGroups = await TournamentMember.findAll({ where: { tournamentId: tournament.id } });
const countsByGroupId = membersWithGroups.reduce((m, tm) => {
m[tm.groupId] = (m[tm.groupId] || 0) + 1;
return m;
}, {});
const sizes = groups.map(g => countsByGroupId[g.id] || 0);
const min = Math.min(...sizes);
const max = Math.max(...sizes);
expect(max - min).toBeLessThanOrEqual(1);
expect(sizes.reduce((a, b) => a + b, 0)).toBe(10);
});
});