From ac769e656b122ff569c3f1534701b71e00fed586 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 27 Oct 2020 19:21:35 +0800 Subject: Split front and back end. --- Timeline.Tests/IntegratedTests/TimelineTest.cs | 1523 ------------------------ 1 file changed, 1523 deletions(-) delete mode 100644 Timeline.Tests/IntegratedTests/TimelineTest.cs (limited to 'Timeline.Tests/IntegratedTests/TimelineTest.cs') diff --git a/Timeline.Tests/IntegratedTests/TimelineTest.cs b/Timeline.Tests/IntegratedTests/TimelineTest.cs deleted file mode 100644 index ec46b96a..00000000 --- a/Timeline.Tests/IntegratedTests/TimelineTest.cs +++ /dev/null @@ -1,1523 +0,0 @@ -using FluentAssertions; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats.Png; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using Timeline.Entities; -using Timeline.Models; -using Timeline.Models.Http; -using Timeline.Tests.Helpers; -using Xunit; - -namespace Timeline.Tests.IntegratedTests -{ - public static class TimelineHelper - { - public static TimelinePostContentInfo TextPostContent(string text) - { - return new TimelinePostContentInfo - { - Type = "text", - Text = text - }; - } - - public static TimelinePostCreateRequest TextPostCreateRequest(string text, DateTime? time = null) - { - return new TimelinePostCreateRequest - { - Content = new TimelinePostCreateRequestContent - { - Type = "text", - Text = text - }, - Time = time - }; - } - } - - public class TimelineTest : IntegratedTestBase - { - public TimelineTest() : base(3) - { - } - - protected override async Task OnInitializeAsync() - { - await CreateTestTimelines(); - } - - private List _testTimelines; - - private async Task CreateTestTimelines() - { - _testTimelines = new List(); - 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().Which; - _testTimelines.Add(timelineInfo); - } - } - - private static string CalculateUrlTail(string subpath, ICollection> query) - { - StringBuilder result = new StringBuilder(); - if (subpath != null) - { - if (!subpath.StartsWith("/", StringComparison.OrdinalIgnoreCase)) - result.Append('/'); - result.Append(subpath); - } - - if (query != null && query.Count != 0) - { - result.Append('?'); - foreach (var (key, value, index) in query.Select((pair, index) => (pair.Key, pair.Value, index))) - { - result.Append(WebUtility.UrlEncode(key)); - result.Append('='); - result.Append(WebUtility.UrlEncode(value)); - if (index != query.Count - 1) - result.Append('&'); - } - } - - return result.ToString(); - } - - private static string GeneratePersonalTimelineUrl(int id, string subpath = null, ICollection> query = null) - { - return $"timelines/@{(id == 0 ? "admin" : ("user" + id))}{CalculateUrlTail(subpath, query)}"; - } - - private static string GenerateOrdinaryTimelineUrl(int id, string subpath = null, ICollection> query = null) - { - return $"timelines/t{id}{CalculateUrlTail(subpath, query)}"; - } - - public delegate string TimelineUrlGenerator(int userId, string subpath = null, ICollection> query = null); - - public static IEnumerable TimelineUrlGeneratorData() - { - yield return new[] { new TimelineUrlGenerator(GeneratePersonalTimelineUrl) }; - yield return new[] { new TimelineUrlGenerator(GenerateOrdinaryTimelineUrl) }; - } - - private static string GeneratePersonalTimelineUrlByName(string name, string subpath = null) - { - return $"timelines/@{name}{(subpath == null ? "" : "/" + subpath)}"; - } - - private static string GenerateOrdinaryTimelineUrlByName(string name, string subpath = null) - { - return $"timelines/{name}{(subpath == null ? "" : "/" + subpath)}"; - } - - public static IEnumerable TimelineUrlByNameGeneratorData() - { - yield return new[] { new Func(GeneratePersonalTimelineUrlByName) }; - yield return new[] { new Func(GenerateOrdinaryTimelineUrlByName) }; - } - - [Fact] - public async Task TimelineGet_Should_Work() - { - using var client = await CreateDefaultClient(); - { - var res = await client.GetAsync("timelines/@user1"); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - body.Owner.Should().BeEquivalentTo(UserInfos[1]); - body.Visibility.Should().Be(TimelineVisibility.Register); - body.Description.Should().Be(""); - body.Members.Should().NotBeNull().And.BeEmpty(); - var links = body._links; - links.Should().NotBeNull(); - links.Self.Should().EndWith("timelines/@user1"); - links.Posts.Should().EndWith("timelines/@user1/posts"); - } - - { - var res = await client.GetAsync("timelines/t1"); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - body.Owner.Should().BeEquivalentTo(UserInfos[1]); - body.Visibility.Should().Be(TimelineVisibility.Register); - body.Description.Should().Be(""); - body.Members.Should().NotBeNull().And.BeEmpty(); - var links = body._links; - links.Should().NotBeNull(); - links.Self.Should().EndWith("timelines/t1"); - links.Posts.Should().EndWith("timelines/t1/posts"); - } - } - - [Fact] - public async Task TimelineList() - { - TimelineInfo user1Timeline; - - var client = await CreateDefaultClient(); - - { - var res = await client.GetAsync("timelines/@user1"); - user1Timeline = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - } - - { - var testResult = new List(); - testResult.Add(user1Timeline); - testResult.AddRange(_testTimelines); - - var res = await client.GetAsync("timelines"); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which.Should().BeEquivalentTo(testResult); - } - } - - [Fact] - public async Task TimelineList_WithQuery() - { - var testResultRelate = new List(); - var testResultOwn = new List(); - var testResultJoin = new List(); - var testResultOwnPrivate = new List(); - var testResultRelatePublic = new List(); - var testResultRelateRegister = new List(); - var testResultJoinPrivate = new List(); - var testResultPublic = new List(); - - { - var client = await CreateClientAsUser(); - - { - var res = await client.PutAsync("timelines/@user1/members/user3", null); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.PutAsync("timelines/t1/members/user3", null); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.PatchAsJsonAsync("timelines/@user1", new TimelinePatchRequest { Visibility = TimelineVisibility.Public }); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.PatchAsJsonAsync("timelines/t1", new TimelinePatchRequest { Visibility = TimelineVisibility.Register }); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.GetAsync("timelines/@user1"); - var timeline = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - testResultRelate.Add(timeline); - testResultJoin.Add(timeline); - testResultRelatePublic.Add(timeline); - testResultPublic.Add(timeline); - } - - { - var res = await client.GetAsync("timelines/t1"); - var timeline = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - testResultRelate.Add(timeline); - testResultJoin.Add(timeline); - testResultRelateRegister.Add(timeline); - } - } - - { - var client = await CreateClientAs(2); - - { - var res = await client.PutAsync("timelines/@user2/members/user3", null); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.PutAsync("timelines/t2/members/user3", null); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.PatchAsJsonAsync("timelines/@user2", new TimelinePatchRequest { Visibility = TimelineVisibility.Register }); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.PatchAsJsonAsync("timelines/t2", new TimelinePatchRequest { Visibility = TimelineVisibility.Private }); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.GetAsync("timelines/@user2"); - var timeline = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - testResultRelate.Add(timeline); - testResultJoin.Add(timeline); - testResultRelateRegister.Add(timeline); - } - - { - var res = await client.GetAsync("timelines/t2"); - var timeline = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - testResultRelate.Add(timeline); - testResultJoin.Add(timeline); - testResultJoinPrivate.Add(timeline); - } - } - - { - var client = await CreateClientAs(3); - - { - var res = await client.PatchAsJsonAsync("timelines/@user3", new TimelinePatchRequest { Visibility = TimelineVisibility.Private }); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.PatchAsJsonAsync("timelines/t3", new TimelinePatchRequest { Visibility = TimelineVisibility.Register }); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.GetAsync("timelines/@user3"); - var timeline = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - testResultRelate.Add(timeline); - testResultOwn.Add(timeline); - testResultOwnPrivate.Add(timeline); - } - - { - var res = await client.GetAsync("timelines/t3"); - var timeline = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - testResultRelate.Add(timeline); - testResultOwn.Add(timeline); - testResultRelateRegister.Add(timeline); - } - } - - { - var client = await CreateClientAs(3); - { - var res = await client.GetAsync("timelines?relate=user3"); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which; - body.Should().BeEquivalentTo(testResultRelate); - } - - { - var res = await client.GetAsync("timelines?relate=user3&relateType=own"); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which; - body.Should().BeEquivalentTo(testResultOwn); - } - - { - var res = await client.GetAsync("timelines?relate=user3&visibility=public"); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which; - body.Should().BeEquivalentTo(testResultRelatePublic); - } - - { - var res = await client.GetAsync("timelines?relate=user3&visibility=register"); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which; - body.Should().BeEquivalentTo(testResultRelateRegister); - } - - { - var res = await client.GetAsync("timelines?relate=user3&relateType=join&visibility=private"); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which; - body.Should().BeEquivalentTo(testResultJoinPrivate); - } - - { - var res = await client.GetAsync("timelines?relate=user3&relateType=own&visibility=private"); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which; - body.Should().BeEquivalentTo(testResultOwnPrivate); - } - } - - { - var client = await CreateDefaultClient(); - { - var res = await client.GetAsync("timelines?visibility=public"); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which; - body.Should().BeEquivalentTo(testResultPublic); - } - } - } - - [Fact] - public async Task TimelineList_InvalidModel() - { - var client = await CreateClientAsUser(); - - { - var res = await client.GetAsync("timelines?relate=us!!"); - res.Should().BeInvalidModel(); - } - - { - var res = await client.GetAsync("timelines?relateType=aaa"); - res.Should().BeInvalidModel(); - } - - { - var res = await client.GetAsync("timelines?visibility=aaa"); - res.Should().BeInvalidModel(); - } - } - - [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().Which; - } - - { - var res = await client.GetAsync("timelines/aaa"); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Should().BeEquivalentTo(timelineInfo); - } - - { - var res = await client.PostAsJsonAsync("timelines", new TimelineCreateRequest { Name = "aaa" }); - res.Should().HaveStatusCode(400) - .And.HaveCommonBody(ErrorCodes.TimelineController.NameConflict); - } - } - } - - [Fact] - public async Task TimelineDelete_Should_Work() - { - { - using var client = await CreateDefaultClient(); - var res = await client.DeleteAsync("timelines/t1"); - res.Should().HaveStatusCode(HttpStatusCode.Unauthorized); - } - - { - using var client = await CreateClientAs(2); - var res = await client.DeleteAsync("timelines/t1"); - res.Should().HaveStatusCode(HttpStatusCode.Forbidden); - } - - { - using var client = await CreateClientAsAdministrator(); - - { - var res = await client.DeleteAsync("timelines/!!!"); - res.Should().BeInvalidModel(); - } - - { - var res = await client.DeleteAsync("timelines/t2"); - res.Should().BeDelete(true); - } - - { - var res = await client.DeleteAsync("timelines/t2"); - res.Should().BeDelete(false); - } - } - - { - using var client = await CreateClientAs(1); - - { - var res = await client.DeleteAsync("timelines/!!!"); - res.Should().BeInvalidModel(); - } - - { - var res = await client.DeleteAsync("timelines/t1"); - res.Should().BeDelete(true); - } - - { - var res = await client.DeleteAsync("timelines/t1"); - res.Should().HaveStatusCode(400); - } - } - } - - [Theory] - [MemberData(nameof(TimelineUrlByNameGeneratorData))] - public async Task InvalidModel_BadName(Func generator) - { - using var client = await CreateClientAsAdministrator(); - { - var res = await client.GetAsync(generator("aaa!!!", null)); - res.Should().BeInvalidModel(); - } - { - var res = await client.PatchAsJsonAsync(generator("aaa!!!", null), new TimelinePatchRequest { }); - res.Should().BeInvalidModel(); - } - { - var res = await client.PutAsync(generator("aaa!!!", "members/user1"), null); - res.Should().BeInvalidModel(); - } - { - var res = await client.DeleteAsync(generator("aaa!!!", "members/user1")); - res.Should().BeInvalidModel(); - } - { - var res = await client.GetAsync(generator("aaa!!!", "posts")); - res.Should().BeInvalidModel(); - } - { - var res = await client.PostAsJsonAsync(generator("aaa!!!", "posts"), TimelineHelper.TextPostCreateRequest("aaa")); - res.Should().BeInvalidModel(); - } - { - var res = await client.DeleteAsync(generator("aaa!!!", "posts/123")); - res.Should().BeInvalidModel(); - } - { - var res = await client.GetAsync(generator("aaa!!!", "posts/123/data")); - res.Should().BeInvalidModel(); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlByNameGeneratorData))] - public async Task Ordinary_NotFound(Func generator) - { - var errorCode = generator == GenerateOrdinaryTimelineUrlByName ? ErrorCodes.TimelineController.NotExist : ErrorCodes.UserCommon.NotExist; - - using var client = await CreateClientAsAdministrator(); - { - var res = await client.GetAsync(generator("notexist", null)); - res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode); - } - { - var res = await client.PatchAsJsonAsync(generator("notexist", null), new TimelinePatchRequest { }); - res.Should().HaveStatusCode(400).And.HaveCommonBody(errorCode); - } - { - var res = await client.PutAsync(generator("notexist", "members/user1"), null); - res.Should().HaveStatusCode(400).And.HaveCommonBody(errorCode); - } - { - var res = await client.DeleteAsync(generator("notexist", "members/user1")); - res.Should().HaveStatusCode(400).And.HaveCommonBody(errorCode); - } - { - var res = await client.GetAsync(generator("notexist", "posts")); - res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode); - } - { - var res = await client.PostAsJsonAsync(generator("notexist", "posts"), TimelineHelper.TextPostCreateRequest("aaa")); - res.Should().HaveStatusCode(400).And.HaveCommonBody(errorCode); - } - { - var res = await client.DeleteAsync(generator("notexist", "posts/123")); - res.Should().HaveStatusCode(400).And.HaveCommonBody(errorCode); - } - { - var res = await client.GetAsync(generator("notexist", "posts/123/data")); - res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Description_Should_Work(TimelineUrlGenerator generator) - { - using var client = await CreateClientAsUser(); - - async Task AssertDescription(string description) - { - var res = await client.GetAsync(generator(1, null)); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Description.Should().Be(description); - } - - const string mockDescription = "haha"; - - await AssertDescription(""); - { - var res = await client.PatchAsJsonAsync(generator(1, null), - new TimelinePatchRequest { Description = mockDescription }); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which.Description.Should().Be(mockDescription); - await AssertDescription(mockDescription); - } - { - var res = await client.PatchAsJsonAsync(generator(1, null), - new TimelinePatchRequest { Description = null }); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which.Description.Should().Be(mockDescription); - await AssertDescription(mockDescription); - } - { - var res = await client.PatchAsJsonAsync(generator(1, null), - new TimelinePatchRequest { Description = "" }); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which.Description.Should().Be(""); - await AssertDescription(""); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Member_Should_Work(TimelineUrlGenerator generator) - { - var getUrl = generator(1, null); - using var client = await CreateClientAsUser(); - - async Task AssertMembers(IList members) - { - var res = await client.GetAsync(getUrl); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Members.Should().NotBeNull().And.BeEquivalentTo(members); - } - - async Task AssertEmptyMembers() - { - var res = await client.GetAsync(getUrl); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Members.Should().NotBeNull().And.BeEmpty(); - } - - await AssertEmptyMembers(); - { - var res = await client.PutAsync(generator(1, "members/usernotexist"), null); - res.Should().HaveStatusCode(400) - .And.HaveCommonBody(ErrorCodes.TimelineController.MemberPut_NotExist); - } - await AssertEmptyMembers(); - { - var res = await client.PutAsync(generator(1, "members/user2"), null); - res.Should().HaveStatusCode(200); - } - await AssertMembers(new List { UserInfos[2] }); - { - var res = await client.DeleteAsync(generator(1, "members/user2")); - res.Should().BeDelete(true); - } - await AssertEmptyMembers(); - { - var res = await client.DeleteAsync(generator(1, "members/aaa")); - res.Should().BeDelete(false); - } - await AssertEmptyMembers(); - } - - public static IEnumerable Permission_Timeline_Data() - { - yield return new object[] { new TimelineUrlGenerator(GenerateOrdinaryTimelineUrl), -1, 200, 401, 401, 401, 401 }; - yield return new object[] { new TimelineUrlGenerator(GenerateOrdinaryTimelineUrl), 1, 200, 200, 403, 200, 403 }; - yield return new object[] { new TimelineUrlGenerator(GenerateOrdinaryTimelineUrl), 0, 200, 200, 200, 200, 200 }; - yield return new object[] { new TimelineUrlGenerator(GeneratePersonalTimelineUrl), -1, 200, 401, 401, 401, 401 }; - yield return new object[] { new TimelineUrlGenerator(GeneratePersonalTimelineUrl), 1, 200, 200, 403, 200, 403 }; - yield return new object[] { new TimelineUrlGenerator(GeneratePersonalTimelineUrl), 0, 200, 200, 200, 200, 200 }; - } - - [Theory] - [MemberData(nameof(Permission_Timeline_Data))] - public async Task Permission_Timeline(TimelineUrlGenerator generator, int userNumber, int get, int opPatchUser, int opPatchAdmin, int opMemberUser, int opMemberAdmin) - { - using var client = await CreateClientAs(userNumber); - { - var res = await client.GetAsync("timelines/t1"); - res.Should().HaveStatusCode(get); - } - - { - var res = await client.PatchAsJsonAsync(generator(1, null), new TimelinePatchRequest { Description = "hahaha" }); - res.Should().HaveStatusCode(opPatchUser); - } - - { - var res = await client.PatchAsJsonAsync(generator(0, null), new TimelinePatchRequest { Description = "hahaha" }); - res.Should().HaveStatusCode(opPatchAdmin); - } - - { - var res = await client.PutAsync(generator(1, "members/user2"), null); - res.Should().HaveStatusCode(opMemberUser); - } - - { - var res = await client.DeleteAsync(generator(1, "members/user2")); - res.Should().HaveStatusCode(opMemberUser); - } - - { - var res = await client.PutAsync(generator(0, "members/user2"), null); - res.Should().HaveStatusCode(opMemberAdmin); - } - - { - var res = await client.DeleteAsync(generator(0, "members/user2")); - res.Should().HaveStatusCode(opMemberAdmin); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Visibility_Test(TimelineUrlGenerator generator) - { - var userUrl = generator(1, "posts"); - var adminUrl = generator(0, "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(generator(1, null), 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(generator(1, null), - 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(generator(1, null), - new TimelinePatchRequest { Visibility = TimelineVisibility.Private }); - res.Should().HaveStatusCode(200); - } - { - var res = await client.PatchAsJsonAsync(generator(0, null), - 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(generator(0, "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); - } - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Permission_Post_Create(TimelineUrlGenerator generator) - { - using (var client = await CreateClientAsUser()) - { - var res = await client.PutAsync(generator(1, "members/user2"), null); - res.Should().HaveStatusCode(200); - } - - using (var client = await CreateDefaultClient()) - { - { // no auth should get 401 - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest("aaa")); - res.Should().HaveStatusCode(401); - } - } - - using (var client = await CreateClientAsUser()) - { - { // post self's - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest("aaa")); - res.Should().HaveStatusCode(200); - } - { // post other not as a member should get 403 - var res = await client.PostAsJsonAsync(generator(0, "posts"), - TimelineHelper.TextPostCreateRequest("aaa")); - res.Should().HaveStatusCode(403); - } - } - - using (var client = await CreateClientAsAdministrator()) - { - { // post as admin - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest("aaa")); - res.Should().HaveStatusCode(200); - } - } - - using (var client = await CreateClientAs(2)) - { - { // post as member - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest("aaa")); - res.Should().HaveStatusCode(200); - } - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Permission_Post_Delete(TimelineUrlGenerator generator) - { - async Task CreatePost(int userNumber) - { - using var client = await CreateClientAs(userNumber); - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest("aaa")); - return res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Id; - } - - using (var client = await CreateClientAsUser()) - { - { - var res = await client.PutAsync(generator(1, "members/user2"), null); - res.Should().HaveStatusCode(200); - } - { - var res = await client.PutAsync(generator(1, "members/user3"), null); - res.Should().HaveStatusCode(200); - } - } - - { // no auth should get 401 - using var client = await CreateDefaultClient(); - var res = await client.DeleteAsync(generator(1, "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(generator(1, $"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(generator(1, $"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(generator(1, $"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(generator(1, $"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(generator(1, $"posts/{postId}")); - res.Should().HaveStatusCode(403); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task TextPost_ShouldWork(TimelineUrlGenerator generator) - { - { - using var client = await CreateClientAsUser(); - { - var res = await client.GetAsync(generator(1, "posts")); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Should().NotBeNull().And.BeEmpty(); - } - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest(null)); - res.Should().BeInvalidModel(); - } - const string mockContent = "aaa"; - TimelinePostInfo createRes; - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest(mockContent)); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which; - body.Should().NotBeNull(); - body.Content.Should().BeEquivalentTo(TimelineHelper.TextPostContent(mockContent)); - body.Author.Should().BeEquivalentTo(UserInfos[1]); - body.Deleted.Should().BeFalse(); - createRes = body; - } - { - var res = await client.GetAsync(generator(1, "posts")); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Should().NotBeNull().And.BeEquivalentTo(createRes); - } - const string mockContent2 = "bbb"; - var mockTime2 = DateTime.UtcNow.AddDays(-1); - TimelinePostInfo createRes2; - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest(mockContent2, mockTime2)); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which; - body.Should().NotBeNull(); - body.Content.Should().BeEquivalentTo(TimelineHelper.TextPostContent(mockContent2)); - body.Author.Should().BeEquivalentTo(UserInfos[1]); - body.Time.Should().BeCloseTo(mockTime2, 1000); - body.Deleted.Should().BeFalse(); - createRes2 = body; - } - { - var res = await client.GetAsync(generator(1, "posts")); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Should().NotBeNull().And.BeEquivalentTo(createRes, createRes2); - } - { - var res = await client.DeleteAsync(generator(1, $"posts/{createRes.Id}")); - res.Should().BeDelete(true); - } - { - var res = await client.DeleteAsync(generator(1, $"posts/{createRes.Id}")); - res.Should().BeDelete(false); - } - { - var res = await client.DeleteAsync(generator(1, "posts/30000")); - res.Should().BeDelete(false); - } - { - var res = await client.GetAsync(generator(1, "posts")); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Should().NotBeNull().And.BeEquivalentTo(createRes2); - } - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task GetPost_Should_Ordered(TimelineUrlGenerator generator) - { - using var client = await CreateClientAsUser(); - - async Task CreatePost(DateTime time) - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest("aaa", time)); - return res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Id; - } - - var now = DateTime.UtcNow; - var id0 = await CreatePost(now.AddDays(1)); - var id1 = await CreatePost(now.AddDays(-1)); - var id2 = await CreatePost(now); - - { - var res = await client.GetAsync(generator(1, "posts")); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Select(p => p.Id).Should().Equal(id1, id2, id0); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task CreatePost_InvalidModel(TimelineUrlGenerator generator) - { - using var client = await CreateClientAsUser(); - - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), new TimelinePostCreateRequest { Content = null }); - res.Should().BeInvalidModel(); - } - - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), new TimelinePostCreateRequest { Content = new TimelinePostCreateRequestContent { Type = null } }); - res.Should().BeInvalidModel(); - } - - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), new TimelinePostCreateRequest { Content = new TimelinePostCreateRequestContent { Type = "hahaha" } }); - res.Should().BeInvalidModel(); - } - - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), new TimelinePostCreateRequest { Content = new TimelinePostCreateRequestContent { Type = "text", Text = null } }); - res.Should().BeInvalidModel(); - } - - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), new TimelinePostCreateRequest { Content = new TimelinePostCreateRequestContent { Type = "image", Data = null } }); - res.Should().BeInvalidModel(); - } - - { - // image not base64 - var res = await client.PostAsJsonAsync(generator(1, "posts"), new TimelinePostCreateRequest { Content = new TimelinePostCreateRequestContent { Type = "image", Data = "!!!" } }); - res.Should().BeInvalidModel(); - } - - { - // image base64 not image - var res = await client.PostAsJsonAsync(generator(1, "posts"), new TimelinePostCreateRequest { Content = new TimelinePostCreateRequestContent { Type = "image", Data = Convert.ToBase64String(new byte[] { 0x01, 0x02, 0x03 }) } }); - res.Should().BeInvalidModel(); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task ImagePost_ShouldWork(TimelineUrlGenerator generator) - { - var imageData = ImageHelper.CreatePngWithSize(100, 200); - - long postId; - string postImageUrl; - - void AssertPostContent(TimelinePostContentInfo content) - { - content.Type.Should().Be(TimelinePostContentTypes.Image); - content.Url.Should().EndWith(generator(1, $"posts/{postId}/data")); - content.Text.Should().Be(null); - } - - using var client = await CreateClientAsUser(); - - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), - new TimelinePostCreateRequest - { - Content = new TimelinePostCreateRequestContent - { - Type = TimelinePostContentTypes.Image, - Data = Convert.ToBase64String(imageData) - } - }); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - postId = body.Id; - postImageUrl = body.Content.Url; - AssertPostContent(body.Content); - } - - { - var res = await client.GetAsync(generator(1, "posts")); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - body.Should().HaveCount(1); - var post = body[0]; - post.Id.Should().Be(postId); - AssertPostContent(post.Content); - } - - { - var res = await client.GetAsync(generator(1, $"posts/{postId}/data")); - res.Content.Headers.ContentType.MediaType.Should().Be("image/png"); - var data = await res.Content.ReadAsByteArrayAsync(); - var image = Image.Load(data, out var format); - image.Width.Should().Be(100); - image.Height.Should().Be(200); - format.Name.Should().Be(PngFormat.Instance.Name); - } - - { - await CacheTestHelper.TestCache(client, generator(1, $"posts/{postId}/data")); - } - - { - var res = await client.DeleteAsync(generator(1, $"posts/{postId}")); - res.Should().BeDelete(true); - } - - { - var res = await client.DeleteAsync(generator(1, $"posts/{postId}")); - res.Should().BeDelete(false); - } - - { - var res = await client.GetAsync(generator(1, "posts")); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Should().BeEmpty(); - } - - { - using var scope = TestApp.Host.Services.CreateScope(); - var database = scope.ServiceProvider.GetRequiredService(); - var count = await database.Data.CountAsync(); - count.Should().Be(0); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task ImagePost_400(TimelineUrlGenerator generator) - { - using var client = await CreateClientAsUser(); - - { - var res = await client.GetAsync(generator(1, "posts/11234/data")); - res.Should().HaveStatusCode(404) - .And.HaveCommonBody(ErrorCodes.TimelineController.PostNotExist); - } - - long postId; - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), - TimelineHelper.TextPostCreateRequest("aaa")); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which; - postId = body.Id; - } - - { - var res = await client.GetAsync(generator(1, $"posts/{postId}/data")); - res.Should().HaveStatusCode(400) - .And.HaveCommonBody(ErrorCodes.TimelineController.PostNoData); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Timeline_LastModified(TimelineUrlGenerator generator) - { - using var client = await CreateClientAsUser(); - - DateTime lastModified; - - { - var res = await client.GetAsync(generator(1)); - lastModified = res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.LastModified; - } - - await Task.Delay(1000); - - { - var res = await client.PatchAsJsonAsync(generator(1), new TimelinePatchRequest { Description = "123" }); - lastModified = res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.LastModified.Should().BeAfter(lastModified).And.Subject.Value; - } - - { - var res = await client.GetAsync(generator(1)); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.LastModified.Should().Be(lastModified); - } - - await Task.Delay(1000); - - { - var res = await client.PutAsync(generator(1, "members/user2"), null); - res.Should().HaveStatusCode(200); - } - - { - var res = await client.GetAsync(generator(1)); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.LastModified.Should().BeAfter(lastModified); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Post_ModifiedSince(TimelineUrlGenerator generator) - { - using var client = await CreateClientAsUser(); - - var postContentList = new List { "a", "b", "c", "d" }; - var posts = new List(); - - foreach (var content in postContentList) - { - var res = await client.PostAsJsonAsync(generator(1, "posts"), - new TimelinePostCreateRequest { Content = new TimelinePostCreateRequestContent { Text = content, Type = TimelinePostContentTypes.Text } }); - var post = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - posts.Add(post); - await Task.Delay(1000); - } - - { - var res = await client.DeleteAsync(generator(1, $"posts/{posts[2].Id}")); - res.Should().BeDelete(true); - } - - { - var res = await client.GetAsync(generator(1, "posts", - new Dictionary { { "modifiedSince", posts[1].LastUpdated.ToString("s", CultureInfo.InvariantCulture) } })); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which.Should().HaveCount(2) - .And.Subject.Select(p => p.Content.Text).Should().Equal("b", "d"); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task PostList_IncludeDeleted(TimelineUrlGenerator urlGenerator) - { - using var client = await CreateClientAsUser(); - - var postContentList = new List { "a", "b", "c", "d" }; - var posts = new List(); - - foreach (var content in postContentList) - { - var res = await client.PostAsJsonAsync(urlGenerator(1, "posts"), - new TimelinePostCreateRequest { Content = new TimelinePostCreateRequestContent { Text = content, Type = TimelinePostContentTypes.Text } }); - posts.Add(res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which); - } - - foreach (var id in new long[] { posts[0].Id, posts[2].Id }) - { - var res = await client.DeleteAsync(urlGenerator(1, $"posts/{id}")); - res.Should().BeDelete(true); - } - - { - var res = await client.GetAsync(urlGenerator(1, "posts", new Dictionary { ["includeDeleted"] = "true" })); - posts = res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which; - posts.Should().HaveCount(4); - posts.Select(p => p.Deleted).Should().Equal(true, false, true, false); - posts.Select(p => p.Content == null).Should().Equal(true, false, true, false); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Post_ModifiedSince_And_IncludeDeleted(TimelineUrlGenerator urlGenerator) - { - using var client = await CreateClientAsUser(); - - var postContentList = new List { "a", "b", "c", "d" }; - var posts = new List(); - - foreach (var (content, index) in postContentList.Select((v, i) => (v, i))) - { - var res = await client.PostAsJsonAsync(urlGenerator(1, "posts"), - new TimelinePostCreateRequest { Content = new TimelinePostCreateRequestContent { Text = content, Type = TimelinePostContentTypes.Text } }); - var post = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - posts.Add(post); - await Task.Delay(1000); - } - - { - var res = await client.DeleteAsync(urlGenerator(1, $"posts/{posts[2].Id}")); - res.Should().BeDelete(true); - } - - { - - var res = await client.GetAsync(urlGenerator(1, "posts", - new Dictionary { - { "modifiedSince", posts[1].LastUpdated.ToString("s", CultureInfo.InvariantCulture) }, - { "includeDeleted", "true" } - })); - posts = res.Should().HaveStatusCode(200) - .And.HaveJsonBody>() - .Which; - posts.Should().HaveCount(3); - posts.Select(p => p.Deleted).Should().Equal(false, true, false); - posts.Select(p => p.Content == null).Should().Equal(false, true, false); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Timeline_Get_IfModifiedSince_And_CheckUniqueId(TimelineUrlGenerator urlGenerator) - { - using var client = await CreateClientAsUser(); - - DateTime lastModifiedTime; - TimelineInfo timeline; - string uniqueId; - - { - var res = await client.GetAsync(urlGenerator(1)); - var body = res.Should().HaveStatusCode(200) - .And.HaveJsonBody().Which; - timeline = body; - lastModifiedTime = body.LastModified; - uniqueId = body.UniqueId; - } - - { - using var req = new HttpRequestMessage - { - RequestUri = new Uri(client.BaseAddress, urlGenerator(1)), - Method = HttpMethod.Get, - }; - req.Headers.IfModifiedSince = lastModifiedTime.AddSeconds(1); - var res = await client.SendAsync(req); - res.Should().HaveStatusCode(304); - } - - { - using var req = new HttpRequestMessage - { - RequestUri = new Uri(client.BaseAddress, urlGenerator(1)), - Method = HttpMethod.Get, - }; - req.Headers.IfModifiedSince = lastModifiedTime.AddSeconds(-1); - var res = await client.SendAsync(req); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Should().BeEquivalentTo(timeline); - } - - { - var res = await client.GetAsync(urlGenerator(1, null, - new Dictionary { { "ifModifiedSince", lastModifiedTime.AddSeconds(1).ToString("s", CultureInfo.InvariantCulture) } })); - res.Should().HaveStatusCode(304); - } - - { - var res = await client.GetAsync(urlGenerator(1, null, - new Dictionary { { "ifModifiedSince", lastModifiedTime.AddSeconds(-1).ToString("s", CultureInfo.InvariantCulture) } })); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Should().BeEquivalentTo(timeline); - } - - { - var res = await client.GetAsync(urlGenerator(1, null, - new Dictionary { { "ifModifiedSince", lastModifiedTime.AddSeconds(1).ToString("s", CultureInfo.InvariantCulture) }, - {"checkUniqueId", uniqueId } })); - res.Should().HaveStatusCode(304); - } - - { - var testUniqueId = (uniqueId[0] == 'a' ? "b" : "a") + uniqueId[1..]; - var res = await client.GetAsync(urlGenerator(1, null, - new Dictionary { { "ifModifiedSince", lastModifiedTime.AddSeconds(1).ToString("s", CultureInfo.InvariantCulture) }, - {"checkUniqueId", testUniqueId } })); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Should().BeEquivalentTo(timeline); - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task Title(TimelineUrlGenerator urlGenerator) - { - using var client = await CreateClientAsUser(); - - { - var res = await client.GetAsync(urlGenerator(1)); - var timeline = res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which; - timeline.Title.Should().Be(timeline.Name); - } - - { - var res = await client.PatchAsJsonAsync(urlGenerator(1), new TimelinePatchRequest { Title = "atitle" }); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Title.Should().Be("atitle"); - } - - { - var res = await client.GetAsync(urlGenerator(1)); - res.Should().HaveStatusCode(200) - .And.HaveJsonBody() - .Which.Title.Should().Be("atitle"); - } - } - - [Fact] - public async Task ChangeName() - { - { - using var client = await CreateDefaultClient(); - var res = await client.PostAsJsonAsync("timelineop/changename", new TimelineChangeNameRequest { OldName = "t1", NewName = "tttttttt" }); - res.Should().HaveStatusCode(401); - } - - { - using var client = await CreateClientAs(2); - var res = await client.PostAsJsonAsync("timelineop/changename", new TimelineChangeNameRequest { OldName = "t1", NewName = "tttttttt" }); - res.Should().HaveStatusCode(403); - } - - using (var client = await CreateClientAsUser()) - { - { - var res = await client.PostAsJsonAsync("timelineop/changename", new TimelineChangeNameRequest { OldName = "!!!", NewName = "tttttttt" }); - res.Should().BeInvalidModel(); - } - - { - var res = await client.PostAsJsonAsync("timelineop/changename", new TimelineChangeNameRequest { OldName = "ttt", NewName = "!!!!" }); - res.Should().BeInvalidModel(); - } - - { - var res = await client.PostAsJsonAsync("timelineop/changename", new TimelineChangeNameRequest { OldName = "ttttt", NewName = "tttttttt" }); - res.Should().HaveStatusCode(400).And.HaveCommonBody().Which.Code.Should().Be(ErrorCodes.TimelineController.NotExist); - } - - { - var res = await client.PostAsJsonAsync("timelineop/changename", new TimelineChangeNameRequest { OldName = "t1", NewName = "newt" }); - res.Should().HaveStatusCode(200).And.HaveJsonBody().Which.Name.Should().Be("newt"); - } - - { - var res = await client.GetAsync("timelines/t1"); - res.Should().HaveStatusCode(404); - } - - { - var res = await client.GetAsync("timelines/newt"); - res.Should().HaveStatusCode(200).And.HaveJsonBody().Which.Name.Should().Be("newt"); - } - } - } - - [Theory] - [MemberData(nameof(TimelineUrlGeneratorData))] - public async Task PostDataETag(TimelineUrlGenerator urlGenerator) - { - using var client = await CreateClientAsUser(); - - long id; - string etag; - - { - var res = await client.PostAsJsonAsync(urlGenerator(1, "posts"), new TimelinePostCreateRequest - { - Content = new TimelinePostCreateRequestContent - { - Type = TimelinePostContentTypes.Image, - Data = Convert.ToBase64String(ImageHelper.CreatePngWithSize(100, 50)) - } - }); - res.Should().HaveStatusCode(200); - var body = await res.ReadBodyAsJsonAsync(); - body.Content.ETag.Should().NotBeNullOrEmpty(); - - id = body.Id; - etag = body.Content.ETag; - } - - { - var res = await client.GetAsync(urlGenerator(1, $"posts/{id}/data")); - res.Should().HaveStatusCode(200); - res.Headers.ETag.Should().NotBeNull(); - res.Headers.ETag.ToString().Should().Be(etag); - } - } - } -} -- cgit v1.2.3