Slip- und Beschuldigungslogik überarbeitet
All checks were successful
Build / build (push) Successful in 2m19s
All checks were successful
Build / build (push) Successful in 2m19s
Slips werden jetzt 30 Sekunden vorgehalten und anschließend automatisch abgeschlossen; direkte Spieler-Beschuldigungen, Win-Status, SignalR-Events, MAUI-UI, EF-Mapping/Migrationen sowie Tests wurden an die neue Spiellogik angepasst.
This commit is contained in:
@@ -244,7 +244,7 @@ public class GameServiceTests
|
||||
// ---------- SubmitSlipAsync ----------
|
||||
|
||||
[Fact]
|
||||
public async Task SubmitSlipAsync_MarksCardAsUsed()
|
||||
public async Task SubmitSlipAsync_HoldsCardFor30Seconds_ThenCompletes()
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
var host = TestDbHelper.SeedUser(db);
|
||||
@@ -260,7 +260,15 @@ public class GameServiceTests
|
||||
|
||||
Assert.True(result);
|
||||
using var verify = _factory.CreateDbContext();
|
||||
Assert.True(verify.PlayerCards.Single(c => c.Id == card.Id).IsUsed);
|
||||
var verifyCard = verify.PlayerCards.Single(c => c.Id == card.Id);
|
||||
Assert.NotNull(verifyCard.SlippedAt);
|
||||
Assert.False(verifyCard.IsUsed); // Für 30 Sekunden vorgehalten!
|
||||
|
||||
// CompleteSlipAsync vollendet nach Ablauf der Zeit
|
||||
var completed = await _sut.CompleteSlipAsync(game.Id, card.Id, force: true);
|
||||
Assert.True(completed);
|
||||
using var verify2 = _factory.CreateDbContext();
|
||||
Assert.True(verify2.PlayerCards.Single(c => c.Id == card.Id).IsUsed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -307,11 +315,11 @@ public class GameServiceTests
|
||||
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 };
|
||||
var card = new PlayerCard { PlayerId = otherPlayer.Id, PhraseId = phrase.Id, GameRoundId = round.Id, IsUsed = false, SlippedAt = DateTime.UtcNow };
|
||||
db.PlayerCards.Add(card);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var challenge = await _sut.CreateChallengeAsync(game.Id, hostPlayer.Id, card.Id);
|
||||
var challenge = await _sut.CreateChallengeAsync(game.Id, hostPlayer.Id, otherPlayer.Id);
|
||||
|
||||
Assert.Equal(round.Id, challenge.GameRoundId);
|
||||
Assert.Equal(hostPlayer.Id, challenge.ChallengingPlayerId);
|
||||
@@ -321,9 +329,37 @@ public class GameServiceTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateChallengeAsync_Throws_WhenTargetCardNotFound()
|
||||
public async Task CreateChallengeAsync_ChallengesPlayerDirectly()
|
||||
{
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.CreateChallengeAsync(1, 1, 999));
|
||||
using var db = _factory.CreateDbContext();
|
||||
var host = TestDbHelper.SeedUser(db, "host", "host@t.local");
|
||||
var other = TestDbHelper.SeedUser(db, "other", "other@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);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var challenge = await _sut.CreateChallengeAsync(game.Id, hostPlayer.Id, otherPlayer.Id);
|
||||
|
||||
Assert.Equal(round.Id, challenge.GameRoundId);
|
||||
Assert.Equal(hostPlayer.Id, challenge.ChallengingPlayerId);
|
||||
Assert.Equal(otherPlayer.Id, challenge.TargetPlayerId);
|
||||
Assert.Equal(ChallengeStatus.Pending, challenge.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateChallengeAsync_Throws_WhenTargetPlayerNotFound()
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
var host = TestDbHelper.SeedUser(db, "host", "host@t.local");
|
||||
var (game, hostPlayer) = TestDbHelper.SeedGame(db, host);
|
||||
var round = new GameRound { GameId = game.Id, RoundNumber = 1, Status = RoundStatus.Active };
|
||||
db.GameRounds.Add(round);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.CreateChallengeAsync(game.Id, hostPlayer.Id, 999));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -331,7 +367,10 @@ public class GameServiceTests
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
var host = TestDbHelper.SeedUser(db);
|
||||
var other = TestDbHelper.SeedUser(db, "other", "other@t.local");
|
||||
var (game, hostPlayer) = TestDbHelper.SeedGame(db, host);
|
||||
var otherPlayer = new Player { UserId = other.Id, GameId = game.Id };
|
||||
db.Players.Add(otherPlayer);
|
||||
var phrase = TestDbHelper.SeedPhrases(db, 1).Single();
|
||||
var round = new GameRound { GameId = game.Id, RoundNumber = 1, Status = RoundStatus.Waiting };
|
||||
db.GameRounds.Add(round);
|
||||
@@ -339,7 +378,7 @@ public class GameServiceTests
|
||||
db.PlayerCards.Add(card);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.CreateChallengeAsync(game.Id, hostPlayer.Id, card.Id));
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.CreateChallengeAsync(game.Id, hostPlayer.Id, otherPlayer.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -361,7 +400,7 @@ public class GameServiceTests
|
||||
|
||||
// outsider ist nicht in diesem Spiel und versucht eine Challenge anzulegen
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.CreateChallengeAsync(game.Id, hostPlayer.Id + 1000, card.Id));
|
||||
() => _sut.CreateChallengeAsync(game.Id, hostPlayer.Id + 1000, otherPlayer.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -382,15 +421,15 @@ public class GameServiceTests
|
||||
db.PlayerCards.Add(cardB);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
// hostPlayerA gehört zu gameA, versucht aber eine Karte aus gameB zu challengen
|
||||
// hostPlayerA gehört zu gameA, versucht aber einen Spieler aus gameB zu challengen
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.CreateChallengeAsync(gameB.Id, hostPlayerA.Id, cardB.Id));
|
||||
() => _sut.CreateChallengeAsync(gameB.Id, hostPlayerA.Id, otherPlayerB.Id));
|
||||
}
|
||||
|
||||
// ---------- ResolveChallengeAsync ----------
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveChallengeAsync_Approved_TransfersCardToChallenger()
|
||||
public async Task ResolveChallengeAsync_Approved_DiscardsCardAndDrawsTwoNewCards()
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
var host = TestDbHelper.SeedUser(db, "host", "host@t.local");
|
||||
@@ -400,8 +439,8 @@ public class GameServiceTests
|
||||
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 };
|
||||
var phrases = TestDbHelper.SeedPhrases(db, 5);
|
||||
var card = new PlayerCard { PlayerId = otherPlayer.Id, PhraseId = phrases[0].Id, GameRoundId = round.Id, IsUsed = false, SlippedAt = DateTime.UtcNow };
|
||||
db.PlayerCards.Add(card);
|
||||
var challenge = new SlipChallenge
|
||||
{
|
||||
@@ -418,13 +457,19 @@ public class GameServiceTests
|
||||
|
||||
Assert.Equal(ChallengeStatus.Approved, result.Status);
|
||||
Assert.NotNull(result.ResolvedAt);
|
||||
// Berechtigte Beschuldigung: Karte geht an den Beschuldiger
|
||||
using var verify = _factory.CreateDbContext();
|
||||
Assert.Equal(hostPlayer.Id, verify.PlayerCards.Single(c => c.Id == card.Id).PlayerId);
|
||||
// Vorgehaltene Karte zählt nicht und wird abgelegt (IsUsed = true)
|
||||
Assert.True(verify.PlayerCards.Single(c => c.Id == card.Id).IsUsed);
|
||||
// Beschuldigter erhält 2 neue Karten (insgesamt 3 Karten für otherPlayer)
|
||||
Assert.Equal(3, verify.PlayerCards.Count(c => c.PlayerId == otherPlayer.Id));
|
||||
// 2 davon aktiv
|
||||
Assert.Equal(2, verify.PlayerCards.Count(c => c.PlayerId == otherPlayer.Id && !c.IsUsed));
|
||||
// Challenger erhält +1 Score
|
||||
Assert.Equal(1, verify.Players.Single(p => p.Id == hostPlayer.Id).Score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveChallengeAsync_Rejected_KeepsCardWithTargetPlayer()
|
||||
public async Task ResolveChallengeAsync_Rejected_WithdrawsChallengeWithoutPenalty()
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
var host = TestDbHelper.SeedUser(db, "host", "host@t.local");
|
||||
@@ -434,8 +479,8 @@ public class GameServiceTests
|
||||
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 };
|
||||
var phrases = TestDbHelper.SeedPhrases(db, 3);
|
||||
var card = new PlayerCard { PlayerId = otherPlayer.Id, PhraseId = phrases[0].Id, GameRoundId = round.Id, IsUsed = false, SlippedAt = DateTime.UtcNow };
|
||||
db.PlayerCards.Add(card);
|
||||
var challenge = new SlipChallenge
|
||||
{
|
||||
@@ -452,9 +497,11 @@ public class GameServiceTests
|
||||
|
||||
Assert.Equal(ChallengeStatus.Rejected, result.Status);
|
||||
Assert.NotNull(result.ResolvedAt);
|
||||
// Falsche Beschuldigung: Karte bleibt beim Beschuldigten
|
||||
using var verify = _factory.CreateDbContext();
|
||||
Assert.Equal(otherPlayer.Id, verify.PlayerCards.Single(c => c.Id == card.Id).PlayerId);
|
||||
// Keine Karten abgelegt oder neu gezogen
|
||||
Assert.Single(verify.PlayerCards.Where(c => c.PlayerId == otherPlayer.Id));
|
||||
Assert.False(verify.PlayerCards.Single(c => c.Id == card.Id).IsUsed);
|
||||
Assert.Equal(0, verify.Players.Single(p => p.Id == hostPlayer.Id).Score);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -463,6 +510,33 @@ public class GameServiceTests
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.ResolveChallengeAsync(999, true));
|
||||
}
|
||||
|
||||
// ---------- Win Condition (Point 3) ----------
|
||||
|
||||
[Fact]
|
||||
public async Task CompleteSlipAsync_PlayerWins_WhenNoCardsRemain()
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
var host = TestDbHelper.SeedUser(db);
|
||||
var other = TestDbHelper.SeedUser(db, "other", "other@t.local");
|
||||
var (game, hostPlayer) = TestDbHelper.SeedGame(db, host, GameStatus.InProgress);
|
||||
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 = hostPlayer.Id, PhraseId = phrase.Id, GameRoundId = round.Id, SlippedAt = DateTime.UtcNow.AddSeconds(-31) };
|
||||
db.PlayerCards.Add(card);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var completed = await _sut.CompleteSlipAsync(game.Id, card.Id);
|
||||
|
||||
Assert.True(completed);
|
||||
using var verify = _factory.CreateDbContext();
|
||||
var verifyGame = verify.Games.Single(g => g.Id == game.Id);
|
||||
Assert.Equal(GameStatus.Completed, verifyGame.Status);
|
||||
Assert.Equal(hostPlayer.Id, verifyGame.WinnerPlayerId);
|
||||
}
|
||||
|
||||
// ---------- GetGameStateAsync ----------
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user