Fix and() bug and add Swiss loop safety guard

- cs2-major-stage.ts: markCs2StageEliminations and setCs2FinalPlacements
  were using JS && instead of Drizzle and(), causing WHERE to filter only
  by participantId (not scoringEventId), which would update rows across
  all events instead of just the target event
- cs-major-simulator.ts: add break guard in simulateSwiss while loop to
  prevent infinite loop if pairGroups returns no pairs

https://claude.ai/code/session_019w21Nkf5TvTZHH6oVHaQXR
This commit is contained in:
Claude 2026-04-05 20:05:02 +00:00
parent 79201820a9
commit a516dedb76
No known key found for this signature in database
2 changed files with 11 additions and 7 deletions

View file

@ -14,7 +14,7 @@
import { database } from "~/database/context"; import { database } from "~/database/context";
import { cs2MajorStageResults, participants } from "~/database/schema"; import { cs2MajorStageResults, participants } from "~/database/schema";
import { eq, inArray, sql } from "drizzle-orm"; import { eq, and, inArray, sql } from "drizzle-orm";
export interface Cs2StageResult { export interface Cs2StageResult {
id: string; id: string;
@ -157,10 +157,10 @@ export async function markCs2StageEliminations(
stageEliminatedWins: winsAtElimination, stageEliminatedWins: winsAtElimination,
updatedAt: now, updatedAt: now,
}) })
.where( .where(and(
eq(cs2MajorStageResults.scoringEventId, scoringEventId) && eq(cs2MajorStageResults.scoringEventId, scoringEventId),
eq(cs2MajorStageResults.participantId, participantId) eq(cs2MajorStageResults.participantId, participantId)
) ))
) )
); );
} }
@ -184,10 +184,10 @@ export async function setCs2FinalPlacements(
db db
.update(cs2MajorStageResults) .update(cs2MajorStageResults)
.set({ finalPlacement: placements[participantId], updatedAt: now }) .set({ finalPlacement: placements[participantId], updatedAt: now })
.where( .where(and(
eq(cs2MajorStageResults.scoringEventId, scoringEventId) && eq(cs2MajorStageResults.scoringEventId, scoringEventId),
eq(cs2MajorStageResults.participantId, participantId) eq(cs2MajorStageResults.participantId, participantId)
) ))
) )
); );
} }

View file

@ -193,6 +193,10 @@ export function simulateSwiss(teams: TeamWithElo[], bo3: boolean): SwissResult {
// (simplified: skip teams that can't be paired — they sit out this round) // (simplified: skip teams that can't be paired — they sit out this round)
const pairs = pairGroups(groups); const pairs = pairGroups(groups);
// Safety: if no pairs can be formed (shouldn't happen with even team counts
// but guards against an infinite loop if an odd active count somehow occurs)
if (pairs.length === 0) break;
for (const [t1, t2] of pairs) { for (const [t1, t2] of pairs) {
const e1 = eloMap.get(t1.id) ?? FALLBACK_ELO; const e1 = eloMap.get(t1.id) ?? FALLBACK_ELO;
const e2 = eloMap.get(t2.id) ?? FALLBACK_ELO; const e2 = eloMap.get(t2.id) ?? FALLBACK_ELO;