diff options
-rw-r--r-- | Timeline.Tests/Helpers/TestClock.cs | 25 | ||||
-rw-r--r-- | Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs | 6 | ||||
-rw-r--r-- | Timeline.Tests/TokenUnitTest.cs | 58 | ||||
-rw-r--r-- | Timeline/Controllers/TokenController.cs | 2 | ||||
-rw-r--r-- | Timeline/Services/UserService.cs | 23 |
5 files changed, 92 insertions, 22 deletions
diff --git a/Timeline.Tests/Helpers/TestClock.cs b/Timeline.Tests/Helpers/TestClock.cs new file mode 100644 index 00000000..fc200be9 --- /dev/null +++ b/Timeline.Tests/Helpers/TestClock.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using System; +using Timeline.Services; + +namespace Timeline.Tests.Helpers +{ + public class TestClock : IClock + { + DateTime? MockCurrentTime { get; set; } = null; + + public DateTime GetCurrentTime() + { + return MockCurrentTime.GetValueOrDefault(DateTime.Now); + } + } + + public static class TestClockWebApplicationFactoryExtensions + { + public static TestClock GetTestClock<T>(this WebApplicationFactory<T> factory) where T : class + { + return factory.Server.Host.Services.GetRequiredService<IClock>() as TestClock; + } + } +} diff --git a/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs b/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs index a7616b41..aa005ba3 100644 --- a/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs +++ b/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs @@ -1,9 +1,11 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Timeline.Models; +using Timeline.Services; using Xunit.Abstractions; namespace Timeline.Tests.Helpers @@ -46,6 +48,10 @@ namespace Timeline.Tests.Helpers db.Users.AddRange(TestMockUsers.MockUsers); db.SaveChanges(); } + }) + .ConfigureTestServices(services => + { + services.AddSingleton<IClock, TestClock>(); }); }); } diff --git a/Timeline.Tests/TokenUnitTest.cs b/Timeline.Tests/TokenUnitTest.cs index 27c2ed32..d7df8797 100644 --- a/Timeline.Tests/TokenUnitTest.cs +++ b/Timeline.Tests/TokenUnitTest.cs @@ -1,10 +1,14 @@ using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using System.Linq; using System.Net; using System.Net.Http; using Timeline.Controllers; +using Timeline.Entities; using Timeline.Entities.Http; +using Timeline.Models; +using Timeline.Services; using Timeline.Tests.Helpers; using Timeline.Tests.Helpers.Authentication; using Xunit; @@ -80,23 +84,63 @@ namespace Timeline.Tests { var response = await client.PostAsJsonAsync(VerifyTokenUrl, new VerifyTokenRequest { Token = "bad token hahaha" }); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + var body = await response.ReadBodyAsJson<CommonResponse>(); + Assert.Equal(TokenController.ErrorCodes.Verify_BadToken, body.Code); } } [Fact] - public async void VerifyTokenTest_GoodToken() + public async void VerifyTokenTest_BadVersion_AND_UserNotExist() { using (var client = _factory.CreateDefaultClient()) { - var createTokenResult = await client.CreateUserTokenAsync("admin", "admin"); + using (var scope = _factory.Server.Host.Services.CreateScope()) // UserService is scoped. + { + // create a user for test + var userService = scope.ServiceProvider.GetRequiredService<IUserService>(); + + const string username = "verifytokentest0"; + const string password = "12345678"; + + await userService.PutUser(username, password, false); + + // create a token + var token = (await client.CreateUserTokenAsync(username, password)).Token; + // increase version + await userService.PatchUser(username, null, null); + + // test against bad version + var response = await client.PostAsJsonAsync(VerifyTokenUrl, new VerifyTokenRequest { Token = token }); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + var body = await response.ReadBodyAsJson<CommonResponse>(); + Assert.Equal(TokenController.ErrorCodes.Verify_BadVersion, body.Code); + + // create another token + var token2 = (await client.CreateUserTokenAsync(username, password)).Token; + + // delete user + await userService.DeleteUser(username); + + // test against user not exist + var response2 = await client.PostAsJsonAsync(VerifyTokenUrl, new VerifyTokenRequest { Token = token }); + Assert.Equal(HttpStatusCode.BadRequest, response2.StatusCode); + var body2 = await response2.ReadBodyAsJson<CommonResponse>(); + Assert.Equal(TokenController.ErrorCodes.Verify_UserNotExist, body2.Code); + } + } + } + + [Fact] + public async void VerifyTokenTest_Success() + { + using (var client = _factory.CreateDefaultClient()) + { + var createTokenResult = await client.CreateUserTokenAsync("admin", "admin"); var response = await client.PostAsJsonAsync(VerifyTokenUrl, new VerifyTokenRequest { Token = createTokenResult.Token }); Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - var result = JsonConvert.DeserializeObject<VerifyTokenResponse>(await response.Content.ReadAsStringAsync()); - Assert.NotNull(result.User); - Assert.Equal(createTokenResult.User.Username, result.User.Username); - Assert.Equal(createTokenResult.User.Administrator, result.User.Administrator); + var body = JsonConvert.DeserializeObject<VerifyTokenResponse>(await response.Content.ReadAsStringAsync()); + Assert.Equal(TestMockUsers.MockUserInfos.Where(u => u.Username == "user").Single(), body.User, UserInfoComparers.EqualityComparer); } } } diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs index 66cf3dad..21f87ded 100644 --- a/Timeline/Controllers/TokenController.cs +++ b/Timeline/Controllers/TokenController.cs @@ -119,7 +119,7 @@ namespace Timeline.Controllers } catch (BadTokenVersionException e) { - var code = ErrorCodes.Verify_BadToken; + var code = ErrorCodes.Verify_BadVersion; _logger.LogInformation(LoggingEventIds.VerifyFailed, e, "Attemp to verify a bad token because version is old. Code: {} Token: {}.", code, request.Token); return BadRequest(new CommonResponse(code, "The token is expired. Try recreate a token.")); } diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs index 328dbff0..65ac98d3 100644 --- a/Timeline/Services/UserService.cs +++ b/Timeline/Services/UserService.cs @@ -19,7 +19,7 @@ namespace Timeline.Services [Serializable] public class UserNotExistException : Exception { - public UserNotExistException(): base("The user does not exist.") { } + public UserNotExistException() : base("The user does not exist.") { } public UserNotExistException(string message) : base(message) { } public UserNotExistException(string message, Exception inner) : base(message, inner) { } protected UserNotExistException( @@ -30,7 +30,7 @@ namespace Timeline.Services [Serializable] public class BadPasswordException : Exception { - public BadPasswordException(): base("Password is wrong.") { } + public BadPasswordException() : base("Password is wrong.") { } public BadPasswordException(string message) : base(message) { } public BadPasswordException(string message, Exception inner) : base(message, inner) { } protected BadPasswordException( @@ -42,7 +42,7 @@ namespace Timeline.Services [Serializable] public class BadTokenVersionException : Exception { - public BadTokenVersionException(): base("Token version is expired.") { } + public BadTokenVersionException() : base("Token version is expired.") { } public BadTokenVersionException(string message) : base(message) { } public BadTokenVersionException(string message, Exception inner) : base(message, inner) { } protected BadTokenVersionException( @@ -105,6 +105,8 @@ namespace Timeline.Services /// <summary> /// Partially modify a user of given username. + /// + /// Note that whether actually modified or not, Version of the user will always increase. /// </summary> /// <param name="username">Username of the user to modify. Can't be null.</param> /// <param name="password">New password. Null if not modify.</param> @@ -309,27 +311,20 @@ namespace Timeline.Services if (user == null) throw new UserNotExistException(); - bool modified = false; - if (password != null) { - modified = true; user.EncryptedPassword = _passwordService.HashPassword(password); } if (administrator != null) { - modified = true; user.RoleString = IsAdminToRoleString(administrator.Value); } - if (modified) - { - user.Version += 1; - await _databaseContext.SaveChangesAsync(); - //clear cache - RemoveCache(user.Id); - } + user.Version += 1; + await _databaseContext.SaveChangesAsync(); + //clear cache + RemoveCache(user.Id); } public async Task DeleteUser(string username) |