From 3231e9e9f9cc8b171911f6b5af2ef5959ccc32dd Mon Sep 17 00:00:00 2001 From: lukaskett Date: Sat, 25 Apr 2026 23:14:36 +0200 Subject: [PATCH 1/2] fix: added check if the new value is really changed, used crateMany for protocol insert (instead of await for loop), #124 --- .../server/src/modules/event/event.service.ts | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/apps/server/src/modules/event/event.service.ts b/apps/server/src/modules/event/event.service.ts index 9bde4b9..106f630 100644 --- a/apps/server/src/modules/event/event.service.ts +++ b/apps/server/src/modules/event/event.service.ts @@ -55,22 +55,24 @@ export const changeCompetitorStatus = async (eventId, competitorId, origin, stat throw new DatabaseError('Error updating competitor'); } - // Add record to protocol - try { - await prisma.protocol.create({ - data: { - eventId: eventId, - competitorId: competitorId, - origin: origin, - type: 'status_change', - previousValue: dbResponseCompetitor.status, - newValue: competitorStatus, - authorId: userId, - }, - }); - } catch (err) { - console.error('Failed to update competitor:', err); - throw new DatabaseError('Error creating protocol record'); + // Add record to protocol only when status actually changed + if (dbResponseCompetitor.status !== competitorStatus) { + try { + await prisma.protocol.create({ + data: { + eventId: eventId, + competitorId: competitorId, + origin: origin, + type: 'status_change', + previousValue: dbResponseCompetitor.status, + newValue: competitorStatus, + authorId: userId, + }, + }); + } catch (err) { + console.error('Failed to update competitor:', err); + throw new DatabaseError('Error creating protocol record'); + } } // Select the current competitor from the database @@ -263,17 +265,22 @@ export const updateCompetitor = async (eventId, competitorId, origin, updateData externalId: 'external_id_change', }; - // Iterate over keys in updateData + // Iterate over keys in updateData, log only actual changes Object.keys(updateData).forEach(key => { if (keyToTypeMap[key]) { const previousValue = dbResponseCompetitor[key]; const nextValue = updateData[key]; - changes.push({ - type: keyToTypeMap[key], - previousValue: - previousValue === null || previousValue === undefined ? null : previousValue.toString(), - newValue: nextValue === null || nextValue === undefined ? 'null' : nextValue.toString(), - }); + const prevStr = + previousValue === null || previousValue === undefined ? null : previousValue.toString(); + const nextStr = + nextValue === null || nextValue === undefined ? null : nextValue.toString(); + if (prevStr !== nextStr) { + changes.push({ + type: keyToTypeMap[key], + previousValue: prevStr, + newValue: nextStr ?? 'null', + }); + } } }); @@ -303,11 +310,11 @@ export const updateCompetitor = async (eventId, competitorId, origin, updateData throw new DatabaseError('Error updating competitor'); } - // Add record to protocol - try { - for (const change of changes) { - await prisma.protocol.create({ - data: { + // Add records to protocol in a single batch insert + if (changes.length > 0) { + try { + await prisma.protocol.createMany({ + data: changes.map(change => ({ eventId: eventId, competitorId: parseInt(competitorId), origin: origin, @@ -315,12 +322,12 @@ export const updateCompetitor = async (eventId, competitorId, origin, updateData previousValue: change.previousValue, newValue: change.newValue, authorId: userId, - }, + })), }); + } catch (err) { + console.error('Failed to update competitor:', err); + throw new DatabaseError('Error creating protocol record'); } - } catch (err) { - console.error('Failed to update competitor:', err); - throw new DatabaseError('Error creating protocol record'); } // Select the current competitor from the database From bf21a1fec1f6f2077342c36f37c740f5b021e50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20K=C5=99ivda?= Date: Tue, 28 Apr 2026 08:21:51 +0200 Subject: [PATCH 2/2] fix: preserve late start protocol entries --- .../event/__tests__/event.service.test.ts | 75 +++++++++++++++++++ .../server/src/modules/event/event.service.ts | 33 ++++++-- 2 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 apps/server/src/modules/event/__tests__/event.service.test.ts diff --git a/apps/server/src/modules/event/__tests__/event.service.test.ts b/apps/server/src/modules/event/__tests__/event.service.test.ts new file mode 100644 index 0000000..58d3f12 --- /dev/null +++ b/apps/server/src/modules/event/__tests__/event.service.test.ts @@ -0,0 +1,75 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +const prismaMock = vi.hoisted(() => ({ + competitor: { + findFirst: vi.fn(), + update: vi.fn(), + findUnique: vi.fn(), + }, + protocol: { + createMany: vi.fn(), + }, +})); + +const subscriptionMocks = vi.hoisted(() => ({ + publishUpdatedCompetitor: vi.fn(), + publishUpdatedCompetitors: vi.fn(), +})); + +vi.mock('../../../utils/context.js', () => ({ + default: prismaMock, +})); + +vi.mock('../../../utils/subscriptionUtils.js', () => subscriptionMocks); + +import { changeCompetitorStatus } from '../event.service.js'; + +describe('event.service changeCompetitorStatus', () => { + beforeEach(() => { + prismaMock.competitor.findFirst.mockResolvedValue({ + id: 7, + classId: 3, + status: 'Active', + lateStart: false, + card: null, + }); + prismaMock.competitor.update.mockResolvedValue({}); + prismaMock.competitor.findUnique.mockResolvedValue({ + id: 7, + classId: 3, + status: 'Active', + lateStart: true, + class: {}, + team: null, + }); + prismaMock.protocol.createMany.mockResolvedValue({ count: 1 }); + subscriptionMocks.publishUpdatedCompetitor.mockResolvedValue(undefined); + subscriptionMocks.publishUpdatedCompetitors.mockResolvedValue(undefined); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('logs late start changes even when the persisted status remains Active', async () => { + await changeCompetitorStatus('event-1', 7, 'START', 'LateStart', 11); + + expect(prismaMock.competitor.update).toHaveBeenCalledWith({ + where: { id: 7 }, + data: { status: 'Active', lateStart: true }, + }); + expect(prismaMock.protocol.createMany).toHaveBeenCalledWith({ + data: [ + { + eventId: 'event-1', + competitorId: 7, + origin: 'START', + type: 'late_start_change', + previousValue: 'false', + newValue: 'true', + authorId: 11, + }, + ], + }); + }); +}); diff --git a/apps/server/src/modules/event/event.service.ts b/apps/server/src/modules/event/event.service.ts index 106f630..23f1ffb 100644 --- a/apps/server/src/modules/event/event.service.ts +++ b/apps/server/src/modules/event/event.service.ts @@ -17,6 +17,7 @@ export const changeCompetitorStatus = async (eventId, competitorId, origin, stat id: true, classId: true, status: true, + lateStart: true, card: true, }, }); @@ -55,19 +56,37 @@ export const changeCompetitorStatus = async (eventId, competitorId, origin, stat throw new DatabaseError('Error updating competitor'); } - // Add record to protocol only when status actually changed + const changes = []; + if (dbResponseCompetitor.status !== competitorStatus) { + changes.push({ + type: 'status_change', + previousValue: dbResponseCompetitor.status, + newValue: competitorStatus, + }); + } + + if (dbResponseCompetitor.lateStart !== lateStart) { + changes.push({ + type: 'late_start_change', + previousValue: String(dbResponseCompetitor.lateStart), + newValue: String(lateStart), + }); + } + + // Add records to protocol only when the persisted values actually changed. + if (changes.length > 0) { try { - await prisma.protocol.create({ - data: { + await prisma.protocol.createMany({ + data: changes.map(change => ({ eventId: eventId, competitorId: competitorId, origin: origin, - type: 'status_change', - previousValue: dbResponseCompetitor.status, - newValue: competitorStatus, + type: change.type, + previousValue: change.previousValue, + newValue: change.newValue, authorId: userId, - }, + })), }); } catch (err) { console.error('Failed to update competitor:', err);