From de54b26be9c8d31e42ca8266820bf5439587e6fa Mon Sep 17 00:00:00 2001 From: hz <1766264+zeeshaun@users.noreply.github.com> Date: Sun, 31 May 2026 02:11:46 -0500 Subject: [PATCH 1/2] Fix stand-in lineup updates after veto --- .../events/MatchUpdatedLineupsEvent.spec.ts | 134 ++++++++++++++++++ .../events/MatchUpdatedLineupsEvent.ts | 2 +- 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 src/matches/events/MatchUpdatedLineupsEvent.spec.ts diff --git a/src/matches/events/MatchUpdatedLineupsEvent.spec.ts b/src/matches/events/MatchUpdatedLineupsEvent.spec.ts new file mode 100644 index 00000000..6ec2a6da --- /dev/null +++ b/src/matches/events/MatchUpdatedLineupsEvent.spec.ts @@ -0,0 +1,134 @@ +jest.mock("./abstracts/MatchEventProcessor", () => { + return class MatchEventProcessor { + protected data: T; + protected matchId: string; + + constructor( + protected readonly logger: any, + protected readonly hasura: any, + protected readonly matchAssistant: any, + ) {} + + public setData(matchId: string, data: T) { + this.data = data; + this.matchId = matchId.trim(); + } + }; +}); + +import MatchUpdatedLineupsEvent from "./MatchUpdatedLineupsEvent"; + +describe("MatchUpdatedLineupsEvent", () => { + const buildProcessor = (existingSteamIds: string[]) => { + const hasura = { + mutation: jest.fn().mockResolvedValue({}), + }; + const existingPlayers = existingSteamIds.map((steam_id) => ({ steam_id })); + const matchAssistant = { + getMatchLineups: jest.fn().mockResolvedValue({ + options: { + type: "Competitive", + }, + lineup_1_id: "lineup-1", + lineup_2_id: "lineup-2", + lineup_1: { + lineup_players: existingPlayers.slice(0, 5), + }, + lineup_2: { + lineup_players: existingPlayers.slice(5), + }, + lineup_players: existingPlayers, + }), + }; + + const processor = new MatchUpdatedLineupsEvent( + {} as any, + hasura as any, + matchAssistant as any, + {} as any, + {} as any, + ); + + return { processor, hasura, matchAssistant }; + }; + + const lineups = { + lineup_1: [ + { name: "Player 1", captain: true, steam_id: "1" }, + { name: "Player 2", captain: false, steam_id: "2" }, + { name: "Player 3", captain: false, steam_id: "3" }, + { name: "Player 4", captain: false, steam_id: "4" }, + { name: "Player 5", captain: false, steam_id: "5" }, + ], + lineup_2: [ + { name: "Player 6", captain: true, steam_id: "6" }, + { name: "Player 7", captain: false, steam_id: "7" }, + { name: "Player 8", captain: false, steam_id: "8" }, + { name: "Player 9", captain: false, steam_id: "9" }, + { name: "Stand In", captain: false, steam_id: "10" }, + ], + }; + + it("processes a full competitive lineup update with a stand-in", async () => { + const { processor, hasura } = buildProcessor([ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + ]); + + processor.setData("match-1", { lineups }); + + await processor.process(); + + expect(hasura.mutation).toHaveBeenCalledWith( + expect.objectContaining({ + delete_match_lineup_players: expect.any(Object), + }), + ); + expect(hasura.mutation).toHaveBeenCalledWith({ + insert_match_lineup_players: { + __args: { + objects: [ + { + discord_id: "Stand In", + captain: false, + steam_id: "10", + match_lineup_id: "lineup-2", + }, + ], + }, + affected_rows: true, + }, + }); + }); + + it("ignores partial lineup updates", async () => { + const { processor, hasura } = buildProcessor([]); + + processor.setData("match-1", { + lineups: { + lineup_1: lineups.lineup_1, + lineup_2: lineups.lineup_2.slice(0, 4), + }, + }); + + await processor.process(); + + expect(hasura.mutation).not.toHaveBeenCalledWith( + expect.objectContaining({ + delete_match_lineup_players: expect.any(Object), + }), + ); + expect(hasura.mutation).not.toHaveBeenCalledWith( + expect.objectContaining({ + insert_match_lineup_players: expect.any(Object), + }), + ); + }); +}); diff --git a/src/matches/events/MatchUpdatedLineupsEvent.ts b/src/matches/events/MatchUpdatedLineupsEvent.ts index 8b84529b..8ca6a113 100644 --- a/src/matches/events/MatchUpdatedLineupsEvent.ts +++ b/src/matches/events/MatchUpdatedLineupsEvent.ts @@ -61,7 +61,7 @@ export default class MatchUpdatedLineupsEvent extends MatchEventProcessor<{ } } - if (players.length < ExpectedPlayers[match.options.type] * 2) { + if (players.length < ExpectedPlayers[match.options.type]) { return; } From 16b7c9aab970aa8127208a5ae6a431c6b83cb911 Mon Sep 17 00:00:00 2001 From: hz <1766264+zeeshaun@users.noreply.github.com> Date: Tue, 2 Jun 2026 16:58:09 -0500 Subject: [PATCH 2/2] Remove lineup completeness guard --- .../events/MatchUpdatedLineupsEvent.spec.ts | 23 +++++++++++-------- .../events/MatchUpdatedLineupsEvent.ts | 5 ---- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/matches/events/MatchUpdatedLineupsEvent.spec.ts b/src/matches/events/MatchUpdatedLineupsEvent.spec.ts index 6ec2a6da..51f800d8 100644 --- a/src/matches/events/MatchUpdatedLineupsEvent.spec.ts +++ b/src/matches/events/MatchUpdatedLineupsEvent.spec.ts @@ -108,27 +108,30 @@ describe("MatchUpdatedLineupsEvent", () => { }); }); - it("ignores partial lineup updates", async () => { - const { processor, hasura } = buildProcessor([]); + it("processes partial lineup updates", async () => { + const { processor, hasura } = buildProcessor(["1", "2", "3"]); processor.setData("match-1", { lineups: { - lineup_1: lineups.lineup_1, - lineup_2: lineups.lineup_2.slice(0, 4), + lineup_1: lineups.lineup_1.slice(0, 2), + lineup_2: [], }, }); await processor.process(); - expect(hasura.mutation).not.toHaveBeenCalledWith( + expect(hasura.mutation).toHaveBeenCalledWith( expect.objectContaining({ delete_match_lineup_players: expect.any(Object), }), ); - expect(hasura.mutation).not.toHaveBeenCalledWith( - expect.objectContaining({ - insert_match_lineup_players: expect.any(Object), - }), - ); + expect(hasura.mutation).toHaveBeenCalledWith({ + insert_match_lineup_players: { + __args: { + objects: [], + }, + affected_rows: true, + }, + }); }); }); diff --git a/src/matches/events/MatchUpdatedLineupsEvent.ts b/src/matches/events/MatchUpdatedLineupsEvent.ts index 8ca6a113..7f7d012f 100644 --- a/src/matches/events/MatchUpdatedLineupsEvent.ts +++ b/src/matches/events/MatchUpdatedLineupsEvent.ts @@ -1,4 +1,3 @@ -import { ExpectedPlayers } from "src/discord-bot/enums/ExpectedPlayers"; import MatchEventProcessor from "./abstracts/MatchEventProcessor"; export default class MatchUpdatedLineupsEvent extends MatchEventProcessor<{ @@ -61,10 +60,6 @@ export default class MatchUpdatedLineupsEvent extends MatchEventProcessor<{ } } - if (players.length < ExpectedPlayers[match.options.type]) { - return; - } - // remove anyone not in the match await this.hasura.mutation({ delete_match_lineup_players: {