fix: support partial updates in tournament PUT endpoint

- Change PUT endpoint to only include fields present in the request
- Add support for team configuration fields (teamDurability, partnerRotation, allowByes)
- Fix contradictory test that expected resetting allowTies when not provided
- When a field is not in the request, it is preserved (not modified)

Fixes issue where Save Configuration button would fail with
'Tournament name is required' error.
This commit is contained in:
2026-04-03 19:56:58 -07:00
parent 07283d0334
commit 4d685558aa
2 changed files with 37 additions and 30 deletions
+11 -12
View File
@@ -101,12 +101,12 @@ describe('Tournament Update API', () => {
); );
}); });
it('should default allowTies to false when not provided', async () => { it('should NOT modify allowTies when not provided in request', async () => {
// Mock existing tournament // Mock existing tournament
eventFindUniqueMock.mockImplementation(async () => ({ eventFindUniqueMock.mockImplementation(async () => ({
id: 1, id: 1,
name: 'Test Tournament', name: 'Test Tournament',
allowTies: true, allowTies: true, // This is the current value
targetScore: 5, targetScore: 5,
eventType: 'tournament', eventType: 'tournament',
format: 'round_robin', format: 'round_robin',
@@ -119,11 +119,11 @@ describe('Tournament Update API', () => {
updatedAt: new Date(), updatedAt: new Date(),
} as any)); } as any));
// Mock successful update // Mock successful update (allowTies should remain unchanged)
eventUpdateMock.mockImplementation(async () => ({ eventUpdateMock.mockImplementation(async () => ({
id: 1, id: 1,
name: 'Test Tournament', name: 'Test Tournament',
allowTies: false, allowTies: true, // Should remain true, not reset to false
targetScore: 5, targetScore: 5,
eventType: 'tournament', eventType: 'tournament',
format: 'round_robin', format: 'round_robin',
@@ -141,7 +141,7 @@ describe('Tournament Update API', () => {
body: JSON.stringify({ body: JSON.stringify({
name: 'Test Tournament', name: 'Test Tournament',
targetScore: 5, targetScore: 5,
// allowTies not provided // allowTies not provided - should NOT be modified
}), }),
}); });
@@ -149,13 +149,12 @@ describe('Tournament Update API', () => {
const response = await PUT(request, { params }); const response = await PUT(request, { params });
expect(response.status).toBe(200); expect(response.status).toBe(200);
expect(prisma.event.update).toHaveBeenCalledWith( // When allowTies is not provided, it should NOT be in the update data
expect.objectContaining({ // (it will keep its existing value in the database)
data: expect.objectContaining({ const updateCall = eventUpdateMock.mock.calls[0][0];
allowTies: false, // Should default to false expect(updateCall.data.allowTies).toBeUndefined();
}), expect(updateCall.data.name).toBe('Test Tournament');
}) expect(updateCall.data.targetScore).toBe(5);
);
}); });
it('should preserve allowTies value when updating other fields', async () => { it('should preserve allowTies value when updating other fields', async () => {
+26 -18
View File
@@ -151,28 +151,36 @@ export async function PUT(request: Request, { params }: RouteParams) {
ownerId, ownerId,
targetScore, targetScore,
allowTies, allowTies,
teamDurability,
partnerRotation,
allowByes,
} = body; } = body;
// Validate required fields // Validate name only if it's being updated
if (!name || typeof name !== 'string' || name.trim().length === 0) { if (body.hasOwnProperty('name')) {
return NextResponse.json( if (!name || typeof name !== 'string' || name.trim().length === 0) {
{ error: "Tournament name is required" }, return NextResponse.json(
{ status: 400 } { error: "Tournament name is required" },
); { status: 400 }
);
}
} }
// Prepare update data // Prepare update data - only include fields that are present in the request
const updateData: Record<string, unknown> = { const updateData: Record<string, unknown> = {};
name: name.trim(),
description: description || null, if (body.hasOwnProperty('name')) updateData.name = name.trim();
eventDate: eventDate ? new Date(eventDate) : null, if (body.hasOwnProperty('description')) updateData.description = description || null;
eventType: eventType || "tournament", if (body.hasOwnProperty('eventDate')) updateData.eventDate = eventDate ? new Date(eventDate) : null;
tournamentType: tournamentType || "individual", if (body.hasOwnProperty('eventType')) updateData.eventType = eventType || "tournament";
format: format || "round_robin", if (body.hasOwnProperty('tournamentType')) updateData.tournamentType = tournamentType || "individual";
maxParticipants: maxParticipants ? parseInt(maxParticipants) : null, if (body.hasOwnProperty('format')) updateData.format = format || "round_robin";
targetScore: targetScore ? parseInt(targetScore) : null, if (body.hasOwnProperty('maxParticipants')) updateData.maxParticipants = maxParticipants ? parseInt(maxParticipants) : null;
allowTies: allowTies ?? false, if (body.hasOwnProperty('targetScore')) updateData.targetScore = targetScore ? parseInt(targetScore) : null;
}; if (body.hasOwnProperty('allowTies')) updateData.allowTies = allowTies ?? false;
if (body.hasOwnProperty('teamDurability')) updateData.teamDurability = teamDurability || "permanent";
if (body.hasOwnProperty('partnerRotation')) updateData.partnerRotation = partnerRotation || "none";
if (body.hasOwnProperty('allowByes')) updateData.allowByes = allowByes ?? true;
// Only allow status updates if they don't conflict with auto-calculation // Only allow status updates if they don't conflict with auto-calculation
if (status) { if (status) {