aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author杨宇千 <crupest@outlook.com>2019-10-19 17:33:38 +0800
committer杨宇千 <crupest@outlook.com>2019-10-19 17:33:38 +0800
commit2984abc9aa0429380459e5b5b6fda2d20058041b (patch)
treef75fde126094e8310dd9e2588bb9b9e8baa79a3b
parentdef8e8dd78812c019a0d6e8e5a3e2de4e82ae3e4 (diff)
downloadtimeline-2984abc9aa0429380459e5b5b6fda2d20058041b.tar.gz
timeline-2984abc9aa0429380459e5b5b6fda2d20058041b.tar.bz2
timeline-2984abc9aa0429380459e5b5b6fda2d20058041b.zip
...
-rw-r--r--Timeline.Tests/Controllers/TokenControllerTest.cs11
-rw-r--r--Timeline.Tests/Helpers/AssertionResponseExtensions.cs95
-rw-r--r--Timeline.Tests/Helpers/Authentication/AuthenticationExtensions.cs2
-rw-r--r--Timeline.Tests/Helpers/HttpClientExtensions.cs4
-rw-r--r--Timeline.Tests/Helpers/InvalidModelTestHelpers.cs22
-rw-r--r--Timeline.Tests/IntegratedTests/TokenUnitTest.cs183
-rw-r--r--Timeline.Tests/IntegratedTests/UserAvatarTests.cs46
-rw-r--r--Timeline.Tests/IntegratedTests/UserDetailTest.cs28
-rw-r--r--Timeline.Tests/IntegratedTests/UserUnitTest.cs288
-rw-r--r--Timeline/GlobalSuppressions.cs9
-rw-r--r--Timeline/Models/Http/Common.cs2
-rw-r--r--Timeline/Program.cs2
-rw-r--r--Timeline/Services/UserService.cs61
-rw-r--r--Timeline/Startup.cs5
-rw-r--r--Timeline/Timeline.csproj7
15 files changed, 392 insertions, 373 deletions
diff --git a/Timeline.Tests/Controllers/TokenControllerTest.cs b/Timeline.Tests/Controllers/TokenControllerTest.cs
index fff7c020..60ba75dc 100644
--- a/Timeline.Tests/Controllers/TokenControllerTest.cs
+++ b/Timeline.Tests/Controllers/TokenControllerTest.cs
@@ -79,6 +79,17 @@ namespace Timeline.Tests.Controllers
.Which.Code.Should().Be(Create.BadCredential);
}
+ [Fact]
+ public async Task Verify_Ok()
+ {
+ const string token = "aaaaaaaaaaaaaa";
+ _mockUserService.Setup(s => s.VerifyToken(token)).ReturnsAsync(MockUser.User.Info);
+ var action = await _controller.Verify(new VerifyTokenRequest { Token = token });
+ action.Should().BeAssignableTo<OkObjectResult>()
+ .Which.Value.Should().BeAssignableTo<VerifyTokenResponse>()
+ .Which.User.Should().BeEquivalentTo(MockUser.User.Info);
+ }
+
// TODO! Verify unit tests
}
}
diff --git a/Timeline.Tests/Helpers/AssertionResponseExtensions.cs b/Timeline.Tests/Helpers/AssertionResponseExtensions.cs
index c7ebdb7a..5ce025ee 100644
--- a/Timeline.Tests/Helpers/AssertionResponseExtensions.cs
+++ b/Timeline.Tests/Helpers/AssertionResponseExtensions.cs
@@ -6,6 +6,7 @@ using Newtonsoft.Json;
using System;
using System.Net;
using System.Net.Http;
+using System.Text;
using Timeline.Models.Http;
namespace Timeline.Tests.Helpers
@@ -23,8 +24,25 @@ namespace Timeline.Tests.Helpers
string padding = new string('\t', context.Depth);
var res = (HttpResponseMessage)value;
- var body = res.Content.ReadAsStringAsync().Result;
- return $"{newline}{padding} Status Code: {res.StatusCode} ; Body: {body.Substring(0, Math.Min(body.Length, 20))} ;";
+
+ var builder = new StringBuilder();
+ builder.Append($"{newline}{padding} Status Code: {res.StatusCode} ; Body: ");
+
+ try
+ {
+ var body = res.Content.ReadAsStringAsync().Result;
+ if (body.Length > 40)
+ {
+ body = body[0..40] + " ...";
+ }
+ builder.Append(body);
+ }
+ catch (AggregateException)
+ {
+ builder.Append("NOT A STRING.");
+ }
+
+ return builder.ToString();
}
}
@@ -43,15 +61,20 @@ namespace Timeline.Tests.Helpers
protected override string Identifier => "HttpResponseMessage";
+ public AndConstraint<HttpResponseMessage> HaveStatusCode(int expected, string because = "", params object[] becauseArgs)
+ {
+ return HaveStatusCode((HttpStatusCode)expected, because, becauseArgs);
+ }
+
public AndConstraint<HttpResponseMessage> HaveStatusCode(HttpStatusCode expected, string because = "", params object[] becauseArgs)
{
Execute.Assertion.BecauseOf(because, becauseArgs)
.ForCondition(Subject.StatusCode == expected)
- .FailWith("Expected status code of {context:HttpResponseMessage} to be {0}{reason}, but found {1}.\nResponse is {2}.", expected, Subject.StatusCode, Subject);
+ .FailWith("Expected status code of {context:HttpResponseMessage} to be {0}{reason}, but found {1}.", expected, Subject.StatusCode);
return new AndConstraint<HttpResponseMessage>(Subject);
}
- public AndWhichConstraint<HttpResponseMessage, T> HaveBodyAsJson<T>(string because = "", params object[] becauseArgs)
+ public AndWhichConstraint<HttpResponseMessage, T> HaveJsonBody<T>(string because = "", params object[] becauseArgs)
{
var a = Execute.Assertion.BecauseOf(because, becauseArgs);
string body;
@@ -61,7 +84,7 @@ namespace Timeline.Tests.Helpers
}
catch (Exception e)
{
- a.FailWith("Failed to read response body of {context:HttpResponseMessage}{reason}.\nException is {0}.", e);
+ a.FailWith("Expected response body of {context:HttpResponseMessage} to be json string{reason}, but failed to read it or it was not a string. Exception is {0}.", e);
return new AndWhichConstraint<HttpResponseMessage, T>(Subject, null);
}
@@ -72,7 +95,7 @@ namespace Timeline.Tests.Helpers
}
catch (Exception e)
{
- a.FailWith("Failed to convert response body of {context:HttpResponseMessage} to {0}{reason}.\nResponse is {1}.\nException is {2}.", typeof(T).FullName, Subject, e);
+ a.FailWith("Expected response body of {context:HttpResponseMessage} to be able to convert to {0} instance{reason}, but failed. Exception is {1}.", typeof(T).FullName, e);
return new AndWhichConstraint<HttpResponseMessage, T>(Subject, null);
}
}
@@ -85,64 +108,48 @@ namespace Timeline.Tests.Helpers
return new HttpResponseMessageAssertions(instance);
}
- public static AndConstraint<HttpResponseMessage> HaveStatusCodeOk(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
- {
- return assertions.HaveStatusCode(HttpStatusCode.OK, because, becauseArgs);
- }
-
- public static AndConstraint<HttpResponseMessage> HaveStatusCodeCreated(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
- {
- return assertions.HaveStatusCode(HttpStatusCode.Created, because, becauseArgs);
- }
-
- public static AndConstraint<HttpResponseMessage> HaveStatusCodeBadRequest(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
- {
- return assertions.HaveStatusCode(HttpStatusCode.BadRequest, because, becauseArgs);
- }
-
- public static AndConstraint<HttpResponseMessage> HaveStatusCodeNotFound(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
- {
- return assertions.HaveStatusCode(HttpStatusCode.NotFound, because, becauseArgs);
- }
-
- public static AndWhichConstraint<HttpResponseMessage, CommonResponse> HaveBodyAsCommonResponse(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
- {
- return assertions.HaveBodyAsJson<CommonResponse>(because, becauseArgs);
- }
-
- public static AndWhichConstraint<HttpResponseMessage, CommonDataResponse<T>> HaveBodyAsCommonDataResponse<T>(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
- {
- return assertions.HaveBodyAsJson<CommonDataResponse<T>>(because, becauseArgs);
- }
-
- public static void HaveBodyAsCommonResponseWithCode(this HttpResponseMessageAssertions assertions, int expected, string because = "", params object[] becauseArgs)
+ public static AndWhichConstraint<HttpResponseMessage, CommonResponse> HaveCommonBody(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
{
- assertions.HaveBodyAsCommonResponse(because, becauseArgs).Which.Code.Should().Be(expected, because, becauseArgs);
+ return assertions.HaveJsonBody<CommonResponse>(because, becauseArgs);
}
- public static void HaveBodyAsCommonDataResponseWithCode<T>(this HttpResponseMessageAssertions assertions, int expected, string because = "", params object[] becauseArgs)
+ public static AndWhichConstraint<HttpResponseMessage, CommonDataResponse<TData>> HaveCommonDataBody<TData>(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
{
- assertions.HaveBodyAsCommonDataResponse<T>(because, becauseArgs).Which.Code.Should().Be(expected, because, becauseArgs);
+ return assertions.HaveJsonBody<CommonDataResponse<TData>>(because, becauseArgs);
}
public static void BePutCreate(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
{
- assertions.HaveStatusCodeCreated(because, becauseArgs).And.Should().HaveBodyAsCommonDataResponse<CommonPutResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonPutResponse.Create(), because, becauseArgs);
+ assertions.HaveStatusCode(201, because, becauseArgs)
+ .And.Should().HaveCommonDataBody<CommonPutResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonPutResponse.Create(), because, becauseArgs);
}
public static void BePutModify(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
{
- assertions.HaveStatusCodeOk(because, becauseArgs).And.Should().HaveBodyAsCommonDataResponse<CommonPutResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonPutResponse.Modify(), because, becauseArgs);
+ assertions.HaveStatusCode(200, because, becauseArgs)
+ .And.Should().HaveCommonDataBody<CommonPutResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonPutResponse.Modify(), because, becauseArgs);
}
public static void BeDeleteDelete(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
{
- assertions.HaveStatusCodeOk(because, becauseArgs).And.Should().HaveBodyAsCommonDataResponse<CommonDeleteResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonDeleteResponse.Delete(), because, becauseArgs);
+ assertions.HaveStatusCode(200, because, becauseArgs)
+ .And.Should().HaveCommonDataBody<CommonDeleteResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonDeleteResponse.Delete(), because, becauseArgs);
}
public static void BeDeleteNotExist(this HttpResponseMessageAssertions assertions, string because = "", params object[] becauseArgs)
{
- assertions.HaveStatusCodeOk(because, becauseArgs).And.Should().HaveBodyAsCommonDataResponse<CommonDeleteResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonDeleteResponse.NotExist(), because, becauseArgs);
+ assertions.HaveStatusCode(200, because, becauseArgs)
+ .And.Should().HaveCommonDataBody<CommonDeleteResponse.ResponseData>(because, becauseArgs).Which.Should().BeEquivalentTo(CommonDeleteResponse.NotExist(), because, becauseArgs);
+ }
+
+ public static void BeInvalidModel(this HttpResponseMessageAssertions assertions, string message = null)
+ {
+ message = string.IsNullOrEmpty(message) ? "" : ", " + message;
+ assertions.HaveStatusCode(400, "Invalid Model Error must have 400 status code{0}", message)
+ .And.Should().HaveCommonBody("Invalid Model Error must have CommonResponse body{0}", message)
+ .Which.Code.Should().Be(ErrorCodes.Http.Common.InvalidModel,
+ "Invalid Model Error must have code {0} in body{1}",
+ ErrorCodes.Http.Common.InvalidModel, message);
}
}
}
diff --git a/Timeline.Tests/Helpers/Authentication/AuthenticationExtensions.cs b/Timeline.Tests/Helpers/Authentication/AuthenticationExtensions.cs
index d068a08a..34d7e460 100644
--- a/Timeline.Tests/Helpers/Authentication/AuthenticationExtensions.cs
+++ b/Timeline.Tests/Helpers/Authentication/AuthenticationExtensions.cs
@@ -14,7 +14,7 @@ namespace Timeline.Tests.Helpers.Authentication
public static async Task<CreateTokenResponse> CreateUserTokenAsync(this HttpClient client, string username, string password, int? expireOffset = null)
{
var response = await client.PostAsJsonAsync(CreateTokenUrl, new CreateTokenRequest { Username = username, Password = password, Expire = expireOffset });
- response.Should().HaveStatusCodeOk();
+ response.Should().HaveStatusCode(200);
var result = JsonConvert.DeserializeObject<CreateTokenResponse>(await response.Content.ReadAsStringAsync());
return result;
}
diff --git a/Timeline.Tests/Helpers/HttpClientExtensions.cs b/Timeline.Tests/Helpers/HttpClientExtensions.cs
index b9204fcc..e3beea1d 100644
--- a/Timeline.Tests/Helpers/HttpClientExtensions.cs
+++ b/Timeline.Tests/Helpers/HttpClientExtensions.cs
@@ -1,6 +1,7 @@
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
+using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
@@ -10,7 +11,8 @@ namespace Timeline.Tests.Helpers
{
public static Task<HttpResponseMessage> PatchAsJsonAsync<T>(this HttpClient client, string url, T body)
{
- return client.PatchAsync(url, new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"));
+ return client.PatchAsync(url, new StringContent(
+ JsonConvert.SerializeObject(body), Encoding.UTF8, MediaTypeNames.Application.Json));
}
public static Task<HttpResponseMessage> PutByteArrayAsync(this HttpClient client, string url, byte[] body, string mimeType)
diff --git a/Timeline.Tests/Helpers/InvalidModelTestHelpers.cs b/Timeline.Tests/Helpers/InvalidModelTestHelpers.cs
deleted file mode 100644
index 4a445ca4..00000000
--- a/Timeline.Tests/Helpers/InvalidModelTestHelpers.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Net.Http;
-using System.Threading.Tasks;
-
-namespace Timeline.Tests.Helpers
-{
- public static class InvalidModelTestHelpers
- {
- public static async Task TestPostInvalidModel<T>(HttpClient client, string url, T body)
- {
- var response = await client.PostAsJsonAsync(url, body);
- response.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(ErrorCodes.Http.Common.InvalidModel);
- }
-
- public static async Task TestPutInvalidModel<T>(HttpClient client, string url, T body)
- {
- var response = await client.PutAsJsonAsync(url, body);
- response.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(ErrorCodes.Http.Common.InvalidModel);
- }
- }
-}
diff --git a/Timeline.Tests/IntegratedTests/TokenUnitTest.cs b/Timeline.Tests/IntegratedTests/TokenUnitTest.cs
index 05e2b3e5..6a60a1a3 100644
--- a/Timeline.Tests/IntegratedTests/TokenUnitTest.cs
+++ b/Timeline.Tests/IntegratedTests/TokenUnitTest.cs
@@ -2,6 +2,7 @@ using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using System;
+using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Timeline.Models.Http;
@@ -12,7 +13,7 @@ using Timeline.Tests.Mock.Data;
using Xunit;
using static Timeline.ErrorCodes.Http.Token;
-namespace Timeline.Tests
+namespace Timeline.Tests.IntegratedTests
{
public class TokenUnitTest : IClassFixture<WebApplicationFactory<Startup>>, IDisposable
{
@@ -32,126 +33,118 @@ namespace Timeline.Tests
{
_testApp.Dispose();
}
- [Fact]
- public async Task CreateToken_InvalidModel()
+
+ public static IEnumerable<object[]> CreateToken_InvalidModel_Data()
{
- using var client = _factory.CreateDefaultClient();
- // missing username
- await InvalidModelTestHelpers.TestPostInvalidModel(client, CreateTokenUrl,
- new CreateTokenRequest { Username = null, Password = "user" });
- // missing password
- await InvalidModelTestHelpers.TestPostInvalidModel(client, CreateTokenUrl,
- new CreateTokenRequest { Username = "user", Password = null });
- // bad expire offset
- await InvalidModelTestHelpers.TestPostInvalidModel(client, CreateTokenUrl,
- new CreateTokenRequest
- {
- Username = "user",
- Password = "password",
- Expire = 1000
- });
+ yield return new[] { null, "p", null };
+ yield return new[] { "u", null, null };
+ yield return new object[] { "u", "p", 2000 };
+ yield return new object[] { "u", "p", -1 };
}
- [Fact]
- public async void CreateToken_UserNotExist()
+ [Theory]
+ [MemberData(nameof(CreateToken_InvalidModel_Data))]
+ public async Task CreateToken_InvalidModel(string username, string password, int expire)
{
using var client = _factory.CreateDefaultClient();
- var response = await client.PostAsJsonAsync(CreateTokenUrl,
- new CreateTokenRequest { Username = "usernotexist", Password = "???" });
- response.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(Create.BadCredential);
+ (await client.PostAsJsonAsync(CreateTokenUrl, new CreateTokenRequest
+ {
+ Username = username,
+ Password = password,
+ Expire = expire
+ })).Should().BeInvalidModel();
}
- [Fact]
- public async void CreateToken_BadPassword()
+ public static IEnumerable<object[]> CreateToken_UserCredential_Data()
{
- using (var client = _factory.CreateDefaultClient())
- {
- var response = await client.PostAsJsonAsync(CreateTokenUrl,
- new CreateTokenRequest { Username = MockUser.User.Username, Password = "???" });
- response.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(Create.BadCredential);
- }
+ yield return new[] { "usernotexist", "p" };
+ yield return new[] { MockUser.User.Username, "???" };
+ }
+
+ [Theory]
+ [MemberData(nameof(CreateToken_UserCredential_Data))]
+ public async void CreateToken_UserCredential(string username, string password)
+ {
+ using var client = _factory.CreateDefaultClient();
+ var response = await client.PostAsJsonAsync(CreateTokenUrl,
+ new CreateTokenRequest { Username = username, Password = password });
+ response.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(Create.BadCredential);
}
[Fact]
- public async void CreateToken_Success()
+ public async Task CreateToken_Success()
{
- using (var client = _factory.CreateDefaultClient())
- {
- var response = await client.PostAsJsonAsync(CreateTokenUrl,
- new CreateTokenRequest { Username = MockUser.User.Username, Password = MockUser.User.Password });
- var body = response.Should().HaveStatusCodeOk()
- .And.Should().HaveBodyAsJson<CreateTokenResponse>().Which;
- body.Token.Should().NotBeNullOrWhiteSpace();
- body.User.Should().BeEquivalentTo(MockUser.User.Info);
- }
+ using var client = _factory.CreateDefaultClient();
+ var response = await client.PostAsJsonAsync(CreateTokenUrl,
+ new CreateTokenRequest { Username = MockUser.User.Username, Password = MockUser.User.Password });
+ var body = response.Should().HaveStatusCode(200)
+ .And.Should().HaveJsonBody<CreateTokenResponse>().Which;
+ body.Token.Should().NotBeNullOrWhiteSpace();
+ body.User.Should().BeEquivalentTo(MockUser.User.Info);
}
[Fact]
- public async void VerifyToken_InvalidModel()
+ public async Task VerifyToken_InvalidModel()
{
- using (var client = _factory.CreateDefaultClient())
- {
- // missing token
- await InvalidModelTestHelpers.TestPostInvalidModel(client, VerifyTokenUrl,
- new VerifyTokenRequest { Token = null });
- }
+ using var client = _factory.CreateDefaultClient();
+ (await client.PostAsJsonAsync(VerifyTokenUrl,
+ new VerifyTokenRequest { Token = null })).Should().BeInvalidModel();
}
[Fact]
- public async void VerifyToken_BadToken()
+ public async Task VerifyToken_BadToken()
{
- using (var client = _factory.CreateDefaultClient())
- {
- var response = await client.PostAsJsonAsync(VerifyTokenUrl, new VerifyTokenRequest { Token = "bad token hahaha" });
- response.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(Verify.BadFormat);
- }
+ using var client = _factory.CreateDefaultClient();
+ var response = await client.PostAsJsonAsync(VerifyTokenUrl,
+ new VerifyTokenRequest { Token = "bad token hahaha" });
+ response.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(Verify.BadFormat);
}
[Fact]
- public async void VerifyToken_BadVersion()
+ public async Task VerifyToken_BadVersion()
{
- using (var client = _factory.CreateDefaultClient())
+ using var client = _factory.CreateDefaultClient();
+ var token = (await client.CreateUserTokenAsync(MockUser.User.Username, MockUser.User.Password)).Token;
+
+ using (var scope = _factory.Server.Host.Services.CreateScope()) // UserService is scoped.
{
- var token = (await client.CreateUserTokenAsync(MockUser.User.Username, MockUser.User.Password)).Token;
-
- using (var scope = _factory.Server.Host.Services.CreateScope()) // UserService is scoped.
- {
- // create a user for test
- var userService = scope.ServiceProvider.GetRequiredService<IUserService>();
- await userService.PatchUser(MockUser.User.Username, null, null);
- }
-
- var response = await client.PostAsJsonAsync(VerifyTokenUrl, new VerifyTokenRequest { Token = token });
- response.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(Verify.OldVersion);
+ // create a user for test
+ var userService = scope.ServiceProvider.GetRequiredService<IUserService>();
+ await userService.PatchUser(MockUser.User.Username, null, null);
}
+
+ (await client.PostAsJsonAsync(VerifyTokenUrl,
+ new VerifyTokenRequest { Token = token }))
+ .Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(Verify.OldVersion);
}
[Fact]
- public async void VerifyToken_UserNotExist()
+ public async Task VerifyToken_UserNotExist()
{
- using (var client = _factory.CreateDefaultClient())
+ using var client = _factory.CreateDefaultClient();
+ var token = (await client.CreateUserTokenAsync(MockUser.User.Username, MockUser.User.Password)).Token;
+
+ using (var scope = _factory.Server.Host.Services.CreateScope()) // UserService is scoped.
{
- var token = (await client.CreateUserTokenAsync(MockUser.User.Username, MockUser.User.Password)).Token;
-
- using (var scope = _factory.Server.Host.Services.CreateScope()) // UserService is scoped.
- {
- // create a user for test
- var userService = scope.ServiceProvider.GetRequiredService<IUserService>();
- await userService.DeleteUser(MockUser.User.Username);
- }
-
- var response = await client.PostAsJsonAsync(VerifyTokenUrl, new VerifyTokenRequest { Token = token });
- response.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(Verify.UserNotExist);
+ var userService = scope.ServiceProvider.GetRequiredService<IUserService>();
+ await userService.DeleteUser(MockUser.User.Username);
}
+
+ (await client.PostAsJsonAsync(VerifyTokenUrl,
+ new VerifyTokenRequest { Token = token }))
+ .Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(Verify.UserNotExist);
}
//[Fact]
- //public async void VerifyToken_Expired()
+ //public async Task VerifyToken_Expired()
//{
// using (var client = _factory.CreateDefaultClient())
// {
@@ -169,17 +162,15 @@ namespace Timeline.Tests
//}
[Fact]
- public async void VerifyToken_Success()
+ public async Task VerifyToken_Success()
{
- using (var client = _factory.CreateDefaultClient())
- {
- var createTokenResult = await client.CreateUserTokenAsync(MockUser.User.Username, MockUser.User.Password);
- var response = await client.PostAsJsonAsync(VerifyTokenUrl,
- new VerifyTokenRequest { Token = createTokenResult.Token });
- response.Should().HaveStatusCodeOk()
- .And.Should().HaveBodyAsJson<VerifyTokenResponse>()
- .Which.User.Should().BeEquivalentTo(MockUser.User.Info);
- }
+ using var client = _factory.CreateDefaultClient();
+ var createTokenResult = await client.CreateUserTokenAsync(MockUser.User.Username, MockUser.User.Password);
+ var response = await client.PostAsJsonAsync(VerifyTokenUrl,
+ new VerifyTokenRequest { Token = createTokenResult.Token });
+ response.Should().HaveStatusCode(200)
+ .And.Should().HaveJsonBody<VerifyTokenResponse>()
+ .Which.User.Should().BeEquivalentTo(MockUser.User.Info);
}
}
}
diff --git a/Timeline.Tests/IntegratedTests/UserAvatarTests.cs b/Timeline.Tests/IntegratedTests/UserAvatarTests.cs
index 439e8d9b..ad0e4221 100644
--- a/Timeline.Tests/IntegratedTests/UserAvatarTests.cs
+++ b/Timeline.Tests/IntegratedTests/UserAvatarTests.cs
@@ -50,8 +50,9 @@ namespace Timeline.Tests.IntegratedTests
{
{
var res = await client.GetAsync("users/usernotexist/avatar");
- res.Should().HaveStatusCodeNotFound()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Get_UserNotExist);
+ res.Should().HaveStatusCode(404)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(UserAvatarController.ErrorCodes.Get_UserNotExist);
}
var env = _factory.Server.Host.Services.GetRequiredService<IWebHostEnvironment>();
@@ -60,7 +61,7 @@ namespace Timeline.Tests.IntegratedTests
async Task GetReturnDefault(string username = "user")
{
var res = await client.GetAsync($"users/{username}/avatar");
- res.Should().HaveStatusCodeOk();
+ res.Should().HaveStatusCode(200);
res.Content.Headers.ContentType.MediaType.Should().Be("image/png");
var body = await res.Content.ReadAsByteArrayAsync();
body.Should().Equal(defaultAvatarData);
@@ -69,7 +70,7 @@ namespace Timeline.Tests.IntegratedTests
EntityTagHeaderValue eTag;
{
var res = await client.GetAsync($"users/user/avatar");
- res.Should().HaveStatusCodeOk();
+ res.Should().HaveStatusCode(200);
res.Content.Headers.ContentType.MediaType.Should().Be("image/png");
var body = await res.Content.ReadAsByteArrayAsync();
body.Should().Equal(defaultAvatarData);
@@ -91,7 +92,7 @@ namespace Timeline.Tests.IntegratedTests
request.Headers.TryAddWithoutValidation("If-None-Match", "\"dsdfd");
var res = await client.SendAsync(request);
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(ErrorCodes.Http.Common.Header.BadFormat_IfNonMatch);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(ErrorCodes.Http.Common.Header.BadFormat_IfNonMatch);
}
{
@@ -121,7 +122,7 @@ namespace Timeline.Tests.IntegratedTests
content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
var res = await client.PutAsync("users/user/avatar", content);
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(ErrorCodes.Http.Common.Header.Missing_ContentLength);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(ErrorCodes.Http.Common.Header.Missing_ContentLength);
}
{
@@ -129,7 +130,7 @@ namespace Timeline.Tests.IntegratedTests
content.Headers.ContentLength = 1;
var res = await client.PutAsync("users/user/avatar", content);
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(ErrorCodes.Http.Common.Header.Missing_ContentType);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(ErrorCodes.Http.Common.Header.Missing_ContentType);
}
{
@@ -138,7 +139,7 @@ namespace Timeline.Tests.IntegratedTests
content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
var res = await client.PutAsync("users/user/avatar", content);
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(ErrorCodes.Http.Common.Header.Zero_ContentLength);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(ErrorCodes.Http.Common.Header.Zero_ContentLength);
}
{
@@ -152,7 +153,7 @@ namespace Timeline.Tests.IntegratedTests
content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
var res = await client.PutAsync("users/user/avatar", content);
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_Content_TooBig);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserAvatarController.ErrorCodes.Put_Content_TooBig);
}
{
@@ -161,7 +162,7 @@ namespace Timeline.Tests.IntegratedTests
content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
var res = await client.PutAsync("users/user/avatar", content);
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_Content_UnmatchedLength_Less);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserAvatarController.ErrorCodes.Put_Content_UnmatchedLength_Less);
}
{
@@ -170,25 +171,25 @@ namespace Timeline.Tests.IntegratedTests
content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
var res = await client.PutAsync("users/user/avatar", content);
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_Content_UnmatchedLength_Bigger);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserAvatarController.ErrorCodes.Put_Content_UnmatchedLength_Bigger);
}
{
var res = await client.PutByteArrayAsync("users/user/avatar", new[] { (byte)0x00 }, "image/png");
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_BadFormat_CantDecode);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserAvatarController.ErrorCodes.Put_BadFormat_CantDecode);
}
{
var res = await client.PutByteArrayAsync("users/user/avatar", mockAvatar.Data, "image/jpeg");
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_BadFormat_UnmatchedFormat);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserAvatarController.ErrorCodes.Put_BadFormat_UnmatchedFormat);
}
{
var res = await client.PutByteArrayAsync("users/user/avatar", ImageHelper.CreatePngWithSize(100, 200), "image/png");
res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_BadFormat_BadSize);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserAvatarController.ErrorCodes.Put_BadFormat_BadSize);
}
{
@@ -196,7 +197,7 @@ namespace Timeline.Tests.IntegratedTests
res.Should().HaveStatusCode(HttpStatusCode.OK);
var res2 = await client.GetAsync("users/user/avatar");
- res2.Should().HaveStatusCodeOk();
+ res2.Should().HaveStatusCode(200);
res2.Content.Headers.ContentType.MediaType.Should().Be(mockAvatar.Type);
var body = await res2.Content.ReadAsByteArrayAsync();
body.Should().Equal(mockAvatar.Data);
@@ -218,19 +219,19 @@ namespace Timeline.Tests.IntegratedTests
{
var res = await client.PutByteArrayAsync("users/admin/avatar", new[] { (byte)0x00 }, "image/png");
res.Should().HaveStatusCode(HttpStatusCode.Forbidden)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_Forbid);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserAvatarController.ErrorCodes.Put_Forbid);
}
{
var res = await client.DeleteAsync("users/admin/avatar");
res.Should().HaveStatusCode(HttpStatusCode.Forbidden)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Delete_Forbid);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserAvatarController.ErrorCodes.Delete_Forbid);
}
for (int i = 0; i < 2; i++) // double delete should work.
{
var res = await client.DeleteAsync("users/user/avatar");
- res.Should().HaveStatusCodeOk();
+ res.Should().HaveStatusCode(200);
await GetReturnDefault();
}
}
@@ -250,14 +251,15 @@ namespace Timeline.Tests.IntegratedTests
{
var res = await client.PutByteArrayAsync("users/usernotexist/avatar", new[] { (byte)0x00 }, "image/png");
- res.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_UserNotExist);
+ res.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(UserAvatarController.ErrorCodes.Put_UserNotExist);
}
{
var res = await client.DeleteAsync("users/usernotexist/avatar");
- res.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Delete_UserNotExist);
+ res.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserAvatarController.ErrorCodes.Delete_UserNotExist);
}
}
}
diff --git a/Timeline.Tests/IntegratedTests/UserDetailTest.cs b/Timeline.Tests/IntegratedTests/UserDetailTest.cs
index d1a67d9d..4d268efa 100644
--- a/Timeline.Tests/IntegratedTests/UserDetailTest.cs
+++ b/Timeline.Tests/IntegratedTests/UserDetailTest.cs
@@ -35,21 +35,21 @@ namespace Timeline.Tests.IntegratedTests
{
{
var res = await client.GetAsync($"users/usernotexist/nickname");
- res.Should().HaveStatusCodeNotFound()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.GetNickname_UserNotExist);
+ res.Should().HaveStatusCode(404)
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserDetailController.ErrorCodes.GetNickname_UserNotExist);
}
{
var res = await client.GetAsync($"users/usernotexist/details");
- res.Should().HaveStatusCodeNotFound()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.Get_UserNotExist);
+ res.Should().HaveStatusCode(404)
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserDetailController.ErrorCodes.Get_UserNotExist);
}
async Task GetAndTest(UserDetail d)
{
var res = await client.GetAsync($"users/{MockUser.User.Username}/details");
- res.Should().HaveStatusCodeOk()
- .And.Should().HaveBodyAsJson<UserDetail>()
+ res.Should().HaveStatusCode(200)
+ .And.Should().HaveJsonBody<UserDetail>()
.Which.Should().BeEquivalentTo(d);
}
@@ -58,7 +58,7 @@ namespace Timeline.Tests.IntegratedTests
{
var res = await client.PatchAsJsonAsync($"users/{MockUser.Admin.Username}/details", new UserDetail());
res.Should().HaveStatusCode(HttpStatusCode.Forbidden)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.Patch_Forbid);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserDetailController.ErrorCodes.Patch_Forbid);
}
{
@@ -70,7 +70,7 @@ namespace Timeline.Tests.IntegratedTests
PhoneNumber = "aaaaaaaa"
});
var body = res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponse().Which;
+ .And.Should().HaveCommonBody().Which;
body.Code.Should().Be(ErrorCodes.Http.Common.InvalidModel);
foreach (var key in new string[] { "nickname", "qq", "email", "phonenumber" })
{
@@ -89,13 +89,13 @@ namespace Timeline.Tests.IntegratedTests
{
var res = await client.PatchAsJsonAsync($"users/{MockUser.User.Username}/details", detail);
- res.Should().HaveStatusCodeOk();
+ res.Should().HaveStatusCode(200);
await GetAndTest(detail);
}
{
var res = await client.GetAsync($"users/{MockUser.User.Username}/nickname");
- res.Should().HaveStatusCodeOk().And.Should().HaveBodyAsJson<UserDetail>()
+ res.Should().HaveStatusCode(200).And.Should().HaveJsonBody<UserDetail>()
.Which.Should().BeEquivalentTo(new UserDetail
{
Nickname = detail.Nickname
@@ -111,7 +111,7 @@ namespace Timeline.Tests.IntegratedTests
{
var res = await client.PatchAsJsonAsync($"users/{MockUser.User.Username}/details", detail2);
- res.Should().HaveStatusCodeOk();
+ res.Should().HaveStatusCode(200);
await GetAndTest(new UserDetail
{
Nickname = detail.Nickname,
@@ -131,13 +131,13 @@ namespace Timeline.Tests.IntegratedTests
{
{
var res = await client.PatchAsJsonAsync($"users/{MockUser.User.Username}/details", new UserDetail());
- res.Should().HaveStatusCodeOk();
+ res.Should().HaveStatusCode(200);
}
{
var res = await client.PatchAsJsonAsync($"users/usernotexist/details", new UserDetail());
- res.Should().HaveStatusCodeNotFound()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.Patch_UserNotExist);
+ res.Should().HaveStatusCode(404)
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(UserDetailController.ErrorCodes.Patch_UserNotExist);
}
}
}
diff --git a/Timeline.Tests/IntegratedTests/UserUnitTest.cs b/Timeline.Tests/IntegratedTests/UserUnitTest.cs
index d228c563..b2aab24c 100644
--- a/Timeline.Tests/IntegratedTests/UserUnitTest.cs
+++ b/Timeline.Tests/IntegratedTests/UserUnitTest.cs
@@ -1,6 +1,7 @@
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using System;
+using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Timeline.Controllers;
@@ -11,7 +12,7 @@ using Timeline.Tests.Helpers.Authentication;
using Timeline.Tests.Mock.Data;
using Xunit;
-namespace Timeline.Tests
+namespace Timeline.Tests.IntegratedTests
{
public class UserUnitTest : IClassFixture<WebApplicationFactory<Startup>>, IDisposable
{
@@ -32,157 +33,141 @@ namespace Timeline.Tests
[Fact]
public async Task Get_Users_List()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- var res = await client.GetAsync("users");
- res.Should().HaveStatusCodeOk().And.Should().HaveBodyAsJson<UserInfo[]>()
- .Which.Should().BeEquivalentTo(MockUser.UserInfoList);
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.GetAsync("users");
+ res.Should().HaveStatusCode(200)
+ .And.Should().HaveJsonBody<UserInfo[]>()
+ .Which.Should().BeEquivalentTo(MockUser.UserInfoList);
}
[Fact]
public async Task Get_Users_User()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- var res = await client.GetAsync("users/" + MockUser.User.Username);
- res.Should().HaveStatusCodeOk()
- .And.Should().HaveBodyAsJson<UserInfo>()
- .Which.Should().BeEquivalentTo(MockUser.User.Info);
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.GetAsync("users/" + MockUser.User.Username);
+ res.Should().HaveStatusCode(200)
+ .And.Should().HaveJsonBody<UserInfo>()
+ .Which.Should().BeEquivalentTo(MockUser.User.Info);
}
[Fact]
public async Task Get_Users_404()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- var res = await client.GetAsync("users/usernotexist");
- res.Should().HaveStatusCodeNotFound()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserController.ErrorCodes.Get_NotExist);
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.GetAsync("users/usernotexist");
+ res.Should().HaveStatusCode(404)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(UserController.ErrorCodes.Get_NotExist);
}
- [Fact]
- public async Task Put_InvalidModel()
+ public static IEnumerable<object[]> Put_InvalidModel_Data()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- const string url = "users/aaaaaaaa";
- // missing password
- await InvalidModelTestHelpers.TestPutInvalidModel(client, url, new UserPutRequest { Password = null, Administrator = false });
- // missing administrator
- await InvalidModelTestHelpers.TestPutInvalidModel(client, url, new UserPutRequest { Password = "???", Administrator = null });
- }
+ yield return new object[] { null, false };
+ yield return new object[] { "p", null };
+ }
+
+ [Theory]
+ [MemberData(nameof(Put_InvalidModel_Data))]
+ public async Task Put_InvalidModel(string password, bool? administrator)
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ const string url = "users/aaaaaaaa";
+ (await client.PutAsJsonAsync(url,
+ new UserPutRequest { Password = password, Administrator = administrator }))
+ .Should().BeInvalidModel();
}
[Fact]
public async Task Put_BadUsername()
{
- using (var client = await _factory.CreateClientAsAdmin())
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.PutAsJsonAsync("users/dsf fddf", new UserPutRequest
{
- var res = await client.PutAsJsonAsync("users/dsf fddf", new UserPutRequest
- {
- Password = "???",
- Administrator = false
- });
- res.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserController.ErrorCodes.Put_BadUsername);
- }
+ Password = "???",
+ Administrator = false
+ });
+ res.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(UserController.ErrorCodes.Put_BadUsername);
}
private async Task CheckAdministrator(HttpClient client, string username, bool administrator)
{
var res = await client.GetAsync("users/" + username);
- res.Should().HaveStatusCodeOk()
- .And.Should().HaveBodyAsJson<UserInfo>()
+ res.Should().HaveStatusCode(200)
+ .And.Should().HaveJsonBody<UserInfo>()
.Which.Administrator.Should().Be(administrator);
}
[Fact]
public async Task Put_Modiefied()
{
- using (var client = await _factory.CreateClientAsAdmin())
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.PutAsJsonAsync("users/" + MockUser.User.Username, new UserPutRequest
{
- var res = await client.PutAsJsonAsync("users/" + MockUser.User.Username, new UserPutRequest
- {
- Password = "password",
- Administrator = false
- });
- res.Should().BePutModify();
- await CheckAdministrator(client, MockUser.User.Username, false);
- }
+ Password = "password",
+ Administrator = false
+ });
+ res.Should().BePutModify();
+ await CheckAdministrator(client, MockUser.User.Username, false);
}
[Fact]
public async Task Put_Created()
{
- using (var client = await _factory.CreateClientAsAdmin())
+ using var client = await _factory.CreateClientAsAdmin();
+ const string username = "puttest";
+ const string url = "users/" + username;
+
+ var res = await client.PutAsJsonAsync(url, new UserPutRequest
{
- const string username = "puttest";
- const string url = "users/" + username;
-
- var res = await client.PutAsJsonAsync(url, new UserPutRequest
- {
- Password = "password",
- Administrator = false
- });
- res.Should().BePutCreate();
- await CheckAdministrator(client, username, false);
- }
+ Password = "password",
+ Administrator = false
+ });
+ res.Should().BePutCreate();
+ await CheckAdministrator(client, username, false);
}
[Fact]
public async Task Patch_NotExist()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- var res = await client.PatchAsJsonAsync("users/usernotexist", new UserPatchRequest { });
- res.Should().HaveStatusCodeNotFound()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserController.ErrorCodes.Patch_NotExist);
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.PatchAsJsonAsync("users/usernotexist", new UserPatchRequest { });
+ res.Should().HaveStatusCode(404)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(UserController.ErrorCodes.Patch_NotExist);
}
[Fact]
public async Task Patch_Success()
{
- using (var client = await _factory.CreateClientAsAdmin())
+ using var client = await _factory.CreateClientAsAdmin();
{
- {
- var res = await client.PatchAsJsonAsync("users/" + MockUser.User.Username,
- new UserPatchRequest { Administrator = false });
- res.Should().HaveStatusCodeOk();
- await CheckAdministrator(client, MockUser.User.Username, false);
- }
+ var res = await client.PatchAsJsonAsync("users/" + MockUser.User.Username,
+ new UserPatchRequest { Administrator = false });
+ res.Should().HaveStatusCode(200);
+ await CheckAdministrator(client, MockUser.User.Username, false);
}
}
[Fact]
public async Task Delete_Deleted()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- {
- var url = "users/" + MockUser.User.Username;
- var res = await client.DeleteAsync(url);
- res.Should().BeDeleteDelete();
-
- var res2 = await client.GetAsync(url);
- res2.Should().HaveStatusCodeNotFound();
- }
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ var url = "users/" + MockUser.User.Username;
+ var res = await client.DeleteAsync(url);
+ res.Should().BeDeleteDelete();
+
+ var res2 = await client.GetAsync(url);
+ res2.Should().HaveStatusCode(404);
}
[Fact]
public async Task Delete_NotExist()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- {
- var res = await client.DeleteAsync("users/usernotexist");
- res.Should().BeDeleteNotExist();
- }
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.DeleteAsync("users/usernotexist");
+ res.Should().BeDeleteNotExist();
}
@@ -204,58 +189,54 @@ namespace Timeline.Tests
_testApp.Dispose();
}
- [Fact]
- public async Task InvalidModel()
+ public static IEnumerable<object[]> InvalidModel_Data()
+ {
+ yield return new[] { null, "uuu" };
+ yield return new[] { "uuu", null };
+ yield return new[] { "uuu", "???" };
+ }
+
+ [Theory]
+ [MemberData(nameof(InvalidModel_Data))]
+ public async Task InvalidModel(string oldUsername, string newUsername)
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- // missing old username
- await InvalidModelTestHelpers.TestPostInvalidModel(client, url,
- new ChangeUsernameRequest { OldUsername = null, NewUsername = "hhh" });
- // missing new username
- await InvalidModelTestHelpers.TestPostInvalidModel(client, url,
- new ChangeUsernameRequest { OldUsername = "hhh", NewUsername = null });
- // bad username
- await InvalidModelTestHelpers.TestPostInvalidModel(client, url,
- new ChangeUsernameRequest { OldUsername = "hhh", NewUsername = "???" });
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ (await client.PostAsJsonAsync(url,
+ new ChangeUsernameRequest { OldUsername = oldUsername, NewUsername = newUsername }))
+ .Should().BeInvalidModel();
}
[Fact]
public async Task UserNotExist()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- var res = await client.PostAsJsonAsync(url,
- new ChangeUsernameRequest { OldUsername = "usernotexist", NewUsername = "newUsername" });
- res.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserController.ErrorCodes.ChangeUsername_NotExist);
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.PostAsJsonAsync(url,
+ new ChangeUsernameRequest { OldUsername = "usernotexist", NewUsername = "newUsername" });
+ res.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(UserController.ErrorCodes.ChangeUsername_NotExist);
}
[Fact]
public async Task UserAlreadyExist()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- var res = await client.PostAsJsonAsync(url,
- new ChangeUsernameRequest { OldUsername = MockUser.User.Username, NewUsername = MockUser.Admin.Username });
- res.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserController.ErrorCodes.ChangeUsername_AlreadyExist);
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.PostAsJsonAsync(url,
+ new ChangeUsernameRequest { OldUsername = MockUser.User.Username, NewUsername = MockUser.Admin.Username });
+ res.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(UserController.ErrorCodes.ChangeUsername_AlreadyExist);
}
[Fact]
public async Task Success()
{
- using (var client = await _factory.CreateClientAsAdmin())
- {
- const string newUsername = "hahaha";
- var res = await client.PostAsJsonAsync(url,
- new ChangeUsernameRequest { OldUsername = MockUser.User.Username, NewUsername = newUsername });
- res.Should().HaveStatusCodeOk();
- await client.CreateUserTokenAsync(newUsername, MockUser.User.Password);
- }
+ using var client = await _factory.CreateClientAsAdmin();
+ const string newUsername = "hahaha";
+ var res = await client.PostAsJsonAsync(url,
+ new ChangeUsernameRequest { OldUsername = MockUser.User.Username, NewUsername = newUsername });
+ res.Should().HaveStatusCode(200);
+ await client.CreateUserTokenAsync(newUsername, MockUser.User.Password);
}
}
@@ -278,42 +259,41 @@ namespace Timeline.Tests
_testApp.Dispose();
}
- [Fact]
- public async Task InvalidModel()
+ public static IEnumerable<object[]> InvalidModel_Data()
+ {
+ yield return new[] { null, "ppp" };
+ yield return new[] { "ppp", null };
+ }
+
+ [Theory]
+ [MemberData(nameof(InvalidModel_Data))]
+ public async Task InvalidModel(string oldPassword, string newPassword)
{
- using (var client = await _factory.CreateClientAsUser())
- {
- // missing old password
- await InvalidModelTestHelpers.TestPostInvalidModel(client, url,
- new ChangePasswordRequest { OldPassword = null, NewPassword = "???" });
- // missing new password
- await InvalidModelTestHelpers.TestPostInvalidModel(client, url,
- new ChangePasswordRequest { OldPassword = "???", NewPassword = null });
- }
+ using var client = await _factory.CreateClientAsUser();
+ (await client.PostAsJsonAsync(url,
+ new ChangePasswordRequest { OldPassword = oldPassword, NewPassword = newPassword }))
+ .Should().BeInvalidModel();
}
[Fact]
public async Task BadOldPassword()
{
- using (var client = await _factory.CreateClientAsUser())
- {
- var res = await client.PostAsJsonAsync(url, new ChangePasswordRequest { OldPassword = "???", NewPassword = "???" });
- res.Should().HaveStatusCodeBadRequest()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserController.ErrorCodes.ChangePassword_BadOldPassword);
- }
+ using var client = await _factory.CreateClientAsUser();
+ var res = await client.PostAsJsonAsync(url, new ChangePasswordRequest { OldPassword = "???", NewPassword = "???" });
+ res.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(UserController.ErrorCodes.ChangePassword_BadOldPassword);
}
[Fact]
public async Task Success()
{
- using (var client = await _factory.CreateClientAsUser())
- {
- const string newPassword = "new";
- var res = await client.PostAsJsonAsync(url,
- new ChangePasswordRequest { OldPassword = MockUser.User.Password, NewPassword = newPassword });
- res.Should().HaveStatusCodeOk();
- await client.CreateUserTokenAsync(MockUser.User.Username, newPassword);
- }
+ using var client = await _factory.CreateClientAsUser();
+ const string newPassword = "new";
+ var res = await client.PostAsJsonAsync(url,
+ new ChangePasswordRequest { OldPassword = MockUser.User.Password, NewPassword = newPassword });
+ res.Should().HaveStatusCode(200);
+ await client.CreateUserTokenAsync(MockUser.User.Username, newPassword);
}
}
}
diff --git a/Timeline/GlobalSuppressions.cs b/Timeline/GlobalSuppressions.cs
new file mode 100644
index 00000000..3c9c8341
--- /dev/null
+++ b/Timeline/GlobalSuppressions.cs
@@ -0,0 +1,9 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "This is not a UI application.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "This is not bad.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "No need to check the null because it's ASP.Net's duty.", Scope = "namespaceanddescendants", Target = "Timeline.Controllers")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Error code constant identifiers use nested names.", Scope = "type", Target = "Timeline.ErrorCodes")]
diff --git a/Timeline/Models/Http/Common.cs b/Timeline/Models/Http/Common.cs
index af185e85..83e6a072 100644
--- a/Timeline/Models/Http/Common.cs
+++ b/Timeline/Models/Http/Common.cs
@@ -38,7 +38,7 @@ namespace Timeline.Models.Http
Message = message;
}
- public int Code { get; set; }
+ public int? Code { get; set; }
public string Message { get; set; }
}
diff --git a/Timeline/Program.cs b/Timeline/Program.cs
index dfc93b9e..7474fe2f 100644
--- a/Timeline/Program.cs
+++ b/Timeline/Program.cs
@@ -6,7 +6,7 @@ using Microsoft.Extensions.Hosting;
namespace Timeline
{
- public class Program
+ public static class Program
{
public static void Main(string[] args)
{
diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs
index 347b8cbb..9564b34b 100644
--- a/Timeline/Services/UserService.cs
+++ b/Timeline/Services/UserService.cs
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using System.Threading.Tasks;
using Timeline.Entities;
+using Timeline.Helpers;
using Timeline.Models;
using Timeline.Models.Validation;
using static Timeline.Helpers.MyLogHelper;
@@ -23,14 +24,20 @@ namespace Timeline.Services
{
private const string message = "The user does not exist.";
+ public UserNotExistException()
+ : base(message)
+ {
+
+ }
+
public UserNotExistException(string username)
- : base(FormatLogMessage(message, Pair("Username", username)))
+ : base(Log.Format(message, ("Username", username)))
{
Username = username;
}
public UserNotExistException(long id)
- : base(FormatLogMessage(message, Pair("Id", id)))
+ : base(Log.Format(message, ("Id", id)))
{
Id = id;
}
@@ -42,21 +49,29 @@ namespace Timeline.Services
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
/// <summary>
- /// The username that does not exist. May be null then <see cref="Id"/> is not null.
+ /// The username that does not exist.
/// </summary>
- public string Username { get; private set; }
+ public string Username { get; set; }
/// <summary>
- /// The id that does not exist. May be null then <see cref="Username"/> is not null.
+ /// The id that does not exist.
/// </summary>
- public long? Id { get; private set; }
+ public long? Id { get; set; }
}
[Serializable]
public class BadPasswordException : Exception
{
+ private const string message = "Password is wrong.";
+
+ public BadPasswordException()
+ : base(message)
+ {
+
+ }
+
public BadPasswordException(string badPassword)
- : base(FormatLogMessage("Password is wrong.", Pair("Bad Password", badPassword)))
+ : base(Log.Format(message, ("Bad Password", badPassword)))
{
Password = badPassword;
}
@@ -70,22 +85,31 @@ namespace Timeline.Services
/// <summary>
/// The wrong password.
/// </summary>
- public string Password { get; private set; }
+ public string Password { get; set; }
}
[Serializable]
public class BadTokenVersionException : Exception
{
+ private const string message = "Token version is expired.";
+
+ public BadTokenVersionException()
+ : base(message)
+ {
+
+ }
+
public BadTokenVersionException(long tokenVersion, long requiredVersion)
- : base(FormatLogMessage("Token version is expired.",
- Pair("Token Version", tokenVersion),
- Pair("Required Version", requiredVersion)))
+ : base(Log.Format(message,
+ ("Token Version", tokenVersion),
+ ("Required Version", requiredVersion)))
{
TokenVersion = tokenVersion;
RequiredVersion = requiredVersion;
}
+ public BadTokenVersionException(string message) : base(message) { }
public BadTokenVersionException(string message, Exception inner) : base(message, inner) { }
protected BadTokenVersionException(
@@ -95,12 +119,12 @@ namespace Timeline.Services
/// <summary>
/// The version in the token.
/// </summary>
- public long TokenVersion { get; private set; }
+ public long? TokenVersion { get; set; }
/// <summary>
/// The version required.
/// </summary>
- public long RequiredVersion { get; private set; }
+ public long? RequiredVersion { get; set; }
}
/// <summary>
@@ -109,6 +133,12 @@ namespace Timeline.Services
[Serializable]
public class UsernameBadFormatException : Exception
{
+ private const string message = "Username is of bad format.";
+
+ public UsernameBadFormatException() : base(message) { }
+ public UsernameBadFormatException(string message) : base(message) { }
+ public UsernameBadFormatException(string message, Exception inner) : base(message, inner) { }
+
public UsernameBadFormatException(string username, string message) : base(message) { Username = username; }
public UsernameBadFormatException(string username, string message, Exception inner) : base(message, inner) { Username = username; }
protected UsernameBadFormatException(
@@ -128,7 +158,10 @@ namespace Timeline.Services
[Serializable]
public class UserAlreadyExistException : Exception
{
- public UserAlreadyExistException(string username) : base($"User {username} already exists.") { Username = username; }
+ private const string message = "User already exists.";
+
+ public UserAlreadyExistException() : base(message) { }
+ public UserAlreadyExistException(string username) : base(Log.Format(message, ("Username", username))) { Username = username; }
public UserAlreadyExistException(string username, string message) : base(message) { Username = username; }
public UserAlreadyExistException(string message, Exception inner) : base(message, inner) { }
protected UserAlreadyExistException(
diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs
index 8e8a6393..fc570fdd 100644
--- a/Timeline/Startup.cs
+++ b/Timeline/Startup.cs
@@ -27,11 +27,12 @@ namespace Timeline
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
- services.AddMvc()
+ services.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = InvalidModelResponseFactory.Factory;
- });
+ })
+ .AddNewtonsoftJson();
services.Configure<JwtConfig>(Configuration.GetSection(nameof(JwtConfig)));
var jwtConfig = Configuration.GetSection(nameof(JwtConfig)).Get<JwtConfig>();
diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj
index f01b8e31..836dfb47 100644
--- a/Timeline/Timeline.csproj
+++ b/Timeline/Timeline.csproj
@@ -13,9 +13,14 @@
</ItemGroup>
<ItemGroup>
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.6">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ </PackageReference>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0-rc1.final" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql.Design" Version="1.1.2" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-dev002868" />
- <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.5.0" />
+ <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.6.0" />
</ItemGroup>
</Project>