Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/matches/game-streamer/game-streamer.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,44 @@ describe("GameStreamerService", () => {
expect(setOf().status).toBe("launching_cs2");
});

it("keeps the row on playing when the pod's live report lands second", async () => {
hasura.query.mockResolvedValueOnce({
match_demo_sessions_by_pk: {
status: "playing",
status_history: [
{ status: "connecting_to_game", at: "2026-01-01T00:00:00.000Z" },
{ status: "playing", at: "2026-01-01T00:01:00.000Z" },
],
},
});
hasura.mutation.mockResolvedValueOnce({});

await service.reportDemoStatus("session-1", { status: "live" });

const set = setOf();
expect(set.status).toBeUndefined();
expect(set.error_message).toBeUndefined();
expect((set.status_history as any[]).map((e: any) => e.status)).toEqual([
"connecting_to_game",
"playing",
"live",
]);
});

it("still lets a rebooted pod move the row off playing", async () => {
hasura.query.mockResolvedValueOnce({
match_demo_sessions_by_pk: {
status: "playing",
status_history: [{ status: "playing", at: "2026-01-01" }],
},
});
hasura.mutation.mockResolvedValueOnce({});

await service.reportDemoStatus("session-1", { status: "booting" });

expect(setOf().status).toBe("booting");
});

it("appends after an event instead of rewriting it", async () => {
// The row still says downloading_cs2 while the newest history entry is
// the demo_ready event — coalescing must compare against the entry.
Expand Down
8 changes: 7 additions & 1 deletion src/matches/game-streamer/game-streamer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,13 @@ export class GameStreamerService {
// An `event` is a one-shot milestone (demo_ready) raised by a worker
// running alongside the main boot — it marks a stage complete in the
// history without yanking the row off whatever setup-steam is doing.
const isEvent = GameStreamerService.isTruthyFlag(body.event);
//
// A `live` landing after `playing` gets the same treatment. The pod sends
// `live` through a 2s-polled daemon but `playing` straight from the
// spec-server, exactly once, so the two can arrive swapped — and applying
// the late `live` strands the viewer on the boot screen for good.
const isLateLive = current.status === "playing" && status === "live";
const isEvent = GameStreamerService.isTruthyFlag(body.event) || isLateLive;
const statusChanged = current.status !== status;
const set: Record<string, unknown> = {
status_history: nextHistory,
Expand Down
Loading