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");