From 7953b8ddd0fb3c9fec9d0802c639e5a4531b0217 Mon Sep 17 00:00:00 2001 From: Tim Krampitz Date: Tue, 1 Sep 2026 20:50:46 +0200 Subject: [PATCH] =?UTF-8?q?P1:=20Challenger-Zugeh=C3=B6rigkeit=20zum=20Spi?= =?UTF-8?q?el=20validieren;=20P2:=20MAUI-Workload=20im=20CI=20installieren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GameService.CreateChallengeAsync prüft jetzt, dass challengingPlayerId zum gameId gehört - Zwei neue Tests für Cross-Game-/Fremdspieler-Challenges - CI: dotnet workload install maui vor dem Restore, damit MAUI auf frischen Runnern baut --- .github/workflows/build.yml | 3 ++ SlipItIn.Server.Tests/GameServiceTests.cs | 45 +++++++++++++++++++++++ SlipItIn.Server/Services/GameService.cs | 5 +++ 3 files changed, 53 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af0c041..22c489a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,9 @@ jobs: with: dotnet-version: "10.0.x" + - name: Install MAUI workload + run: dotnet workload install maui + - name: Restore run: dotnet restore SlipItIn.slnx diff --git a/SlipItIn.Server.Tests/GameServiceTests.cs b/SlipItIn.Server.Tests/GameServiceTests.cs index e2d2ad7..8b643b3 100644 --- a/SlipItIn.Server.Tests/GameServiceTests.cs +++ b/SlipItIn.Server.Tests/GameServiceTests.cs @@ -313,6 +313,51 @@ public class GameServiceTests await Assert.ThrowsAsync(() => _sut.CreateChallengeAsync(game.Id, hostPlayer.Id, card.Id)); } + [Fact] + public async Task CreateChallengeAsync_Throws_WhenChallengerNotInGame() + { + using var db = _factory.CreateDbContext(); + var host = TestDbHelper.SeedUser(db, "host", "host@t.local"); + var other = TestDbHelper.SeedUser(db, "other", "other@t.local"); + var outsider = TestDbHelper.SeedUser(db, "outsider", "outsider@t.local"); + var (game, hostPlayer) = TestDbHelper.SeedGame(db, host); + var otherPlayer = new Player { UserId = other.Id, GameId = game.Id }; + db.Players.Add(otherPlayer); + var round = new GameRound { GameId = game.Id, RoundNumber = 1, Status = RoundStatus.Active }; + db.GameRounds.Add(round); + var phrase = TestDbHelper.SeedPhrases(db, 1).Single(); + var card = new PlayerCard { PlayerId = otherPlayer.Id, PhraseId = phrase.Id, GameRoundId = round.Id, IsUsed = true }; + db.PlayerCards.Add(card); + await db.SaveChangesAsync(); + + // outsider ist nicht in diesem Spiel und versucht eine Challenge anzulegen + await Assert.ThrowsAsync( + () => _sut.CreateChallengeAsync(game.Id, hostPlayer.Id + 1000, card.Id)); + } + + [Fact] + public async Task CreateChallengeAsync_Throws_WhenChallengerFromOtherGame() + { + using var db = _factory.CreateDbContext(); + var hostA = TestDbHelper.SeedUser(db, "hostA", "hostA@t.local"); + var hostB = TestDbHelper.SeedUser(db, "hostB", "hostB@t.local"); + var other = TestDbHelper.SeedUser(db, "other", "other@t.local"); + var (gameA, hostPlayerA) = TestDbHelper.SeedGame(db, hostA); + var (gameB, _) = TestDbHelper.SeedGame(db, hostB); + var otherPlayerB = new Player { UserId = other.Id, GameId = gameB.Id }; + db.Players.Add(otherPlayerB); + var roundB = new GameRound { GameId = gameB.Id, RoundNumber = 1, Status = RoundStatus.Active }; + db.GameRounds.Add(roundB); + var phrase = TestDbHelper.SeedPhrases(db, 1).Single(); + var cardB = new PlayerCard { PlayerId = otherPlayerB.Id, PhraseId = phrase.Id, GameRoundId = roundB.Id, IsUsed = true }; + db.PlayerCards.Add(cardB); + await db.SaveChangesAsync(); + + // hostPlayerA gehört zu gameA, versucht aber eine Karte aus gameB zu challengen + await Assert.ThrowsAsync( + () => _sut.CreateChallengeAsync(gameB.Id, hostPlayerA.Id, cardB.Id)); + } + // ---------- ResolveChallengeAsync ---------- [Fact] diff --git a/SlipItIn.Server/Services/GameService.cs b/SlipItIn.Server/Services/GameService.cs index 8b12fe4..d78b86b 100644 --- a/SlipItIn.Server/Services/GameService.cs +++ b/SlipItIn.Server/Services/GameService.cs @@ -176,6 +176,11 @@ public class GameService : IGameService .Where(gr => gr.GameId == gameId && gr.Status == RoundStatus.Active) .FirstOrDefaultAsync() ?? throw new InvalidOperationException("No active round found"); + var challengerBelongsToGame = await context.Players + .AnyAsync(p => p.Id == challengingPlayerId && p.GameId == gameId); + if (!challengerBelongsToGame) + throw new UnauthorizedAccessException("Challenger does not belong to this game"); + var targetCard = await context.PlayerCards .FirstOrDefaultAsync(pc => pc.Id == targetCardId) ?? throw new InvalidOperationException("Target card not found");