diff options
author | crupest <crupest@outlook.com> | 2020-02-03 18:18:45 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-02-03 18:18:45 +0800 |
commit | be8cbe2c4ddf2076cc02bcb2feb1a70d30a4bda0 (patch) | |
tree | 28ef20f9dfe742008bb934f09b99d1d4719cabaa /Timeline.Tests/IntegratedTests/TimelineTest.cs | |
parent | 5f93daed17338abbcade5eaba8936acbc4955b56 (diff) | |
download | timeline-be8cbe2c4ddf2076cc02bcb2feb1a70d30a4bda0.tar.gz timeline-be8cbe2c4ddf2076cc02bcb2feb1a70d30a4bda0.tar.bz2 timeline-be8cbe2c4ddf2076cc02bcb2feb1a70d30a4bda0.zip |
Finish normal timeline development.
Diffstat (limited to 'Timeline.Tests/IntegratedTests/TimelineTest.cs')
-rw-r--r-- | Timeline.Tests/IntegratedTests/TimelineTest.cs | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/Timeline.Tests/IntegratedTests/TimelineTest.cs b/Timeline.Tests/IntegratedTests/TimelineTest.cs new file mode 100644 index 00000000..58066d71 --- /dev/null +++ b/Timeline.Tests/IntegratedTests/TimelineTest.cs @@ -0,0 +1,589 @@ +using FluentAssertions;
+using Microsoft.AspNetCore.Mvc.Testing;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Timeline.Models.Http;
+using Timeline.Tests.Helpers;
+using Xunit;
+
+namespace Timeline.Tests.IntegratedTests
+{
+ public class TimelineTest : IntegratedTestBase
+ {
+ public TimelineTest(WebApplicationFactory<Startup> factory)
+ : base(factory, 3)
+ {
+
+ }
+
+ private List<TimelineInfo> _testTimelines;
+
+ private async Task CreateTestTimelines()
+ {
+ _testTimelines = new List<TimelineInfo>();
+ for (int i = 0; i <= 3; i++)
+ {
+ var client = await CreateClientAs(i);
+ var res = await client.PostAsJsonAsync("timelines", new TimelineCreateRequest { Name = $"t{i}" });
+ var timelineInfo = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which;
+ _testTimelines.Add(timelineInfo);
+ }
+ }
+
+ [Fact]
+ public async Task TimelineCreate_Should_Work()
+ {
+ {
+ using var client = await CreateDefaultClient();
+ var res = await client.PostAsJsonAsync("timelines", new TimelineCreateRequest { Name = "aaa" });
+ res.Should().HaveStatusCode(HttpStatusCode.Unauthorized);
+ }
+
+ using (var client = await CreateClientAsUser())
+ {
+ {
+ var res = await client.PostAsJsonAsync("timelines", new TimelineCreateRequest { Name = "!!!" });
+ res.Should().BeInvalidModel();
+ }
+
+ TimelineInfo timelineInfo;
+ {
+ var res = await client.PostAsJsonAsync("timelines", new TimelineCreateRequest { Name = "aaa" });
+ timelineInfo = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which;
+ }
+
+ {
+ var res = await client.GetAsync("timelines/aaa");
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>()
+ .Which.Should().BeEquivalentTo(timelineInfo);
+ }
+
+ {
+ var res = await client.PostAsJsonAsync("timelines", new TimelineCreateRequest { Name = "aaa" });
+ res.Should().HaveStatusCode(400)
+ .And.HaveCommonBody(ErrorCodes.TimelineCommon.NameConflict);
+ }
+ }
+ }
+
+ [Fact]
+ public async Task InvalidModel_BadName()
+ {
+ using var client = await CreateClientAsAdministrator();
+ {
+ var res = await client.GetAsync("timelines/aaa!!!");
+ res.Should().BeInvalidModel();
+ }
+ {
+ var res = await client.PatchAsJsonAsync("timelines/aaa!!!", new TimelinePatchRequest { });
+ res.Should().BeInvalidModel();
+ }
+ {
+ var res = await client.PutAsync("timelines/aaa!!!/members/user1", null);
+ res.Should().BeInvalidModel();
+ }
+ {
+ var res = await client.DeleteAsync("timelines/aaa!!!/members/user1");
+ res.Should().BeInvalidModel();
+ }
+ {
+ var res = await client.GetAsync("timelines/aaa!!!/posts");
+ res.Should().BeInvalidModel();
+ }
+ {
+ var res = await client.PostAsJsonAsync("timelines/aaa!!!/posts", new TimelinePostCreateRequest { Content = "aaa" });
+ res.Should().BeInvalidModel();
+ }
+ {
+ var res = await client.DeleteAsync("timelines/aaa!!!/posts/123");
+ res.Should().BeInvalidModel();
+ }
+ }
+
+ [Fact]
+ public async Task NotFound()
+ {
+ using var client = await CreateClientAsAdministrator();
+ {
+ var res = await client.GetAsync("timelines/notexist");
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ }
+ {
+ var res = await client.PatchAsJsonAsync("timelines/notexist", new TimelinePatchRequest { });
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ }
+ {
+ var res = await client.PutAsync("timelines/notexist/members/user1", null);
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ }
+ {
+ var res = await client.DeleteAsync("timelines/notexist/members/user1");
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ }
+ {
+ var res = await client.GetAsync("timelines/notexist/posts");
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ }
+ {
+ var res = await client.PostAsJsonAsync("timelines/notexist/posts", new TimelinePostCreateRequest { Content = "aaa" });
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ }
+ {
+ var res = await client.DeleteAsync("timelines/notexist/posts/123");
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ }
+ }
+
+ [Fact]
+ public async Task Description_Should_Work()
+ {
+ await CreateTestTimelines();
+
+ using var client = await CreateClientAsUser();
+
+ async Task AssertDescription(string description)
+ {
+ var res = await client.GetAsync("timelines/t1");
+ var body = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>()
+ .Which.Description.Should().Be(description);
+ }
+
+ const string mockDescription = "haha";
+
+ await AssertDescription("");
+ {
+ var res = await client.PatchAsJsonAsync("timelines/t1",
+ new TimelinePatchRequest { Description = mockDescription });
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be(mockDescription);
+ await AssertDescription(mockDescription);
+ }
+ {
+ var res = await client.PatchAsJsonAsync("timelines/t1",
+ new TimelinePatchRequest { Description = null });
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be(mockDescription);
+ await AssertDescription(mockDescription);
+ }
+ {
+ var res = await client.PatchAsJsonAsync("timelines/t1",
+ new TimelinePatchRequest { Description = "" });
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be("");
+ await AssertDescription("");
+ }
+ }
+
+ [Fact]
+ public async Task Member_Should_Work()
+ {
+ await CreateTestTimelines();
+
+ const string getUrl = "timelines/t1";
+ using var client = await CreateClientAsUser();
+
+ async Task AssertMembers(IList<UserInfo> members)
+ {
+ var res = await client.GetAsync(getUrl);
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>()
+ .Which.Members.Should().NotBeNull().And.BeEquivalentTo(members);
+ }
+
+ async Task AssertEmptyMembers()
+ {
+ var res = await client.GetAsync(getUrl);
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>()
+ .Which.Members.Should().NotBeNull().And.BeEmpty();
+ }
+
+ await AssertEmptyMembers();
+ {
+ var res = await client.PutAsync("/timelines/t1/members/usernotexist", null);
+ res.Should().HaveStatusCode(400)
+ .And.HaveCommonBody(ErrorCodes.TimelineCommon.MemberPut_NotExist);
+ }
+ await AssertEmptyMembers();
+ {
+ var res = await client.PutAsync("/timelines/t1/members/user2", null);
+ res.Should().HaveStatusCode(200);
+ }
+ await AssertMembers(new List<UserInfo> { UserInfos[2] });
+ {
+ var res = await client.DeleteAsync("/timelines/t1/members/user2");
+ res.Should().BeDelete(true);
+ }
+ await AssertEmptyMembers();
+ {
+ var res = await client.DeleteAsync("/timelines/t1/members/users2");
+ res.Should().BeDelete(false);
+ }
+ await AssertEmptyMembers();
+ }
+
+ [Theory]
+ [InlineData(-1, 200, 401, 401, 401, 401)]
+ [InlineData(1, 200, 200, 403, 200, 403)]
+ [InlineData(0, 200, 200, 200, 200, 200)]
+ public async Task Permission_Timeline(int userNumber, int get, int opPatchUser, int opPatchAdmin, int opMemberUser, int opMemberAdmin)
+ {
+ await CreateTestTimelines();
+
+ using var client = await CreateClientAs(userNumber);
+ {
+ var res = await client.GetAsync("timelines/t1");
+ res.Should().HaveStatusCode(get);
+ }
+
+ {
+ var res = await client.PatchAsJsonAsync("timelines/t1", new TimelinePatchRequest { Description = "hahaha" });
+ res.Should().HaveStatusCode(opPatchUser);
+ }
+
+ {
+ var res = await client.PatchAsJsonAsync("timelines/t0", new TimelinePatchRequest { Description = "hahaha" });
+ res.Should().HaveStatusCode(opPatchAdmin);
+ }
+
+ {
+ var res = await client.PutAsync("timelines/t1/members/user2", null);
+ res.Should().HaveStatusCode(opMemberUser);
+ }
+
+ {
+ var res = await client.DeleteAsync("timelines/t1/members/user2");
+ res.Should().HaveStatusCode(opMemberUser);
+ }
+
+ {
+ var res = await client.PutAsync("timelines/t0/members/user2", null);
+ res.Should().HaveStatusCode(opMemberAdmin);
+ }
+
+ {
+ var res = await client.DeleteAsync("timelines/t0/members/user2");
+ res.Should().HaveStatusCode(opMemberAdmin);
+ }
+ }
+
+ [Fact]
+ public async Task Visibility_Test()
+ {
+ await CreateTestTimelines();
+
+ const string userUrl = "timelines/t1/posts";
+ const string adminUrl = "timelines/t0/posts";
+ {
+
+ using var client = await CreateClientAsUser();
+ using var content = new StringContent(@"{""visibility"":""abcdefg""}", System.Text.Encoding.UTF8, System.Net.Mime.MediaTypeNames.Application.Json);
+ var res = await client.PatchAsync("timelines/t1", content);
+ res.Should().BeInvalidModel();
+ }
+ { // default visibility is registered
+ {
+ using var client = await CreateDefaultClient();
+ var res = await client.GetAsync(userUrl);
+ res.Should().HaveStatusCode(403);
+ }
+
+ {
+ using var client = await CreateClientAsUser();
+ var res = await client.GetAsync(adminUrl);
+ res.Should().HaveStatusCode(200);
+ }
+ }
+
+ { // change visibility to public
+ {
+ using var client = await CreateClientAsUser();
+ var res = await client.PatchAsJsonAsync("timelines/t1",
+ new TimelinePatchRequest { Visibility = TimelineVisibility.Public });
+ res.Should().HaveStatusCode(200);
+ }
+ {
+ using var client = await CreateDefaultClient();
+ var res = await client.GetAsync(userUrl);
+ res.Should().HaveStatusCode(200);
+ }
+ }
+
+ { // change visibility to private
+ {
+ using var client = await CreateClientAsAdministrator();
+ {
+ var res = await client.PatchAsJsonAsync("timelines/t1",
+ new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
+ res.Should().HaveStatusCode(200);
+ }
+ {
+ var res = await client.PatchAsJsonAsync("timelines/t0",
+ new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
+ res.Should().HaveStatusCode(200);
+ }
+ }
+ {
+ using var client = await CreateDefaultClient();
+ var res = await client.GetAsync(userUrl);
+ res.Should().HaveStatusCode(403);
+ }
+ { // user can't read admin's
+ using var client = await CreateClientAsUser();
+ var res = await client.GetAsync(adminUrl);
+ res.Should().HaveStatusCode(403);
+ }
+ { // admin can read user's
+ using var client = await CreateClientAsAdministrator();
+ var res = await client.GetAsync(userUrl);
+ res.Should().HaveStatusCode(200);
+ }
+ { // add member
+ using var client = await CreateClientAsAdministrator();
+ var res = await client.PutAsync("/timelines/t0/members/user1", null);
+ res.Should().HaveStatusCode(200);
+ }
+ { // now user can read admin's
+ using var client = await CreateClientAsUser();
+ var res = await client.GetAsync(adminUrl);
+ res.Should().HaveStatusCode(200);
+ }
+ }
+ }
+
+
+ [Fact]
+ public async Task Permission_Post_Create()
+ {
+ await CreateTestTimelines();
+
+ using (var client = await CreateClientAsUser())
+ {
+ var res = await client.PutAsync("timelines/t1/members/user2", null);
+ res.Should().HaveStatusCode(200);
+ }
+
+ using (var client = await CreateDefaultClient())
+ {
+ { // no auth should get 401
+ var res = await client.PostAsJsonAsync("timelines/t1/posts",
+ new TimelinePostCreateRequest { Content = "aaa" });
+ res.Should().HaveStatusCode(401);
+ }
+ }
+
+ using (var client = await CreateClientAsUser())
+ {
+ { // post self's
+ var res = await client.PostAsJsonAsync("timelines/t1/posts",
+ new TimelinePostCreateRequest { Content = "aaa" });
+ res.Should().HaveStatusCode(200);
+ }
+ { // post other not as a member should get 403
+ var res = await client.PostAsJsonAsync("timelines/t0/posts",
+ new TimelinePostCreateRequest { Content = "aaa" });
+ res.Should().HaveStatusCode(403);
+ }
+ }
+
+ using (var client = await CreateClientAsAdministrator())
+ {
+ { // post as admin
+ var res = await client.PostAsJsonAsync("timelines/t1/posts",
+ new TimelinePostCreateRequest { Content = "aaa" });
+ res.Should().HaveStatusCode(200);
+ }
+ }
+
+ using (var client = await CreateClientAs(2))
+ {
+ { // post as member
+ var res = await client.PostAsJsonAsync("timelines/t1/posts",
+ new TimelinePostCreateRequest { Content = "aaa" });
+ res.Should().HaveStatusCode(200);
+ }
+ }
+ }
+
+ [Fact]
+ public async Task Permission_Post_Delete()
+ {
+ await CreateTestTimelines();
+
+ async Task<long> CreatePost(int userNumber)
+ {
+ using var client = await CreateClientAs(userNumber);
+ var res = await client.PostAsJsonAsync($"timelines/t1/posts",
+ new TimelinePostCreateRequest { Content = "aaa" });
+ return res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelinePostInfo>()
+ .Which.Id;
+ }
+
+ using (var client = await CreateClientAsUser())
+ {
+ {
+ var res = await client.PutAsync("timelines/t1/members/user2", null);
+ res.Should().HaveStatusCode(200);
+ }
+ {
+ var res = await client.PutAsync("timelines/t1/members/user3", null);
+ res.Should().HaveStatusCode(200);
+ }
+ }
+
+ { // no auth should get 401
+ using var client = await CreateDefaultClient();
+ var res = await client.DeleteAsync("timelines/t1/posts/12");
+ res.Should().HaveStatusCode(401);
+ }
+
+ { // self can delete self
+ var postId = await CreatePost(1);
+ using var client = await CreateClientAsUser();
+ var res = await client.DeleteAsync($"timelines/t1/posts/{postId}");
+ res.Should().HaveStatusCode(200);
+ }
+
+ { // admin can delete any
+ var postId = await CreatePost(1);
+ using var client = await CreateClientAsAdministrator();
+ var res = await client.DeleteAsync($"timelines/t1/posts/{postId}");
+ res.Should().HaveStatusCode(200);
+ }
+
+ { // owner can delete other
+ var postId = await CreatePost(2);
+ using var client = await CreateClientAsUser();
+ var res = await client.DeleteAsync($"timelines/t1/posts/{postId}");
+ res.Should().HaveStatusCode(200);
+ }
+
+ { // author can delete self
+ var postId = await CreatePost(2);
+ using var client = await CreateClientAs(2);
+ var res = await client.DeleteAsync($"timelines/t1/posts/{postId}");
+ res.Should().HaveStatusCode(200);
+ }
+
+ { // otherwise is forbidden
+ var postId = await CreatePost(2);
+ using var client = await CreateClientAs(3);
+ var res = await client.DeleteAsync($"timelines/t1/posts/{postId}");
+ res.Should().HaveStatusCode(403);
+ }
+ }
+
+ [Fact]
+ public async Task Post_Op_Should_Work()
+ {
+ await CreateTestTimelines();
+
+ {
+ using var client = await CreateClientAsUser();
+ {
+ var res = await client.GetAsync("timelines/t1/posts");
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelinePostInfo[]>()
+ .Which.Should().NotBeNull().And.BeEmpty();
+ }
+ {
+ var res = await client.PostAsJsonAsync("timelines/t1/posts",
+ new TimelinePostCreateRequest { Content = null });
+ res.Should().BeInvalidModel();
+ }
+ const string mockContent = "aaa";
+ TimelinePostInfo createRes;
+ {
+ var res = await client.PostAsJsonAsync("timelines/t1/posts",
+ new TimelinePostCreateRequest { Content = mockContent });
+ var body = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelinePostInfo>()
+ .Which;
+ body.Should().NotBeNull();
+ body.Content.Should().Be(mockContent);
+ body.Author.Should().BeEquivalentTo(UserInfos[1]);
+ createRes = body;
+ }
+ {
+ var res = await client.GetAsync("timelines/t1/posts");
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelinePostInfo[]>()
+ .Which.Should().NotBeNull().And.BeEquivalentTo(createRes);
+ }
+ const string mockContent2 = "bbb";
+ var mockTime2 = DateTime.Now.AddDays(-1);
+ TimelinePostInfo createRes2;
+ {
+ var res = await client.PostAsJsonAsync("timelines/t1/posts",
+ new TimelinePostCreateRequest { Content = mockContent2, Time = mockTime2 });
+ var body = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelinePostInfo>()
+ .Which;
+ body.Should().NotBeNull();
+ body.Content.Should().Be(mockContent2);
+ body.Author.Should().BeEquivalentTo(UserInfos[1]);
+ body.Time.Should().BeCloseTo(mockTime2, 1000);
+ createRes2 = body;
+ }
+ {
+ var res = await client.GetAsync("timelines/t1/posts");
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelinePostInfo[]>()
+ .Which.Should().NotBeNull().And.BeEquivalentTo(createRes, createRes2);
+ }
+ {
+ var res = await client.DeleteAsync($"timelines/t1/posts/{createRes.Id}");
+ res.Should().BeDelete(true);
+ }
+ {
+ var res = await client.DeleteAsync("timelines/t1/posts/30000");
+ res.Should().BeDelete(false);
+ }
+ {
+ var res = await client.GetAsync("timelines/t1/posts");
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelinePostInfo[]>()
+ .Which.Should().NotBeNull().And.BeEquivalentTo(createRes2);
+ }
+ }
+ }
+
+ [Fact]
+ public async Task GetPost_Should_Ordered()
+ {
+ await CreateTestTimelines();
+
+ using var client = await CreateClientAsUser();
+
+ async Task<long> CreatePost(DateTime time)
+ {
+ var res = await client.PostAsJsonAsync("timelines/t1/posts",
+ new TimelinePostCreateRequest { Content = "aaa", Time = time });
+ return res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelinePostInfo>()
+ .Which.Id;
+ }
+
+ var now = DateTime.Now;
+ var id0 = await CreatePost(now.AddDays(1));
+ var id1 = await CreatePost(now.AddDays(-1));
+ var id2 = await CreatePost(now);
+
+ {
+ var res = await client.GetAsync("timelines/t1/posts");
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelinePostInfo[]>()
+ .Which.Select(p => p.Id).Should().Equal(id1, id2, id0);
+ }
+ }
+ }
+}
|