aboutsummaryrefslogtreecommitdiff
path: root/Timeline.Tests/IntegratedTests
diff options
context:
space:
mode:
author杨宇千 <crupest@outlook.com>2019-10-24 20:15:58 +0800
committerGitHub <noreply@github.com>2019-10-24 20:15:58 +0800
commit7305358a88ffc87f51f7b78deb4f07ef99120beb (patch)
tree7ca5010a06829cc5fadea1ea17ae72d082fc344c /Timeline.Tests/IntegratedTests
parent297d0c9029360f1d5334ed843b9b299356740ec1 (diff)
parenta0f3cd7599a48c14fb5492fb1c6e2dbd0a82fb45 (diff)
downloadtimeline-7305358a88ffc87f51f7b78deb4f07ef99120beb.tar.gz
timeline-7305358a88ffc87f51f7b78deb4f07ef99120beb.tar.bz2
timeline-7305358a88ffc87f51f7b78deb4f07ef99120beb.zip
Merge pull request #50 from crupest/refactor
Refactor : A Huge Step
Diffstat (limited to 'Timeline.Tests/IntegratedTests')
-rw-r--r--Timeline.Tests/IntegratedTests/AuthorizationTest.cs69
-rw-r--r--Timeline.Tests/IntegratedTests/TokenTest.cs176
-rw-r--r--Timeline.Tests/IntegratedTests/UserAvatarTest.cs (renamed from Timeline.Tests/IntegratedTests/UserAvatarTests.cs)83
-rw-r--r--Timeline.Tests/IntegratedTests/UserDetailTest.cs146
-rw-r--r--Timeline.Tests/IntegratedTests/UserTest.cs277
5 files changed, 575 insertions, 176 deletions
diff --git a/Timeline.Tests/IntegratedTests/AuthorizationTest.cs b/Timeline.Tests/IntegratedTests/AuthorizationTest.cs
new file mode 100644
index 00000000..a31d98f5
--- /dev/null
+++ b/Timeline.Tests/IntegratedTests/AuthorizationTest.cs
@@ -0,0 +1,69 @@
+using FluentAssertions;
+using Microsoft.AspNetCore.Mvc.Testing;
+using System;
+using System.Net;
+using System.Threading.Tasks;
+using Timeline.Tests.Helpers;
+using Timeline.Tests.Helpers.Authentication;
+using Xunit;
+
+namespace Timeline.Tests.IntegratedTests
+{
+ public class AuthorizationTest : IClassFixture<WebApplicationFactory<Startup>>, IDisposable
+ {
+ private readonly TestApplication _testApp;
+ private readonly WebApplicationFactory<Startup> _factory;
+
+ public AuthorizationTest(WebApplicationFactory<Startup> factory)
+ {
+ _testApp = new TestApplication(factory);
+ _factory = _testApp.Factory;
+ }
+
+ public void Dispose()
+ {
+ _testApp.Dispose();
+ }
+
+ private const string BaseUrl = "testing/auth/";
+ private const string AuthorizeUrl = BaseUrl + "Authorize";
+ private const string UserUrl = BaseUrl + "User";
+ private const string AdminUrl = BaseUrl + "Admin";
+
+ [Fact]
+ public async Task UnauthenticationTest()
+ {
+ using var client = _factory.CreateDefaultClient();
+ var response = await client.GetAsync(AuthorizeUrl);
+ response.Should().HaveStatusCode(HttpStatusCode.Unauthorized);
+ }
+
+ [Fact]
+ public async Task AuthenticationTest()
+ {
+ using var client = await _factory.CreateClientAsUser();
+ var response = await client.GetAsync(AuthorizeUrl);
+ response.Should().HaveStatusCode(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task UserAuthorizationTest()
+ {
+ using var client = await _factory.CreateClientAsUser();
+ var response1 = await client.GetAsync(UserUrl);
+ response1.Should().HaveStatusCode(HttpStatusCode.OK);
+ var response2 = await client.GetAsync(AdminUrl);
+ response2.Should().HaveStatusCode(HttpStatusCode.Forbidden);
+ }
+
+ [Fact]
+ public async Task AdminAuthorizationTest()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ var response1 = await client.GetAsync(UserUrl);
+ response1.Should().HaveStatusCode(HttpStatusCode.OK);
+ var response2 = await client.GetAsync(AdminUrl);
+ response2.Should().HaveStatusCode(HttpStatusCode.OK);
+ }
+ }
+}
diff --git a/Timeline.Tests/IntegratedTests/TokenTest.cs b/Timeline.Tests/IntegratedTests/TokenTest.cs
new file mode 100644
index 00000000..e9b6e1e9
--- /dev/null
+++ b/Timeline.Tests/IntegratedTests/TokenTest.cs
@@ -0,0 +1,176 @@
+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;
+using Timeline.Services;
+using Timeline.Tests.Helpers;
+using Timeline.Tests.Helpers.Authentication;
+using Timeline.Tests.Mock.Data;
+using Xunit;
+using static Timeline.ErrorCodes.Http.Token;
+
+namespace Timeline.Tests.IntegratedTests
+{
+ public class TokenTest : IClassFixture<WebApplicationFactory<Startup>>, IDisposable
+ {
+ private const string CreateTokenUrl = "token/create";
+ private const string VerifyTokenUrl = "token/verify";
+
+ private readonly TestApplication _testApp;
+ private readonly WebApplicationFactory<Startup> _factory;
+
+ public TokenTest(WebApplicationFactory<Startup> factory)
+ {
+ _testApp = new TestApplication(factory);
+ _factory = _testApp.Factory;
+ }
+
+ public void Dispose()
+ {
+ _testApp.Dispose();
+ }
+
+ public static IEnumerable<object[]> CreateToken_InvalidModel_Data()
+ {
+ 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 };
+ }
+
+ [Theory]
+ [MemberData(nameof(CreateToken_InvalidModel_Data))]
+ public async Task CreateToken_InvalidModel(string username, string password, int expire)
+ {
+ using var client = _factory.CreateDefaultClient();
+ (await client.PostAsJsonAsync(CreateTokenUrl, new CreateTokenRequest
+ {
+ Username = username,
+ Password = password,
+ Expire = expire
+ })).Should().BeInvalidModel();
+ }
+
+ public static IEnumerable<object[]> CreateToken_UserCredential_Data()
+ {
+ 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 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().HaveStatusCode(200)
+ .And.Should().HaveJsonBody<CreateTokenResponse>().Which;
+ body.Token.Should().NotBeNullOrWhiteSpace();
+ body.User.Should().BeEquivalentTo(MockUser.User.Info);
+ }
+
+ [Fact]
+ public async Task VerifyToken_InvalidModel()
+ {
+ using var client = _factory.CreateDefaultClient();
+ (await client.PostAsJsonAsync(VerifyTokenUrl,
+ new VerifyTokenRequest { Token = null })).Should().BeInvalidModel();
+ }
+
+ [Fact]
+ public async Task VerifyToken_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 Task VerifyToken_OldVersion()
+ {
+ 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.
+ {
+ // 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 Task VerifyToken_UserNotExist()
+ {
+ 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 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 Task VerifyToken_Expired()
+ //{
+ // using (var client = _factory.CreateDefaultClient())
+ // {
+ // // I can only control the token expired time but not current time
+ // // because verify logic is encapsuled in other library.
+ // var mockClock = _factory.GetTestClock();
+ // mockClock.MockCurrentTime = DateTime.Now - TimeSpan.FromDays(2);
+ // var token = (await client.CreateUserTokenAsync(MockUsers.UserUsername, MockUsers.UserPassword, 1)).Token;
+ // var response = await client.PostAsJsonAsync(VerifyTokenUrl,
+ // new VerifyTokenRequest { Token = token });
+ // response.Should().HaveStatusCodeBadRequest()
+ // .And.Should().HaveBodyAsCommonResponseWithCode(TokenController.ErrorCodes.Verify_Expired);
+ // mockClock.MockCurrentTime = null;
+ // }
+ //}
+
+ [Fact]
+ 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().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/UserAvatarTest.cs
index 2a3442d1..ce389046 100644
--- a/Timeline.Tests/IntegratedTests/UserAvatarTests.cs
+++ b/Timeline.Tests/IntegratedTests/UserAvatarTest.cs
@@ -3,9 +3,9 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.Formats.Gif;
using System;
using System.Collections.Generic;
using System.IO;
@@ -14,31 +14,33 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Timeline.Controllers;
-using Timeline.Models.Http;
using Timeline.Services;
using Timeline.Tests.Helpers;
using Timeline.Tests.Helpers.Authentication;
using Xunit;
-using Xunit.Abstractions;
+using static Timeline.ErrorCodes.Http.Common;
+using static Timeline.ErrorCodes.Http.UserAvatar;
namespace Timeline.Tests.IntegratedTests
{
- public class UserAvatarUnitTest : IClassFixture<MyWebApplicationFactory<Startup>>, IDisposable
+ public class UserAvatarUnitTest : IClassFixture<WebApplicationFactory<Startup>>, IDisposable
{
+ private readonly TestApplication _testApp;
private readonly WebApplicationFactory<Startup> _factory;
- private readonly Action _disposeAction;
- public UserAvatarUnitTest(MyWebApplicationFactory<Startup> factory, ITestOutputHelper outputHelper)
+ public UserAvatarUnitTest(WebApplicationFactory<Startup> factory)
{
- _factory = factory.WithTestConfig(outputHelper, out _disposeAction);
+ _testApp = new TestApplication(factory);
+ _factory = _testApp.Factory;
}
public void Dispose()
{
- _disposeAction();
+ _testApp.Dispose();
}
[Fact]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "HttpMessageRequest should be disposed ???")]
public async Task Test()
{
Avatar mockAvatar = new Avatar
@@ -51,8 +53,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(Get.UserNotExist);
}
var env = _factory.Server.Host.Services.GetRequiredService<IWebHostEnvironment>();
@@ -61,7 +64,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);
@@ -70,7 +73,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);
@@ -92,7 +95,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(CommonResponse.ErrorCodes.Header_BadFormat_IfNonMatch);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(ErrorCodes.Http.Common.Header.BadFormat_IfNonMatch);
}
{
@@ -122,7 +125,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(CommonResponse.ErrorCodes.Header_Missing_ContentLength);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(ErrorCodes.Http.Common.Header.Missing_ContentLength);
}
{
@@ -130,7 +133,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(CommonResponse.ErrorCodes.Header_Missing_ContentType);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(ErrorCodes.Http.Common.Header.Missing_ContentType);
}
{
@@ -139,7 +142,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(CommonResponse.ErrorCodes.Header_Zero_ContentLength);
+ .And.Should().HaveCommonBody().Which.Code.Should().Be(ErrorCodes.Http.Common.Header.Zero_ContentLength);
}
{
@@ -153,7 +156,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(Content.TooBig);
}
{
@@ -162,7 +165,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(Content.UnmatchedLength_Smaller);
}
{
@@ -171,25 +174,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(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(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(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(Put.BadFormat_BadSize);
}
{
@@ -197,7 +200,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);
@@ -219,19 +222,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(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(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();
}
}
@@ -251,14 +254,34 @@ 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(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(Delete.UserNotExist);
+ }
+ }
+
+ // bad username check
+ using (var client = await _factory.CreateClientAsAdmin())
+ {
+ {
+ var res = await client.GetAsync("users/u!ser/avatar");
+ res.Should().BeInvalidModel();
+ }
+
+ {
+ var res = await client.PutByteArrayAsync("users/u!ser/avatar", ImageHelper.CreatePngWithSize(100, 100), "image/png");
+ res.Should().BeInvalidModel();
+ }
+
+ {
+ var res = await client.DeleteAsync("users/u!ser/avatar");
+ res.Should().BeInvalidModel();
}
}
}
diff --git a/Timeline.Tests/IntegratedTests/UserDetailTest.cs b/Timeline.Tests/IntegratedTests/UserDetailTest.cs
deleted file mode 100644
index ba15b7ca..00000000
--- a/Timeline.Tests/IntegratedTests/UserDetailTest.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-using FluentAssertions;
-using Microsoft.AspNetCore.Mvc.Testing;
-using System;
-using System.Net;
-using System.Threading.Tasks;
-using Timeline.Controllers;
-using Timeline.Models;
-using Timeline.Models.Http;
-using Timeline.Tests.Helpers;
-using Timeline.Tests.Helpers.Authentication;
-using Timeline.Tests.Mock.Data;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Timeline.Tests.IntegratedTests
-{
- public class UserDetailTest : IClassFixture<MyWebApplicationFactory<Startup>>, IDisposable
- {
- private readonly WebApplicationFactory<Startup> _factory;
- private readonly Action _disposeAction;
-
- public UserDetailTest(MyWebApplicationFactory<Startup> factory, ITestOutputHelper outputHelper)
- {
- _factory = factory.WithTestConfig(outputHelper, out _disposeAction);
- }
-
- public void Dispose()
- {
- _disposeAction();
- }
-
- [Fact]
- public async Task TestAsUser()
- {
- using (var client = await _factory.CreateClientAsUser())
- {
- {
- var res = await client.GetAsync($"users/usernotexist/nickname");
- res.Should().HaveStatusCodeNotFound()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.GetNickname_UserNotExist);
- }
-
- {
- var res = await client.GetAsync($"users/usernotexist/details");
- res.Should().HaveStatusCodeNotFound()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.Get_UserNotExist);
- }
-
- async Task GetAndTest(UserDetail d)
- {
- var res = await client.GetAsync($"users/{MockUsers.UserUsername}/details");
- res.Should().HaveStatusCodeOk()
- .And.Should().HaveBodyAsJson<UserDetail>()
- .Which.Should().BeEquivalentTo(d);
- }
-
- await GetAndTest(new UserDetail());
-
- {
- var res = await client.PatchAsJsonAsync($"users/{MockUsers.AdminUsername}/details", new UserDetail());
- res.Should().HaveStatusCode(HttpStatusCode.Forbidden)
- .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.Patch_Forbid);
- }
-
- {
- var res = await client.PatchAsJsonAsync($"users/{MockUsers.UserUsername}/details", new UserDetail
- {
- Nickname = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- QQ = "aaaaaaa",
- Email = "aaaaaa",
- PhoneNumber = "aaaaaaaa"
- });
- var body = res.Should().HaveStatusCode(HttpStatusCode.BadRequest)
- .And.Should().HaveBodyAsCommonResponse().Which;
- body.Code.Should().Be(CommonResponse.ErrorCodes.InvalidModel);
- foreach (var key in new string[] { "nickname", "qq", "email", "phonenumber" })
- {
- body.Message.Should().ContainEquivalentOf(key);
- }
- }
-
-
- var detail = new UserDetail
- {
- Nickname = "aaa",
- QQ = "1234567",
- Email = "aaaa@aaa.net",
- Description = "aaaaaaaaa"
- };
-
- {
- var res = await client.PatchAsJsonAsync($"users/{MockUsers.UserUsername}/details", detail);
- res.Should().HaveStatusCodeOk();
- await GetAndTest(detail);
- }
-
- {
- var res = await client.GetAsync($"users/{MockUsers.UserUsername}/nickname");
- res.Should().HaveStatusCodeOk().And.Should().HaveBodyAsJson<UserDetail>()
- .Which.Should().BeEquivalentTo(new UserDetail
- {
- Nickname = detail.Nickname
- });
- }
-
- var detail2 = new UserDetail
- {
- QQ = "",
- PhoneNumber = "12345678910",
- Description = "bbbbbbbb"
- };
-
- {
- var res = await client.PatchAsJsonAsync($"users/{MockUsers.UserUsername}/details", detail2);
- res.Should().HaveStatusCodeOk();
- await GetAndTest(new UserDetail
- {
- Nickname = detail.Nickname,
- QQ = null,
- Email = detail.Email,
- PhoneNumber = detail2.PhoneNumber,
- Description = detail2.Description
- });
- }
- }
- }
-
- [Fact]
- public async Task TestAsAdmin()
- {
- using (var client = await _factory.CreateClientAsAdmin())
- {
- {
- var res = await client.PatchAsJsonAsync($"users/{MockUsers.UserUsername}/details", new UserDetail());
- res.Should().HaveStatusCodeOk();
- }
-
- {
- var res = await client.PatchAsJsonAsync($"users/usernotexist/details", new UserDetail());
- res.Should().HaveStatusCodeNotFound()
- .And.Should().HaveBodyAsCommonResponseWithCode(UserDetailController.ErrorCodes.Patch_UserNotExist);
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/Timeline.Tests/IntegratedTests/UserTest.cs b/Timeline.Tests/IntegratedTests/UserTest.cs
new file mode 100644
index 00000000..ec70b7e8
--- /dev/null
+++ b/Timeline.Tests/IntegratedTests/UserTest.cs
@@ -0,0 +1,277 @@
+using FluentAssertions;
+using Microsoft.AspNetCore.Mvc.Testing;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Timeline.Models;
+using Timeline.Models.Http;
+using Timeline.Tests.Helpers;
+using Timeline.Tests.Helpers.Authentication;
+using Timeline.Tests.Mock.Data;
+using Xunit;
+using static Timeline.ErrorCodes.Http.User;
+
+namespace Timeline.Tests.IntegratedTests
+{
+ public class UserTest : IClassFixture<WebApplicationFactory<Startup>>, IDisposable
+ {
+ private readonly TestApplication _testApp;
+ private readonly WebApplicationFactory<Startup> _factory;
+
+ public UserTest(WebApplicationFactory<Startup> factory)
+ {
+ _testApp = new TestApplication(factory);
+ _factory = _testApp.Factory;
+ }
+
+ public void Dispose()
+ {
+ _testApp.Dispose();
+ }
+
+ [Fact]
+ public async Task Get_List_Success()
+ {
+ 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_Single_Success()
+ {
+ 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_InvalidModel()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.GetAsync("users/aaa!a");
+ res.Should().BeInvalidModel();
+ }
+
+ [Fact]
+ public async Task Get_Users_404()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.GetAsync("users/usernotexist");
+ res.Should().HaveStatusCode(404)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(Get.NotExist);
+ }
+
+ public static IEnumerable<object[]> Put_InvalidModel_Data()
+ {
+ yield return new object[] { "aaa", null, false };
+ yield return new object[] { "aaa", "p", null };
+ yield return new object[] { "aa!a", "p", false };
+ }
+
+ [Theory]
+ [MemberData(nameof(Put_InvalidModel_Data))]
+ public async Task Put_InvalidModel(string username, string password, bool? administrator)
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ (await client.PutAsJsonAsync("users/" + username,
+ new UserPutRequest { Password = password, Administrator = administrator }))
+ .Should().BeInvalidModel();
+ }
+
+ private async Task CheckAdministrator(HttpClient client, string username, bool administrator)
+ {
+ var res = await client.GetAsync("users/" + username);
+ 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();
+ var res = await client.PutAsJsonAsync("users/" + MockUser.User.Username, new UserPutRequest
+ {
+ Password = "password",
+ Administrator = false
+ });
+ res.Should().BePut(false);
+ await CheckAdministrator(client, MockUser.User.Username, false);
+ }
+
+ [Fact]
+ public async Task Put_Created()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ const string username = "puttest";
+ const string url = "users/" + username;
+
+ var res = await client.PutAsJsonAsync(url, new UserPutRequest
+ {
+ Password = "password",
+ Administrator = false
+ });
+ res.Should().BePut(true);
+ 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().HaveStatusCode(404)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(Patch.NotExist);
+ }
+
+ [Fact]
+ public async Task Patch_InvalidModel()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.PatchAsJsonAsync("users/aaa!a", new UserPatchRequest { });
+ res.Should().BeInvalidModel();
+ }
+
+ [Fact]
+ public async Task Patch_Success()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ {
+ 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_InvalidModel()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ var url = "users/aaa!a";
+ var res = await client.DeleteAsync(url);
+ res.Should().BeInvalidModel();
+ }
+
+ [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().BeDelete(true);
+
+ 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().BeDelete(false);
+ }
+
+ private const string changeUsernameUrl = "userop/changeusername";
+
+ public static IEnumerable<object[]> Op_ChangeUsername_InvalidModel_Data()
+ {
+ yield return new[] { null, "uuu" };
+ yield return new[] { "uuu", null };
+ yield return new[] { "a!a", "uuu" };
+ yield return new[] { "uuu", "a!a" };
+ }
+
+ [Theory]
+ [MemberData(nameof(Op_ChangeUsername_InvalidModel_Data))]
+ public async Task Op_ChangeUsername_InvalidModel(string oldUsername, string newUsername)
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ (await client.PostAsJsonAsync(changeUsernameUrl,
+ new ChangeUsernameRequest { OldUsername = oldUsername, NewUsername = newUsername }))
+ .Should().BeInvalidModel();
+ }
+
+ [Fact]
+ public async Task Op_ChangeUsername_UserNotExist()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.PostAsJsonAsync(changeUsernameUrl,
+ new ChangeUsernameRequest { OldUsername = "usernotexist", NewUsername = "newUsername" });
+ res.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(Op.ChangeUsername.NotExist);
+ }
+
+ [Fact]
+ public async Task Op_ChangeUsername_UserAlreadyExist()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ var res = await client.PostAsJsonAsync(changeUsernameUrl,
+ new ChangeUsernameRequest { OldUsername = MockUser.User.Username, NewUsername = MockUser.Admin.Username });
+ res.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(Op.ChangeUsername.AlreadyExist);
+ }
+
+ [Fact]
+ public async Task Op_ChangeUsername_Success()
+ {
+ using var client = await _factory.CreateClientAsAdmin();
+ const string newUsername = "hahaha";
+ var res = await client.PostAsJsonAsync(changeUsernameUrl,
+ new ChangeUsernameRequest { OldUsername = MockUser.User.Username, NewUsername = newUsername });
+ res.Should().HaveStatusCode(200);
+ await client.CreateUserTokenAsync(newUsername, MockUser.User.Password);
+ }
+
+ private const string changePasswordUrl = "userop/changepassword";
+
+ public static IEnumerable<object[]> Op_ChangePassword_InvalidModel_Data()
+ {
+ yield return new[] { null, "ppp" };
+ yield return new[] { "ppp", null };
+ }
+
+ [Theory]
+ [MemberData(nameof(Op_ChangePassword_InvalidModel_Data))]
+ public async Task Op_ChangePassword_InvalidModel(string oldPassword, string newPassword)
+ {
+ using var client = await _factory.CreateClientAsUser();
+ (await client.PostAsJsonAsync(changePasswordUrl,
+ new ChangePasswordRequest { OldPassword = oldPassword, NewPassword = newPassword }))
+ .Should().BeInvalidModel();
+ }
+
+ [Fact]
+ public async Task Op_ChangePassword_BadOldPassword()
+ {
+ using var client = await _factory.CreateClientAsUser();
+ var res = await client.PostAsJsonAsync(changePasswordUrl, new ChangePasswordRequest { OldPassword = "???", NewPassword = "???" });
+ res.Should().HaveStatusCode(400)
+ .And.Should().HaveCommonBody()
+ .Which.Code.Should().Be(Op.ChangePassword.BadOldPassword);
+ }
+
+ [Fact]
+ public async Task Op_ChangePassword_Success()
+ {
+ using var client = await _factory.CreateClientAsUser();
+ const string newPassword = "new";
+ var res = await client.PostAsJsonAsync(changePasswordUrl,
+ new ChangePasswordRequest { OldPassword = MockUser.User.Password, NewPassword = newPassword });
+ res.Should().HaveStatusCode(200);
+ await _factory.CreateDefaultClient() // don't use client above, because it sets authorization header
+ .CreateUserTokenAsync(MockUser.User.Username, newPassword);
+ }
+ }
+}