aboutsummaryrefslogtreecommitdiff
path: root/Timeline.Tests
diff options
context:
space:
mode:
Diffstat (limited to 'Timeline.Tests')
-rw-r--r--Timeline.Tests/Helpers/ResponseAssertions.cs8
-rw-r--r--Timeline.Tests/Helpers/TestApplication.cs31
-rw-r--r--Timeline.Tests/IntegratedTests/IntegratedTestBase.cs84
-rw-r--r--Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs532
-rw-r--r--Timeline.Tests/IntegratedTests/TimelineTest.cs532
-rw-r--r--Timeline.Tests/IntegratedTests/TokenTest.cs1
-rw-r--r--Timeline.Tests/Timeline.Tests.csproj2
7 files changed, 479 insertions, 711 deletions
diff --git a/Timeline.Tests/Helpers/ResponseAssertions.cs b/Timeline.Tests/Helpers/ResponseAssertions.cs
index f01a0677..024732f5 100644
--- a/Timeline.Tests/Helpers/ResponseAssertions.cs
+++ b/Timeline.Tests/Helpers/ResponseAssertions.cs
@@ -33,7 +33,9 @@ namespace Timeline.Tests.Helpers
try
{
- var body = res.Content.ReadAsStringAsync().Result;
+ var task = res.Content.ReadAsStringAsync();
+ task.Wait();
+ var body = task.Result;
if (body.Length > 40)
{
body = body[0..40] + " ...";
@@ -83,7 +85,9 @@ namespace Timeline.Tests.Helpers
string body;
try
{
- body = Subject.Content.ReadAsStringAsync().Result;
+ var task = Subject.Content.ReadAsStringAsync();
+ task.Wait();
+ body = task.Result;
}
catch (AggregateException e)
{
diff --git a/Timeline.Tests/Helpers/TestApplication.cs b/Timeline.Tests/Helpers/TestApplication.cs
index 52c2f2e2..11fe8f87 100644
--- a/Timeline.Tests/Helpers/TestApplication.cs
+++ b/Timeline.Tests/Helpers/TestApplication.cs
@@ -3,29 +3,35 @@ using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using System;
using System.Collections.Generic;
using System.IO;
+using System.Threading.Tasks;
using Timeline.Entities;
using Timeline.Migrations;
+using Xunit;
namespace Timeline.Tests.Helpers
{
- public class TestApplication : IDisposable
+ public class TestApplication : IAsyncLifetime
{
- public SqliteConnection DatabaseConnection { get; }
+ public SqliteConnection DatabaseConnection { get; private set; }
- public WebApplicationFactory<Startup> Factory { get; }
+ public WebApplicationFactory<Startup> Factory { get; private set; }
- public string WorkDir { get; }
+ public string WorkDir { get; private set; }
public TestApplication(WebApplicationFactory<Startup> factory)
{
+ Factory = factory;
+ }
+
+ public async Task InitializeAsync()
+ {
WorkDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(WorkDir);
DatabaseConnection = new SqliteConnection("Data Source=:memory:;");
- DatabaseConnection.Open();
+ await DatabaseConnection.OpenAsync();
var options = new DbContextOptionsBuilder<DatabaseContext>()
.UseSqlite(DatabaseConnection)
@@ -33,15 +39,15 @@ namespace Timeline.Tests.Helpers
using (var context = new DatabaseContext(options))
{
- context.Database.EnsureCreated();
+ await context.Database.EnsureCreatedAsync();
context.JwtToken.Add(new JwtTokenEntity
{
Key = JwtTokenGenerateHelper.GenerateKey()
});
- context.SaveChanges();
+ await context.SaveChangesAsync();
}
- Factory = factory.WithWebHostBuilder(builder =>
+ Factory = Factory.WithWebHostBuilder(builder =>
{
builder.ConfigureAppConfiguration((context, config) =>
{
@@ -60,11 +66,10 @@ namespace Timeline.Tests.Helpers
});
}
- public void Dispose()
+ public async Task DisposeAsync()
{
- DatabaseConnection.Close();
- DatabaseConnection.Dispose();
-
+ await DatabaseConnection.CloseAsync();
+ await DatabaseConnection.DisposeAsync();
Directory.Delete(WorkDir, true);
}
}
diff --git a/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs b/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs
index dfde2ea5..a4a7638c 100644
--- a/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs
+++ b/Timeline.Tests/IntegratedTests/IntegratedTestBase.cs
@@ -1,10 +1,13 @@
-using AutoMapper;
-using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Net.Http;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
+using Timeline.Models;
+using Timeline.Models.Converters;
using Timeline.Models.Http;
using Timeline.Services;
using Timeline.Tests.Helpers;
@@ -12,18 +15,16 @@ using Xunit;
namespace Timeline.Tests.IntegratedTests
{
- public abstract class IntegratedTestBase : IClassFixture<WebApplicationFactory<Startup>>, IDisposable
+ public abstract class IntegratedTestBase : IClassFixture<WebApplicationFactory<Startup>>, IAsyncLifetime
{
- static IntegratedTestBase()
- {
- FluentAssertions.AssertionOptions.AssertEquivalencyUsing(options =>
- options.Excluding(m => m.RuntimeType == typeof(UserInfoLinks)));
- }
-
protected TestApplication TestApp { get; }
protected WebApplicationFactory<Startup> Factory => TestApp.Factory;
+ public IReadOnlyList<UserInfo> UserInfos { get; private set; }
+
+ private readonly int _userCount;
+
public IntegratedTestBase(WebApplicationFactory<Startup> factory) : this(factory, 1)
{
@@ -34,8 +35,30 @@ namespace Timeline.Tests.IntegratedTests
if (userCount < 0)
throw new ArgumentOutOfRangeException(nameof(userCount), userCount, "User count can't be negative.");
- TestApp = new TestApplication(factory);
+ _userCount = userCount;
+ TestApp = new TestApplication(factory);
+ }
+
+ protected virtual Task OnInitializeAsync()
+ {
+ return Task.CompletedTask;
+ }
+
+ protected virtual Task OnDisposeAsync()
+ {
+ return Task.CompletedTask;
+ }
+
+ protected virtual void OnDispose()
+ {
+
+ }
+
+ public async Task InitializeAsync()
+ {
+ await TestApp.InitializeAsync();
+
using (var scope = Factory.Services.CreateScope())
{
var users = new List<User>()
@@ -49,7 +72,7 @@ namespace Timeline.Tests.IntegratedTests
}
};
- for (int i = 1; i <= userCount; i++)
+ for (int i = 1; i <= _userCount; i++)
{
users.Add(new User
{
@@ -63,30 +86,37 @@ namespace Timeline.Tests.IntegratedTests
var userInfoList = new List<UserInfo>();
var userService = scope.ServiceProvider.GetRequiredService<IUserService>();
- var mapper = scope.ServiceProvider.GetRequiredService<IMapper>();
+ foreach (var user in users)
+ {
+ await userService.CreateUser(user);
+ }
+ using var client = await CreateDefaultClient();
+ var options = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ };
+ options.Converters.Add(new JsonStringEnumConverter());
+ options.Converters.Add(new JsonDateTimeConverter());
foreach (var user in users)
{
- userService.CreateUser(user).Wait();
- userInfoList.Add(mapper.Map<UserInfo>(user));
+ var s = await client.GetStringAsync($"/users/{user.Username}");
+ userInfoList.Add(JsonSerializer.Deserialize<UserInfo>(s, options));
}
UserInfos = userInfoList;
}
+
+ await OnInitializeAsync();
+ }
+
+ public async Task DisposeAsync()
+ {
+ await OnDisposeAsync();
+ OnDispose();
+ await TestApp.DisposeAsync();
}
- protected virtual void OnDispose()
- {
- }
-
- public void Dispose()
- {
- OnDispose();
- TestApp.Dispose();
- }
-
- public IReadOnlyList<UserInfo> UserInfos { get; }
-
public Task<HttpClient> CreateDefaultClient()
{
return Task.FromResult(Factory.CreateDefaultClient());
@@ -121,6 +151,6 @@ namespace Timeline.Tests.IntegratedTests
public Task<HttpClient> CreateClientAsUser()
{
return CreateClientAs(1);
- }
+ }
}
}
diff --git a/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs b/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs
deleted file mode 100644
index 7d0a68e8..00000000
--- a/Timeline.Tests/IntegratedTests/PersonalTimelineTest.cs
+++ /dev/null
@@ -1,532 +0,0 @@
-using FluentAssertions;
-using Microsoft.AspNetCore.Mvc.Testing;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
-using Timeline.Models.Http;
-using Timeline.Tests.Helpers;
-using Xunit;
-
-namespace Timeline.Tests.IntegratedTests
-{
- public class PersonalTimelineTest : IntegratedTestBase
- {
- public PersonalTimelineTest(WebApplicationFactory<Startup> factory)
- : base(factory, 3)
- {
-
- }
-
- [Fact]
- public async Task TimelineGet_Should_Work()
- {
- using var client = await CreateDefaultClient();
- var res = await client.GetAsync("users/user1/timeline");
- var body = res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<TimelineInfo>().Which;
- body.Owner.Should().BeEquivalentTo(UserInfos[1]);
- body.Visibility.Should().Be(TimelineVisibility.Register);
- body.Description.Should().Be("");
- body.Members.Should().NotBeNull().And.BeEmpty();
- }
-
- [Fact]
- public async Task InvalidModel_BadUsername()
- {
- using var client = await CreateClientAsAdministrator();
- {
- var res = await client.GetAsync("users/user!!!/timeline");
- res.Should().BeInvalidModel();
- }
- {
- var res = await client.PatchAsJsonAsync("users/user!!!/timeline", new TimelinePatchRequest { });
- res.Should().BeInvalidModel();
- }
- {
- var res = await client.PutAsync("users/user!!!/timeline/members/user1", null);
- res.Should().BeInvalidModel();
- }
- {
- var res = await client.DeleteAsync("users/user!!!/timeline/members/user1");
- res.Should().BeInvalidModel();
- }
- {
- var res = await client.GetAsync("users/user!!!/timeline/posts");
- res.Should().BeInvalidModel();
- }
- {
- var res = await client.PostAsJsonAsync("users/user!!!/timeline/posts", new TimelinePostCreateRequest { Content = "aaa" });
- res.Should().BeInvalidModel();
- }
- {
- var res = await client.DeleteAsync("users/user!!!/timeline/posts/123");
- res.Should().BeInvalidModel();
- }
- }
-
- [Fact]
- public async Task NotFound()
- {
- using var client = await CreateClientAsAdministrator();
- {
- var res = await client.GetAsync("users/usernotexist/timeline");
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
- }
- {
- var res = await client.PatchAsJsonAsync("users/usernotexist/timeline", new TimelinePatchRequest { });
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
- }
- {
- var res = await client.PutAsync("users/usernotexist/timeline/members/user1", null);
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
- }
- {
- var res = await client.DeleteAsync("users/usernotexist/timeline/members/user1");
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
- }
- {
- var res = await client.GetAsync("users/usernotexist/timeline/posts");
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
- }
- {
- var res = await client.PostAsJsonAsync("users/usernotexist/timeline/posts", new TimelinePostCreateRequest { Content = "aaa" });
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
- }
- {
- var res = await client.DeleteAsync("users/usernotexist/timeline/posts/123");
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.UserCommon.NotExist);
- }
- }
-
- [Fact]
- public async Task Description_Should_Work()
- {
- using var client = await CreateClientAsUser();
-
- async Task AssertDescription(string description)
- {
- var res = await client.GetAsync("users/user1/timeline");
- 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("users/user1/timeline",
- new TimelinePatchRequest { Description = mockDescription });
- res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be(mockDescription);
- await AssertDescription(mockDescription);
- }
- {
- var res = await client.PatchAsJsonAsync("users/user1/timeline",
- new TimelinePatchRequest { Description = null });
- res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be(mockDescription);
- await AssertDescription(mockDescription);
- }
- {
- var res = await client.PatchAsJsonAsync("users/user1/timeline",
- new TimelinePatchRequest { Description = "" });
- res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be("");
- await AssertDescription("");
- }
- }
-
- [Fact]
- public async Task Member_Should_Work()
- {
- const string getUrl = "users/user1/timeline";
- 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("/users/user1/timeline/members/usernotexist", null);
- res.Should().HaveStatusCode(400)
- .And.HaveCommonBody(ErrorCodes.TimelineCommon.MemberPut_NotExist);
- }
- await AssertEmptyMembers();
- {
- var res = await client.PutAsync("/users/user1/timeline/members/user2", null);
- res.Should().HaveStatusCode(200);
- }
- await AssertMembers(new List<UserInfo> { UserInfos[2] });
- {
- var res = await client.DeleteAsync("/users/user1/timeline/members/user2");
- res.Should().BeDelete(true);
- }
- await AssertEmptyMembers();
- {
- var res = await client.DeleteAsync("/users/user1/timeline/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)
- {
- using var client = await CreateClientAs(userNumber);
- {
- var res = await client.GetAsync("users/user1/timeline");
- res.Should().HaveStatusCode(get);
- }
-
- {
- var res = await client.PatchAsJsonAsync("users/user1/timeline", new TimelinePatchRequest { Description = "hahaha" });
- res.Should().HaveStatusCode(opPatchUser);
- }
-
- {
- var res = await client.PatchAsJsonAsync("users/admin/timeline", new TimelinePatchRequest { Description = "hahaha" });
- res.Should().HaveStatusCode(opPatchAdmin);
- }
-
- {
- var res = await client.PutAsync("users/user1/timeline/members/user2", null);
- res.Should().HaveStatusCode(opMemberUser);
- }
-
- {
- var res = await client.DeleteAsync("users/user1/timeline/members/user2");
- res.Should().HaveStatusCode(opMemberUser);
- }
-
- {
- var res = await client.PutAsync("users/admin/timeline/members/user2", null);
- res.Should().HaveStatusCode(opMemberAdmin);
- }
-
- {
- var res = await client.DeleteAsync("users/admin/timeline/members/user2");
- res.Should().HaveStatusCode(opMemberAdmin);
- }
- }
-
- [Fact]
- public async Task Visibility_Test()
- {
- const string userUrl = "users/user1/timeline/posts";
- const string adminUrl = "users/admin/timeline/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("users/user1/timeline", 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("users/user1/timeline",
- 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("users/user1/timeline",
- new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
- res.Should().HaveStatusCode(200);
- }
- {
- var res = await client.PatchAsJsonAsync("users/admin/timeline",
- 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("/users/admin/timeline/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()
- {
- using (var client = await CreateClientAsUser())
- {
- var res = await client.PutAsync("users/user1/timeline/members/user2", null);
- res.Should().HaveStatusCode(200);
- }
-
- using (var client = await CreateDefaultClient())
- {
- { // no auth should get 401
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
- res.Should().HaveStatusCode(401);
- }
- }
-
- using (var client = await CreateClientAsUser())
- {
- { // post self's
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
- res.Should().HaveStatusCode(200);
- }
- { // post other not as a member should get 403
- var res = await client.PostAsJsonAsync("users/admin/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
- res.Should().HaveStatusCode(403);
- }
- }
-
- using (var client = await CreateClientAsAdministrator())
- {
- { // post as admin
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
- res.Should().HaveStatusCode(200);
- }
- }
-
- using (var client = await CreateClientAs(2))
- {
- { // post as member
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
- res.Should().HaveStatusCode(200);
- }
- }
- }
-
- [Fact]
- public async Task Permission_Post_Delete()
- {
- async Task<long> CreatePost(int userNumber)
- {
- using var client = await CreateClientAs(userNumber);
- var res = await client.PostAsJsonAsync($"users/user1/timeline/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("users/user1/timeline/members/user2", null);
- res.Should().HaveStatusCode(200);
- }
- {
- var res = await client.PutAsync("users/user1/timeline/members/user3", null);
- res.Should().HaveStatusCode(200);
- }
- }
-
- { // no auth should get 401
- using var client = await CreateDefaultClient();
- var res = await client.DeleteAsync("users/user1/timeline/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($"users/user1/timeline/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($"users/user1/timeline/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($"users/user1/timeline/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($"users/user1/timeline/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($"users/user1/timeline/posts/{postId}");
- res.Should().HaveStatusCode(403);
- }
- }
-
- [Fact]
- public async Task Post_Op_Should_Work()
- {
- {
- using var client = await CreateClientAsUser();
- {
- var res = await client.GetAsync("users/user1/timeline/posts");
- res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<TimelinePostInfo[]>()
- .Which.Should().NotBeNull().And.BeEmpty();
- }
- {
- var res = await client.PostAsJsonAsync("users/user1/timeline/posts",
- new TimelinePostCreateRequest { Content = null });
- res.Should().BeInvalidModel();
- }
- const string mockContent = "aaa";
- TimelinePostInfo createRes;
- {
- var res = await client.PostAsJsonAsync("users/user1/timeline/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("users/user1/timeline/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("users/user1/timeline/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("users/user1/timeline/posts");
- res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<TimelinePostInfo[]>()
- .Which.Should().NotBeNull().And.BeEquivalentTo(createRes, createRes2);
- }
- {
- var res = await client.DeleteAsync($"users/user1/timeline/posts/{createRes.Id}");
- res.Should().BeDelete(true);
- }
- {
- var res = await client.DeleteAsync("users/user1/timeline/posts/30000");
- res.Should().BeDelete(false);
- }
- {
- var res = await client.GetAsync("users/user1/timeline/posts");
- res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<TimelinePostInfo[]>()
- .Which.Should().NotBeNull().And.BeEquivalentTo(createRes2);
- }
- }
- }
-
- [Fact]
- public async Task GetPost_Should_Ordered()
- {
- using var client = await CreateClientAsUser();
-
- async Task<long> CreatePost(DateTime time)
- {
- var res = await client.PostAsJsonAsync("users/user1/timeline/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("users/user1/timeline/posts");
- res.Should().HaveStatusCode(200)
- .And.HaveJsonBody<TimelinePostInfo[]>()
- .Which.Select(p => p.Id).Should().Equal(id1, id2, id0);
- }
- }
- }
-}
diff --git a/Timeline.Tests/IntegratedTests/TimelineTest.cs b/Timeline.Tests/IntegratedTests/TimelineTest.cs
index 14a0a59e..682cfd7c 100644
--- a/Timeline.Tests/IntegratedTests/TimelineTest.cs
+++ b/Timeline.Tests/IntegratedTests/TimelineTest.cs
@@ -1,23 +1,58 @@
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Png;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
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(WebApplicationFactory<Startup> factory)
: base(factory, 3)
{
+ }
+ protected override async Task OnInitializeAsync()
+ {
+ await CreateTestTimelines();
}
private List<TimelineInfo> _testTimelines;
@@ -35,17 +70,80 @@ namespace Timeline.Tests.IntegratedTests
}
}
+ private static string GeneratePersonalTimelineUrl(int id, string subpath = null)
+ {
+ return $"timelines/@{(id == 0 ? "admin" : ("user" + id))}{(subpath == null ? "" : ("/" + subpath))}";
+ }
+
+ private static string GenerateOrdinaryTimelineUrl(int id, string subpath = null)
+ {
+ return $"timelines/t{id}{(subpath == null ? "" : ("/" + subpath))}";
+ }
+
+ public static IEnumerable<object[]> TimelineUrlGeneratorData()
+ {
+ yield return new[] { new Func<int, string, string>(GeneratePersonalTimelineUrl) };
+ yield return new[] { new Func<int, string, string>(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<object[]> TimelineUrlByNameGeneratorData()
+ {
+ yield return new[] { new Func<string, string, string>(GeneratePersonalTimelineUrlByName) };
+ yield return new[] { new Func<string, string, string>(GenerateOrdinaryTimelineUrlByName) };
+ }
+
[Fact]
- public async Task TimelineList()
+ public async Task TimelineGet_Should_Work()
{
- await CreateTestTimelines();
+ using var client = await CreateDefaultClient();
+ {
+ var res = await client.GetAsync("timelines/@user1");
+ var body = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().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<TimelineInfo>().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("/users/user1/timeline");
+ var res = await client.GetAsync("/timelines/@user1");
user1Timeline = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which;
}
@@ -65,8 +163,6 @@ namespace Timeline.Tests.IntegratedTests
[Fact]
public async Task TimelineList_WithQuery()
{
- await CreateTestTimelines();
-
var testResultRelate = new List<TimelineInfo>();
var testResultOwn = new List<TimelineInfo>();
var testResultJoin = new List<TimelineInfo>();
@@ -80,7 +176,7 @@ namespace Timeline.Tests.IntegratedTests
var client = await CreateClientAsUser();
{
- var res = await client.PutAsync("/users/user1/timeline/members/user3", null);
+ var res = await client.PutAsync("/timelines/@user1/members/user3", null);
res.Should().HaveStatusCode(200);
}
@@ -90,7 +186,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.PatchAsJsonAsync("/users/user1/timeline", new TimelinePatchRequest { Visibility = TimelineVisibility.Public });
+ var res = await client.PatchAsJsonAsync("/timelines/@user1", new TimelinePatchRequest { Visibility = TimelineVisibility.Public });
res.Should().HaveStatusCode(200);
}
@@ -100,7 +196,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.GetAsync("/users/user1/timeline");
+ var res = await client.GetAsync("/timelines/@user1");
var timeline = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which;
testResultRelate.Add(timeline);
@@ -123,7 +219,7 @@ namespace Timeline.Tests.IntegratedTests
var client = await CreateClientAs(2);
{
- var res = await client.PutAsync("/users/user2/timeline/members/user3", null);
+ var res = await client.PutAsync("/timelines/@user2/members/user3", null);
res.Should().HaveStatusCode(200);
}
@@ -133,7 +229,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.PatchAsJsonAsync("/users/user2/timeline", new TimelinePatchRequest { Visibility = TimelineVisibility.Register });
+ var res = await client.PatchAsJsonAsync("/timelines/@user2", new TimelinePatchRequest { Visibility = TimelineVisibility.Register });
res.Should().HaveStatusCode(200);
}
@@ -143,7 +239,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.GetAsync("/users/user2/timeline");
+ var res = await client.GetAsync("/timelines/@user2");
var timeline = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which;
testResultRelate.Add(timeline);
@@ -165,7 +261,7 @@ namespace Timeline.Tests.IntegratedTests
var client = await CreateClientAs(3);
{
- var res = await client.PatchAsJsonAsync("/users/user3/timeline", new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
+ var res = await client.PatchAsJsonAsync("/timelines/@user3", new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
res.Should().HaveStatusCode(200);
}
@@ -175,7 +271,7 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.GetAsync("/users/user3/timeline");
+ var res = await client.GetAsync("/timelines/@user3");
var timeline = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which;
testResultRelate.Add(timeline);
@@ -310,7 +406,7 @@ namespace Timeline.Tests.IntegratedTests
{
var res = await client.PostAsJsonAsync("timelines", new TimelineCreateRequest { Name = "aaa" });
res.Should().HaveStatusCode(400)
- .And.HaveCommonBody(ErrorCodes.TimelineCommon.NameConflict);
+ .And.HaveCommonBody(ErrorCodes.TimelineController.NameConflict);
}
}
}
@@ -318,8 +414,6 @@ namespace Timeline.Tests.IntegratedTests
[Fact]
public async Task TimelineDelete_Should_Work()
{
- await CreateTestTimelines();
-
{
using var client = await CreateDefaultClient();
var res = await client.DeleteAsync("timelines/t1");
@@ -371,84 +465,95 @@ namespace Timeline.Tests.IntegratedTests
}
}
- [Fact]
- public async Task InvalidModel_BadName()
+ [Theory]
+ [MemberData(nameof(TimelineUrlByNameGeneratorData))]
+ public async Task InvalidModel_BadName(Func<string, string, string> generator)
{
using var client = await CreateClientAsAdministrator();
{
- var res = await client.GetAsync("timelines/aaa!!!");
+ 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.PatchAsJsonAsync("timelines/aaa!!!", new TimelinePatchRequest { });
+ var res = await client.PutAsync(generator("aaa!!!", "members/user1"), null);
res.Should().BeInvalidModel();
}
{
- var res = await client.PutAsync("timelines/aaa!!!/members/user1", null);
+ var res = await client.DeleteAsync(generator("aaa!!!/members", "user1"));
res.Should().BeInvalidModel();
}
{
- var res = await client.DeleteAsync("timelines/aaa!!!/members/user1");
+ var res = await client.GetAsync(generator("aaa!!!", "posts"));
res.Should().BeInvalidModel();
}
{
- var res = await client.GetAsync("timelines/aaa!!!/posts");
+ var res = await client.PostAsJsonAsync(generator("aaa!!!", "posts"), TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().BeInvalidModel();
}
{
- var res = await client.PostAsJsonAsync("timelines/aaa!!!/posts", new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.DeleteAsync(generator("aaa!!!", "posts/123"));
res.Should().BeInvalidModel();
}
{
- var res = await client.DeleteAsync("timelines/aaa!!!/posts/123");
+ var res = await client.GetAsync(generator("aaa!!!", "posts/123/data"));
res.Should().BeInvalidModel();
}
}
- [Fact]
- public async Task NotFound()
+ [Theory]
+ [MemberData(nameof(TimelineUrlByNameGeneratorData))]
+ public async Task Ordinary_NotFound(Func<string, string, string> generator)
{
+ var errorCode = generator == GenerateOrdinaryTimelineUrlByName ? ErrorCodes.TimelineController.NotExist : ErrorCodes.UserCommon.NotExist;
+
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.GetAsync(generator("notexist", null));
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode);
+ }
+ {
+ var res = await client.PatchAsJsonAsync(generator("notexist", null), new TimelinePatchRequest { });
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode);
}
{
- var res = await client.PatchAsJsonAsync("timelines/notexist", new TimelinePatchRequest { });
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ var res = await client.PutAsync(generator("notexist", "members/user1"), null);
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode);
}
{
- var res = await client.PutAsync("timelines/notexist/members/user1", null);
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ var res = await client.DeleteAsync(generator("notexist", "members/user1"));
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode);
}
{
- var res = await client.DeleteAsync("timelines/notexist/members/user1");
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ var res = await client.GetAsync(generator("notexist", "posts"));
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode);
}
{
- var res = await client.GetAsync("timelines/notexist/posts");
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ var res = await client.PostAsJsonAsync(generator("notexist", "posts"), TimelineHelper.TextPostCreateRequest("aaa"));
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode);
}
{
- 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(generator("notexist", "posts/123"));
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode);
}
{
- var res = await client.DeleteAsync("timelines/notexist/posts/123");
- res.Should().HaveStatusCode(404).And.HaveCommonBody(ErrorCodes.TimelineCommon.NotExist);
+ var res = await client.GetAsync(generator("notexist", "posts/123/data"));
+ res.Should().HaveStatusCode(404).And.HaveCommonBody(errorCode);
}
}
- [Fact]
- public async Task Description_Should_Work()
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task Description_Should_Work(Func<int, string, string> generator)
{
- await CreateTestTimelines();
-
using var client = await CreateClientAsUser();
async Task AssertDescription(string description)
{
- var res = await client.GetAsync("timelines/t1");
+ var res = await client.GetAsync(generator(1, null));
var body = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>()
.Which.Description.Should().Be(description);
@@ -458,21 +563,21 @@ namespace Timeline.Tests.IntegratedTests
await AssertDescription("");
{
- var res = await client.PatchAsJsonAsync("timelines/t1",
+ var res = await client.PatchAsJsonAsync(generator(1, null),
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",
+ var res = await client.PatchAsJsonAsync(generator(1, null),
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",
+ var res = await client.PatchAsJsonAsync(generator(1, null),
new TimelinePatchRequest { Description = "" });
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelineInfo>().Which.Description.Should().Be("");
@@ -480,12 +585,11 @@ namespace Timeline.Tests.IntegratedTests
}
}
- [Fact]
- public async Task Member_Should_Work()
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task Member_Should_Work(Func<int, string, string> generator)
{
- await CreateTestTimelines();
-
- const string getUrl = "timelines/t1";
+ var getUrl = generator(1, null);
using var client = await CreateClientAsUser();
async Task AssertMembers(IList<UserInfo> members)
@@ -506,35 +610,40 @@ namespace Timeline.Tests.IntegratedTests
await AssertEmptyMembers();
{
- var res = await client.PutAsync("/timelines/t1/members/usernotexist", null);
+ var res = await client.PutAsync(generator(1, "members/usernotexist"), null);
res.Should().HaveStatusCode(400)
- .And.HaveCommonBody(ErrorCodes.TimelineCommon.MemberPut_NotExist);
+ .And.HaveCommonBody(ErrorCodes.TimelineController.MemberPut_NotExist);
}
await AssertEmptyMembers();
{
- var res = await client.PutAsync("/timelines/t1/members/user2", null);
+ var res = await client.PutAsync(generator(1, "members/user2"), null);
res.Should().HaveStatusCode(200);
}
await AssertMembers(new List<UserInfo> { UserInfos[2] });
{
- var res = await client.DeleteAsync("/timelines/t1/members/user2");
+ var res = await client.DeleteAsync(generator(1, "members/user2"));
res.Should().BeDelete(true);
}
await AssertEmptyMembers();
{
- var res = await client.DeleteAsync("/timelines/t1/members/users2");
+ var res = await client.DeleteAsync(generator(1, "members/aaa"));
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)
+ [InlineData(nameof(GenerateOrdinaryTimelineUrl), -1, 200, 401, 401, 401, 401)]
+ [InlineData(nameof(GenerateOrdinaryTimelineUrl), 1, 200, 200, 403, 200, 403)]
+ [InlineData(nameof(GenerateOrdinaryTimelineUrl), 0, 200, 200, 200, 200, 200)]
+ [InlineData(nameof(GeneratePersonalTimelineUrl), -1, 200, 401, 401, 401, 401)]
+ [InlineData(nameof(GeneratePersonalTimelineUrl), 1, 200, 200, 403, 200, 403)]
+ [InlineData(nameof(GeneratePersonalTimelineUrl), 0, 200, 200, 200, 200, 200)]
+
+ public async Task Permission_Timeline(string generatorName, int userNumber, int get, int opPatchUser, int opPatchAdmin, int opMemberUser, int opMemberAdmin)
{
- await CreateTestTimelines();
+ var method = GetType().GetMethod(generatorName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
+ Func<int, string, string> generator = (int id, string subpath) => (string)method.Invoke(null, new object[] { id, subpath });
using var client = await CreateClientAs(userNumber);
{
@@ -543,48 +652,47 @@ namespace Timeline.Tests.IntegratedTests
}
{
- var res = await client.PatchAsJsonAsync("timelines/t1", new TimelinePatchRequest { Description = "hahaha" });
+ var res = await client.PatchAsJsonAsync(generator(1, null), new TimelinePatchRequest { Description = "hahaha" });
res.Should().HaveStatusCode(opPatchUser);
}
{
- var res = await client.PatchAsJsonAsync("timelines/t0", new TimelinePatchRequest { Description = "hahaha" });
+ var res = await client.PatchAsJsonAsync(generator(0, null), new TimelinePatchRequest { Description = "hahaha" });
res.Should().HaveStatusCode(opPatchAdmin);
}
{
- var res = await client.PutAsync("timelines/t1/members/user2", null);
+ var res = await client.PutAsync(generator(1, "members/user2"), null);
res.Should().HaveStatusCode(opMemberUser);
}
{
- var res = await client.DeleteAsync("timelines/t1/members/user2");
+ var res = await client.DeleteAsync(generator(1, "members/user2"));
res.Should().HaveStatusCode(opMemberUser);
}
{
- var res = await client.PutAsync("timelines/t0/members/user2", null);
+ var res = await client.PutAsync(generator(0, "members/user2"), null);
res.Should().HaveStatusCode(opMemberAdmin);
}
{
- var res = await client.DeleteAsync("timelines/t0/members/user2");
+ var res = await client.DeleteAsync(generator(0, "members/user2"));
res.Should().HaveStatusCode(opMemberAdmin);
}
}
- [Fact]
- public async Task Visibility_Test()
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task Visibility_Test(Func<int, string, string> generator)
{
- await CreateTestTimelines();
-
- const string userUrl = "timelines/t1/posts";
- const string adminUrl = "timelines/t0/posts";
+ 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("timelines/t1", content);
+ var res = await client.PatchAsync(generator(1, null), content);
res.Should().BeInvalidModel();
}
{ // default visibility is registered
@@ -604,7 +712,7 @@ namespace Timeline.Tests.IntegratedTests
{ // change visibility to public
{
using var client = await CreateClientAsUser();
- var res = await client.PatchAsJsonAsync("timelines/t1",
+ var res = await client.PatchAsJsonAsync(generator(1, null),
new TimelinePatchRequest { Visibility = TimelineVisibility.Public });
res.Should().HaveStatusCode(200);
}
@@ -619,12 +727,12 @@ namespace Timeline.Tests.IntegratedTests
{
using var client = await CreateClientAsAdministrator();
{
- var res = await client.PatchAsJsonAsync("timelines/t1",
+ var res = await client.PatchAsJsonAsync(generator(1, null),
new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
res.Should().HaveStatusCode(200);
}
{
- var res = await client.PatchAsJsonAsync("timelines/t0",
+ var res = await client.PatchAsJsonAsync(generator(0, null),
new TimelinePatchRequest { Visibility = TimelineVisibility.Private });
res.Should().HaveStatusCode(200);
}
@@ -646,7 +754,7 @@ namespace Timeline.Tests.IntegratedTests
}
{ // add member
using var client = await CreateClientAsAdministrator();
- var res = await client.PutAsync("/timelines/t0/members/user1", null);
+ var res = await client.PutAsync(generator(0, "members/user1"), null);
res.Should().HaveStatusCode(200);
}
{ // now user can read admin's
@@ -657,23 +765,21 @@ namespace Timeline.Tests.IntegratedTests
}
}
-
- [Fact]
- public async Task Permission_Post_Create()
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task Permission_Post_Create(Func<int, string, string> generator)
{
- await CreateTestTimelines();
-
using (var client = await CreateClientAsUser())
{
- var res = await client.PutAsync("timelines/t1/members/user2", null);
+ 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("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync(generator(1, "posts"),
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(401);
}
}
@@ -681,13 +787,13 @@ namespace Timeline.Tests.IntegratedTests
using (var client = await CreateClientAsUser())
{
{ // post self's
- var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ 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("timelines/t0/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync(generator(0, "posts"),
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(403);
}
}
@@ -695,8 +801,8 @@ namespace Timeline.Tests.IntegratedTests
using (var client = await CreateClientAsAdministrator())
{
{ // post as admin
- var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync(generator(1, "posts"),
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(200);
}
}
@@ -704,23 +810,22 @@ namespace Timeline.Tests.IntegratedTests
using (var client = await CreateClientAs(2))
{
{ // post as member
- var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = "aaa" });
+ var res = await client.PostAsJsonAsync(generator(1, "posts"),
+ TimelineHelper.TextPostCreateRequest("aaa"));
res.Should().HaveStatusCode(200);
}
}
}
- [Fact]
- public async Task Permission_Post_Delete()
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task Permission_Post_Delete(Func<int, string, string> generator)
{
- 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" });
+ var res = await client.PostAsJsonAsync(generator(1, "posts"),
+ TimelineHelper.TextPostCreateRequest("aaa"));
return res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which.Id;
@@ -729,90 +834,89 @@ namespace Timeline.Tests.IntegratedTests
using (var client = await CreateClientAsUser())
{
{
- var res = await client.PutAsync("timelines/t1/members/user2", null);
+ var res = await client.PutAsync(generator(1, "members/user2"), null);
res.Should().HaveStatusCode(200);
}
{
- var res = await client.PutAsync("timelines/t1/members/user3", null);
+ 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("timelines/t1/posts/12");
+ 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($"timelines/t1/posts/{postId}");
+ 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($"timelines/t1/posts/{postId}");
+ 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($"timelines/t1/posts/{postId}");
+ 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($"timelines/t1/posts/{postId}");
+ 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($"timelines/t1/posts/{postId}");
+ var res = await client.DeleteAsync(generator(1, $"posts/{postId}"));
res.Should().HaveStatusCode(403);
}
}
- [Fact]
- public async Task Post_Op_Should_Work()
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task TextPost_ShouldWork(Func<int, string, string> generator)
{
- await CreateTestTimelines();
-
{
using var client = await CreateClientAsUser();
{
- var res = await client.GetAsync("timelines/t1/posts");
+ var res = await client.GetAsync(generator(1, "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 });
+ 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("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = mockContent });
+ var res = await client.PostAsJsonAsync(generator(1, "posts"),
+ TimelineHelper.TextPostCreateRequest(mockContent));
var body = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which;
body.Should().NotBeNull();
- body.Content.Should().Be(mockContent);
+ body.Content.Should().BeEquivalentTo(TimelineHelper.TextPostContent(mockContent));
body.Author.Should().BeEquivalentTo(UserInfos[1]);
createRes = body;
}
{
- var res = await client.GetAsync("timelines/t1/posts");
+ var res = await client.GetAsync(generator(1, "posts"));
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo[]>()
.Which.Should().NotBeNull().And.BeEquivalentTo(createRes);
@@ -821,33 +925,37 @@ namespace Timeline.Tests.IntegratedTests
var mockTime2 = DateTime.Now.AddDays(-1);
TimelinePostInfo createRes2;
{
- var res = await client.PostAsJsonAsync("timelines/t1/posts",
- new TimelinePostCreateRequest { Content = mockContent2, Time = mockTime2 });
+ var res = await client.PostAsJsonAsync(generator(1, "posts"),
+ TimelineHelper.TextPostCreateRequest(mockContent2, mockTime2));
var body = res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which;
body.Should().NotBeNull();
- body.Content.Should().Be(mockContent2);
+ body.Content.Should().BeEquivalentTo(TimelineHelper.TextPostContent(mockContent2));
body.Author.Should().BeEquivalentTo(UserInfos[1]);
body.Time.Should().BeCloseTo(mockTime2, 1000);
createRes2 = body;
}
{
- var res = await client.GetAsync("timelines/t1/posts");
+ var res = await client.GetAsync(generator(1, "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}");
+ var res = await client.DeleteAsync(generator(1, $"posts/{createRes.Id}"));
res.Should().BeDelete(true);
}
{
- var res = await client.DeleteAsync("timelines/t1/posts/30000");
+ 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("timelines/t1/posts");
+ var res = await client.GetAsync(generator(1, "posts"));
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo[]>()
.Which.Should().NotBeNull().And.BeEquivalentTo(createRes2);
@@ -855,17 +963,16 @@ namespace Timeline.Tests.IntegratedTests
}
}
- [Fact]
- public async Task GetPost_Should_Ordered()
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task GetPost_Should_Ordered(Func<int, string, string> generator)
{
- 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 });
+ var res = await client.PostAsJsonAsync(generator(1, "posts"),
+ TimelineHelper.TextPostCreateRequest("aaa", time));
return res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo>()
.Which.Id;
@@ -877,11 +984,164 @@ namespace Timeline.Tests.IntegratedTests
var id2 = await CreatePost(now);
{
- var res = await client.GetAsync("timelines/t1/posts");
+ var res = await client.GetAsync(generator(1, "posts"));
res.Should().HaveStatusCode(200)
.And.HaveJsonBody<TimelinePostInfo[]>()
.Which.Select(p => p.Id).Should().Equal(id1, id2, id0);
}
}
+
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task CreatePost_InvalidModel(Func<int, string, string> 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(Func<int, string, string> 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<TimelinePostInfo>().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<TimelinePostInfo[]>().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);
+ }
+
+ {
+ 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<TimelinePostInfo[]>()
+ .Which.Should().BeEmpty();
+ }
+
+ {
+ using var scope = TestApp.Factory.Services.CreateScope();
+ var database = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
+ var count = await database.Data.CountAsync();
+ count.Should().Be(0);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task ImagePost_400(Func<int, string, string> 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<TimelinePostInfo>()
+ .Which;
+ postId = body.Id;
+ }
+
+ {
+ var res = await client.GetAsync(generator(1, $"posts/{postId}/data"));
+ res.Should().HaveStatusCode(400)
+ .And.HaveCommonBody(ErrorCodes.TimelineController.PostNoData);
+ }
+ }
}
}
diff --git a/Timeline.Tests/IntegratedTests/TokenTest.cs b/Timeline.Tests/IntegratedTests/TokenTest.cs
index 928d546c..7b28746f 100644
--- a/Timeline.Tests/IntegratedTests/TokenTest.cs
+++ b/Timeline.Tests/IntegratedTests/TokenTest.cs
@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
+using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Services;
using Timeline.Tests.Helpers;
diff --git a/Timeline.Tests/Timeline.Tests.csproj b/Timeline.Tests/Timeline.Tests.csproj
index 00c1e103..1f00cc6c 100644
--- a/Timeline.Tests/Timeline.Tests.csproj
+++ b/Timeline.Tests/Timeline.Tests.csproj
@@ -28,6 +28,6 @@
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\Timeline\Timeline.csproj" />
+ <ProjectReference Include="..\Timeline\Timeline.csproj" />
</ItemGroup>
</Project>