diff --git a/app/services/__tests__/qualifying-points-discord.server.test.ts b/app/services/__tests__/qualifying-points-discord.server.test.ts index d65d823..d258f92 100644 --- a/app/services/__tests__/qualifying-points-discord.server.test.ts +++ b/app/services/__tests__/qualifying-points-discord.server.test.ts @@ -373,6 +373,29 @@ describe("notifyQualifyingPointsUpdate", () => { expect(call.eliminated).toEqual([]); }); + it("short-circuits before the QP-total/season lookups when nothing drafted is involved", async () => { + // A knockout occurred but the player isn't drafted in any league. The notifier + // is invoked (the sync fires it on any elimination) but must not run the rest + // of its query battery just to send nothing. + const db = makeDb({ + draftPicks: { findMany: vi.fn().mockResolvedValue([]) }, + }); + + await notifyQualifyingPointsUpdate( + SPORTS_SEASON_ID, + SCORING_EVENT_ID, + db as never, + new Set(["p-nonexistent"]), + new Set(["p-undrafted"]) + ); + + expect(db.query.draftPicks.findMany).toHaveBeenCalledOnce(); + expect(db.query.seasonParticipantQualifyingTotals.findMany).not.toHaveBeenCalled(); + expect(db.query.seasons.findMany).not.toHaveBeenCalled(); + expect(db.query.seasonParticipants.findMany).not.toHaveBeenCalled(); + expect(sendQualifyingPointsUpdateNotification).not.toHaveBeenCalled(); + }); + it("fires when a drafted player is knocked out even though no QP changed", async () => { const db = makeDbWithEliminated(); diff --git a/app/services/qualifying-points-discord.server.ts b/app/services/qualifying-points-discord.server.ts index 8584c0f..5601b57 100644 --- a/app/services/qualifying-points-discord.server.ts +++ b/app/services/qualifying-points-discord.server.ts @@ -59,6 +59,34 @@ export async function notifyQualifyingPointsUpdate( if (qpEarnedById.size === 0 && eliminatedIds.size === 0) return; + const seasonIds = seasonSports.map((s) => s.seasonId); + + // Batch-fetch all draft picks for all leagues at once, grouped by season. + // Fetched before the remaining lookups so we can short-circuit when nothing + // that happened this sync was actually drafted: during a Grand Slam's early + // rounds most eliminated players are undrafted, and the notifier now fires on + // every sync that has any elimination — no point running the rest of the + // queries just to send nothing. + const allPicks = await db.query.draftPicks.findMany({ + where: inArray(schema.draftPicks.seasonId, seasonIds), + with: { team: true }, + }); + const picksBySeasonId = new Map(); + const draftedParticipantIds = new Set(); + for (const pick of allPicks) { + draftedParticipantIds.add(pick.participantId); + const bucket = picksBySeasonId.get(pick.seasonId); + if (bucket) { + bucket.push(pick); + } else { + picksBySeasonId.set(pick.seasonId, [pick]); + } + } + + const hasDraftedQP = [...qpEarnedById.keys()].some((id) => draftedParticipantIds.has(id)); + const hasDraftedEliminated = [...eliminatedIds].some((id) => draftedParticipantIds.has(id)); + if (!hasDraftedQP && !hasDraftedEliminated) return; + // Current running QP totals for the season const qpTotals = await db.query.seasonParticipantQualifyingTotals.findMany({ where: eq(schema.seasonParticipantQualifyingTotals.sportsSeasonId, sportsSeasonId), @@ -67,8 +95,6 @@ export async function notifyQualifyingPointsUpdate( qpTotals.map((t) => [t.participantId, parseFloat(t.totalQualifyingPoints)]) ); - const seasonIds = seasonSports.map((s) => s.seasonId); - // Batch-fetch all season + league metadata in one query const seasons = await db.query.seasons.findMany({ where: inArray(schema.seasons.id, seasonIds), @@ -76,21 +102,6 @@ export async function notifyQualifyingPointsUpdate( }); const seasonMap = new Map(seasons.map((s) => [s.id, s])); - // Batch-fetch all draft picks for all leagues at once, grouped by season - const allPicks = await db.query.draftPicks.findMany({ - where: inArray(schema.draftPicks.seasonId, seasonIds), - with: { team: true }, - }); - const picksBySeasonId = new Map(); - for (const pick of allPicks) { - const bucket = picksBySeasonId.get(pick.seasonId); - if (bucket) { - bucket.push(pick); - } else { - picksBySeasonId.set(pick.seasonId, [pick]); - } - } - // Batch-fetch participant display names once (same participants across all leagues) const allParticipantIds = [...new Set([...qpEarnedById.keys(), ...eliminatedIds])]; const participants = await db.query.seasonParticipants.findMany({