using System; using System.Net; using System.Threading.Tasks; using GermanApp.Application.DTOs.Auth; using GermanApp.Application.Interfaces; using GermanApp.Domain.Entities; using GermanApp.Presentation.Controllers; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; namespace GermanApp.Tests.Integration.Controllers; /// /// Integration tests for AuthController. /// Tests controller behavior with mocked services. /// [TestClass] public class AuthControllerTests { private Mock? _mockAuthService; private AuthController? _controller; [TestInitialize] public void TestInitialize() { _mockAuthService = new Mock(); _controller = new AuthController(_mockAuthService.Object); } [TestCleanup] public void TestCleanup() { _controller = null; _mockAuthService = null; } // ==================== REGISTER ENDPOINT TESTS ==================== [TestMethod] [TestCategory("AuthController")] [TestCategory("Register")] public async Task Register_WithValidData_ReturnsOkWithToken() { // Arrange var registerDto = new RegisterDto { Username = "testuser", Email = "test@example.com", Password = "TestPassword123!" }; var expectedResponse = new AuthResponse { UserId = 1, Username = "testuser", Email = "test@example.com", Token = "test-token", RefreshToken = "test-refresh-token", ExpiresAt = DateTime.UtcNow.AddHours(24) }; _mockAuthService!.Setup(s => s.RegisterAsync(It.IsAny())) .ReturnsAsync(expectedResponse); // Act var result = await _controller!.Register(registerDto); // Assert Assert.IsInstanceOfType(result, typeof(OkObjectResult)); var okResult = result as OkObjectResult; Assert.IsNotNull(okResult); Assert.AreEqual(expectedResponse, okResult.Value); } [TestMethod] [TestCategory("AuthController")] [TestCategory("Register")] public async Task Register_WithDuplicateUsername_ReturnsBadRequest() { // Arrange var registerDto = new RegisterDto { Username = "duplicate_user", Email = "test@example.com", Password = "TestPassword123!" }; _mockAuthService!.Setup(s => s.RegisterAsync(It.IsAny())) .ThrowsAsync(new InvalidOperationException("Username already taken")); // Act var result = await _controller!.Register(registerDto); // Assert Assert.IsInstanceOfType(result, typeof(BadRequestObjectResult)); var badRequestResult = result as BadRequestObjectResult; Assert.IsNotNull(badRequestResult); Assert.AreEqual("Username already taken", badRequestResult.Value); } [TestMethod] [TestCategory("AuthController")] [TestCategory("Register")] public async Task Register_WithDuplicateEmail_ReturnsBadRequest() { // Arrange var registerDto = new RegisterDto { Username = "testuser", Email = "duplicate@example.com", Password = "TestPassword123!" }; _mockAuthService!.Setup(s => s.RegisterAsync(It.IsAny())) .ThrowsAsync(new InvalidOperationException("Email already in use")); // Act var result = await _controller!.Register(registerDto); // Assert Assert.IsInstanceOfType(result, typeof(BadRequestObjectResult)); var badRequestResult = result as BadRequestObjectResult; Assert.IsNotNull(badRequestResult); Assert.AreEqual("Email already in use", badRequestResult.Value); } // ==================== LOGIN ENDPOINT TESTS ==================== [TestMethod] [TestCategory("AuthController")] [TestCategory("Login")] public async Task Login_WithValidCredentials_ReturnsOkWithToken() { // Arrange var loginDto = new LoginDto { Email = "test@example.com", Password = "TestPassword123!" }; var expectedResponse = new AuthResponse { UserId = 1, Username = "testuser", Email = "test@example.com", Token = "test-token", RefreshToken = "test-refresh-token", ExpiresAt = DateTime.UtcNow.AddHours(24) }; _mockAuthService!.Setup(s => s.LoginAsync(It.IsAny())) .ReturnsAsync(expectedResponse); // Act var result = await _controller!.Login(loginDto); // Assert Assert.IsInstanceOfType(result, typeof(OkObjectResult)); var okResult = result as OkObjectResult; Assert.IsNotNull(okResult); Assert.AreEqual(expectedResponse, okResult.Value); } [TestMethod] [TestCategory("AuthController")] [TestCategory("Login")] public async Task Login_WithInvalidEmail_ReturnsUnauthorized() { // Arrange var loginDto = new LoginDto { Email = "nonexistent@example.com", Password = "TestPassword123!" }; _mockAuthService!.Setup(s => s.LoginAsync(It.IsAny())) .ThrowsAsync(new UnauthorizedAccessException("Invalid email or password")); // Act var result = await _controller!.Login(loginDto); // Assert Assert.IsInstanceOfType(result, typeof(UnauthorizedObjectResult)); } [TestMethod] [TestCategory("AuthController")] [TestCategory("Login")] public async Task Login_WithInvalidPassword_ReturnsUnauthorized() { // Arrange var loginDto = new LoginDto { Email = "test@example.com", Password = "wrong_password" }; _mockAuthService!.Setup(s => s.LoginAsync(It.IsAny())) .ThrowsAsync(new UnauthorizedAccessException("Invalid email or password")); // Act var result = await _controller!.Login(loginDto); // Assert Assert.IsInstanceOfType(result, typeof(UnauthorizedObjectResult)); } // ==================== GET CURRENT USER ENDPOINT TESTS ==================== [TestMethod] [TestCategory("AuthController")] [TestCategory("Me")] public async Task GetCurrentUser_WithValidUser_ReturnsUserInfo() { // Arrange var user = User.Create("testuser", "test@example.com", "hashed-password"); _mockAuthService!.Setup(s => s.GetCurrentUserAsync(1)) .ReturnsAsync(user); // Arrange - set up controller context with user claim _controller!.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new System.Security.Claims.ClaimsPrincipal(new System.Security.Claims.ClaimsIdentity(new[] { new System.Security.Claims.Claim("nameid", "1"), new System.Security.Claims.Claim("name", "testuser"), new System.Security.Claims.Claim("email", "test@example.com"), new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Role, "User") })) } }; // Act var result = await _controller.GetCurrentUser(); // Assert Assert.IsInstanceOfType(result, typeof(OkObjectResult)); var okResult = result as OkObjectResult; Assert.IsNotNull(okResult); var response = okResult.Value as AuthResponse; Assert.IsNotNull(response); Assert.AreEqual(user.Id, response.UserId); Assert.AreEqual(user.Username, response.Username); Assert.AreEqual(user.Email, response.Email); } [TestMethod] [TestCategory("AuthController")] [TestCategory("Me")] public async Task GetCurrentUser_WithoutAuthentication_ReturnsUnauthorized() { // Arrange - no user in context _controller!.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }; // Act var result = await _controller.GetCurrentUser(); // Assert Assert.IsInstanceOfType(result, typeof(UnauthorizedResult)); } // ==================== REFRESH TOKEN ENDPOINT TESTS ==================== [TestMethod] [TestCategory("AuthController")] [TestCategory("Refresh")] public async Task Refresh_WithValidRefreshToken_ReturnsNewTokens() { // Arrange var validRefreshToken = "valid-refresh-token"; var expectedResponse = new RefreshTokenResponse { Token = "new-access-token", RefreshToken = "new-refresh-token", ExpiresAt = DateTime.UtcNow.AddHours(24) }; _mockAuthService!.Setup(s => s.RefreshTokenAsync(validRefreshToken)) .ReturnsAsync(expectedResponse); // Act var result = await _controller!.Refresh(validRefreshToken); // Assert Assert.IsInstanceOfType(result, typeof(OkObjectResult)); var okResult = result as OkObjectResult; Assert.IsNotNull(okResult); Assert.AreEqual(expectedResponse, okResult.Value); } [TestMethod] [TestCategory("AuthController")] [TestCategory("Refresh")] public async Task Refresh_WithInvalidRefreshToken_ReturnsUnauthorized() { // Arrange var invalidRefreshToken = "invalid-refresh-token"; _mockAuthService!.Setup(s => s.RefreshTokenAsync(invalidRefreshToken)) .ThrowsAsync(new UnauthorizedAccessException("Invalid refresh token")); // Act var result = await _controller!.Refresh(invalidRefreshToken); // Assert Assert.IsInstanceOfType(result, typeof(UnauthorizedObjectResult)); } // ==================== REVOKE REFRESH TOKEN ENDPOINT TESTS ==================== [TestMethod] [TestCategory("AuthController")] [TestCategory("RevokeRefresh")] public async Task RevokeRefreshToken_WithValidToken_ReturnsOk() { // Arrange var validRefreshToken = "valid-refresh-token"; // No exception means success _mockAuthService!.Setup(s => s.RevokeRefreshTokenAsync(validRefreshToken)) .Returns(Task.CompletedTask); // Arrange - set up authorized context _controller!.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new System.Security.Claims.ClaimsPrincipal(new System.Security.Claims.ClaimsIdentity(new[] { new System.Security.Claims.Claim("nameid", "1"), new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Role, "User") })) } }; // Act var result = await _controller.RevokeRefreshToken(validRefreshToken); // Assert Assert.IsInstanceOfType(result, typeof(OkObjectResult)); var okResult = result as OkObjectResult; Assert.IsNotNull(okResult); // Check that the response contains the expected message dynamic responseValue = okResult.Value!; Assert.AreEqual("Refresh token revoked successfully", (string)responseValue.message); } [TestMethod] [TestCategory("AuthController")] [TestCategory("RevokeRefresh")] public async Task RevokeRefreshToken_WithoutAuthentication_ReturnsUnauthorized() { // Note: When testing controllers directly (not through HTTP pipeline), // the [Authorize] attribute is not automatically enforced. // This test would pass in a full integration test with WebApplicationFactory. // For now, we test that the controller correctly uses the service. // Arrange - no user in context, service throws exception var invalidRefreshToken = "any-token"; _mockAuthService!.Setup(s => s.RevokeRefreshTokenAsync(invalidRefreshToken)) .ThrowsAsync(new UnauthorizedAccessException("Refresh token not found")); _controller!.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }; // Act var result = await _controller.RevokeRefreshToken(invalidRefreshToken); // Assert - Without [Authorize] enforcement in direct controller tests, // we expect the service exception to propagate as UnauthorizedObjectResult Assert.IsInstanceOfType(result, typeof(UnauthorizedObjectResult)); } [TestMethod] [TestCategory("AuthController")] [TestCategory("RevokeRefresh")] public async Task RevokeRefreshToken_WithInvalidToken_ReturnsUnauthorized() { // Arrange var invalidRefreshToken = "invalid-refresh-token"; _mockAuthService!.Setup(s => s.RevokeRefreshTokenAsync(invalidRefreshToken)) .ThrowsAsync(new UnauthorizedAccessException("Refresh token not found")); // Arrange - set up authorized context _controller!.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new System.Security.Claims.ClaimsPrincipal(new System.Security.Claims.ClaimsIdentity(new[] { new System.Security.Claims.Claim("nameid", "1"), new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Role, "User") })) } }; // Act var result = await _controller.RevokeRefreshToken(invalidRefreshToken); // Assert Assert.IsInstanceOfType(result, typeof(UnauthorizedObjectResult)); } }