Testprojekt hinzugefügt & umfassende Server-Tests
Neues Testprojekt SlipItIn.Server.Tests erstellt und in die Solution eingebunden. Unit- und Integrationstests für AuthController, DbSeeder, GameHub und GameService implementiert. TestDbHelper für InMemory-DbContexts und Testdaten hinzugefügt. Tests prüfen Erfolgs- und Fehlerfälle inkl. Validierungen, Fehlerbehandlung und DB-Zustände.
This commit is contained in:
203
SlipItIn.Server.Tests/AuthControllerTests.cs
Normal file
203
SlipItIn.Server.Tests/AuthControllerTests.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using SlipItIn.Server.Controllers;
|
||||
using SlipItIn.Server.Data;
|
||||
using SlipItIn.Shared.DTOs;
|
||||
using SlipItIn.Shared.Models;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace SlipItIn.Server.Tests;
|
||||
|
||||
public class AuthControllerTests
|
||||
{
|
||||
private readonly IDbContextFactory<SlipItInDbContext> _factory = TestDbHelper.CreateFactory();
|
||||
private readonly AuthController _sut;
|
||||
|
||||
public AuthControllerTests()
|
||||
{
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["Jwt:Key"] = "TestKeyThatIsAtLeast32CharactersLong!!",
|
||||
["Jwt:Issuer"] = "TestIssuer",
|
||||
["Jwt:Audience"] = "TestAudience",
|
||||
["Jwt:ExpirationMinutes"] = "60"
|
||||
})
|
||||
.Build();
|
||||
_sut = new AuthController(_factory, config);
|
||||
}
|
||||
|
||||
// ---------- Register ----------
|
||||
|
||||
[Fact]
|
||||
public async Task Register_ReturnsOk_WithTokenAndUserData()
|
||||
{
|
||||
var result = await _sut.Register(new RegisterRequestDto
|
||||
{
|
||||
Username = " Alice ",
|
||||
Email = " ALICE@Test.Local ",
|
||||
Password = "Secret123!"
|
||||
});
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var dto = Assert.IsType<AuthResponseDto>(ok.Value);
|
||||
Assert.Equal("Alice", dto.Username); // getrimmt
|
||||
Assert.Equal("alice@test.local", dto.Email); // getrimmt + lowercase
|
||||
Assert.True(dto.UserId > 0);
|
||||
Assert.False(string.IsNullOrWhiteSpace(dto.Token));
|
||||
Assert.True(dto.ExpiresAtUtc > DateTime.UtcNow);
|
||||
|
||||
// Token enthält die erwarteten Claims und ist valide lesbar
|
||||
var token = new JwtSecurityTokenHandler().ReadJwtToken(dto.Token);
|
||||
Assert.Equal("TestIssuer", token.Issuer);
|
||||
Assert.Contains("TestAudience", token.Audiences);
|
||||
Assert.Equal(dto.UserId.ToString(), token.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "a@b.c", "pw")]
|
||||
[InlineData("user", "", "pw")]
|
||||
[InlineData("user", "a@b.c", "")]
|
||||
[InlineData(" ", "a@b.c", "pw")]
|
||||
public async Task Register_ReturnsBadRequest_WhenFieldsMissing(string username, string email, string password)
|
||||
{
|
||||
var result = await _sut.Register(new RegisterRequestDto { Username = username, Email = email, Password = password });
|
||||
|
||||
Assert.IsType<BadRequestObjectResult>(result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Register_ReturnsConflict_WhenEmailExists_CaseInsensitive()
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
TestDbHelper.SeedUser(db, "existing", "alice@test.local");
|
||||
|
||||
var result = await _sut.Register(new RegisterRequestDto
|
||||
{
|
||||
Username = "newuser",
|
||||
Email = "ALICE@TEST.LOCAL",
|
||||
Password = "Secret123!"
|
||||
});
|
||||
|
||||
Assert.IsType<ConflictObjectResult>(result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Register_ReturnsConflict_WhenUsernameExists_CaseInsensitive()
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
TestDbHelper.SeedUser(db, "alice", "other@test.local");
|
||||
|
||||
var result = await _sut.Register(new RegisterRequestDto
|
||||
{
|
||||
Username = "ALICE",
|
||||
Email = "new@test.local",
|
||||
Password = "Secret123!"
|
||||
});
|
||||
|
||||
Assert.IsType<ConflictObjectResult>(result.Result);
|
||||
}
|
||||
|
||||
// ---------- Login ----------
|
||||
|
||||
[Fact]
|
||||
public async Task Login_ReturnsOk_WithValidCredentials()
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
db.Users.Add(new User
|
||||
{
|
||||
Username = "bob",
|
||||
Email = "bob@test.local",
|
||||
PasswordHash = BCrypt.Net.BCrypt.HashPassword("Secret123!")
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var result = await _sut.Login(new LoginRequestDto { Email = " BOB@test.local ", Password = "Secret123!" });
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var dto = Assert.IsType<AuthResponseDto>(ok.Value);
|
||||
Assert.Equal("bob", dto.Username);
|
||||
Assert.False(string.IsNullOrWhiteSpace(dto.Token));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "pw")]
|
||||
[InlineData("a@b.c", "")]
|
||||
public async Task Login_ReturnsBadRequest_WhenFieldsMissing(string email, string password)
|
||||
{
|
||||
var result = await _sut.Login(new LoginRequestDto { Email = email, Password = password });
|
||||
|
||||
Assert.IsType<BadRequestObjectResult>(result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Login_ReturnsUnauthorized_WhenUserNotFound()
|
||||
{
|
||||
var result = await _sut.Login(new LoginRequestDto { Email = "ghost@test.local", Password = "Secret123!" });
|
||||
|
||||
Assert.IsType<UnauthorizedObjectResult>(result.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Login_ReturnsUnauthorized_WhenPasswordWrong()
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
db.Users.Add(new User
|
||||
{
|
||||
Username = "bob",
|
||||
Email = "bob@test.local",
|
||||
PasswordHash = BCrypt.Net.BCrypt.HashPassword("CorrectPassword")
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var result = await _sut.Login(new LoginRequestDto { Email = "bob@test.local", Password = "WrongPassword" });
|
||||
|
||||
Assert.IsType<UnauthorizedObjectResult>(result.Result);
|
||||
}
|
||||
|
||||
// ---------- Me ----------
|
||||
|
||||
[Fact]
|
||||
public void Me_ReturnsUserData_FromClaims()
|
||||
{
|
||||
var identity = new ClaimsIdentity([
|
||||
new Claim(ClaimTypes.NameIdentifier, "42"),
|
||||
new Claim(ClaimTypes.Name, "alice"),
|
||||
new Claim(ClaimTypes.Email, "alice@test.local")
|
||||
], "TestAuth");
|
||||
_sut.ControllerContext = new ControllerContext
|
||||
{
|
||||
HttpContext = new Microsoft.AspNetCore.Http.DefaultHttpContext
|
||||
{
|
||||
User = new ClaimsPrincipal(identity)
|
||||
}
|
||||
};
|
||||
|
||||
var result = _sut.Me();
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var value = ok.Value!;
|
||||
Assert.Equal(42, (int)value.GetType().GetProperty("UserId")!.GetValue(value)!);
|
||||
Assert.Equal("alice", (string)value.GetType().GetProperty("Username")!.GetValue(value)!);
|
||||
Assert.Equal("alice@test.local", (string)value.GetType().GetProperty("Email")!.GetValue(value)!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Me_ReturnsUnauthorized_WhenNameIdentifierClaimMissing()
|
||||
{
|
||||
var identity = new ClaimsIdentity([new Claim(ClaimTypes.Name, "alice")], "TestAuth");
|
||||
_sut.ControllerContext = new ControllerContext
|
||||
{
|
||||
HttpContext = new Microsoft.AspNetCore.Http.DefaultHttpContext
|
||||
{
|
||||
User = new ClaimsPrincipal(identity)
|
||||
}
|
||||
};
|
||||
|
||||
var result = _sut.Me();
|
||||
|
||||
Assert.IsType<UnauthorizedResult>(result.Result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user