From 36a5a7c81569bbc4fa76b77e9823767d951944b4 Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Sun, 18 Aug 2019 18:07:50 +0800 Subject: Add avatar service. --- Timeline.Tests/UserAvatarServiceTest.cs | 159 ++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 Timeline.Tests/UserAvatarServiceTest.cs (limited to 'Timeline.Tests/UserAvatarServiceTest.cs') diff --git a/Timeline.Tests/UserAvatarServiceTest.cs b/Timeline.Tests/UserAvatarServiceTest.cs new file mode 100644 index 00000000..a8e0562b --- /dev/null +++ b/Timeline.Tests/UserAvatarServiceTest.cs @@ -0,0 +1,159 @@ +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Timeline.Entities; +using Timeline.Services; +using Timeline.Tests.Helpers; +using Timeline.Tests.Mock.Data; +using Xunit; +using Xunit.Abstractions; + +namespace Timeline.Tests +{ + public class MockDefaultUserAvatarProvider : IDefaultUserAvatarProvider + { + public static Avatar Avatar { get; } = new Avatar { Type = "image/test", Data = Encoding.ASCII.GetBytes("test") }; + + public Task GetDefaultAvatar() + { + return Task.FromResult(Avatar); + } + } + + public class UserAvatarServiceTest : IDisposable, IClassFixture + { + private static Avatar MockAvatar { get; } = new Avatar + { + Type = "image/testaaa", + Data = Encoding.ASCII.GetBytes("amock") + }; + + private static Avatar MockAvatar2 { get; } = new Avatar + { + Type = "image/testbbb", + Data = Encoding.ASCII.GetBytes("bmock") + }; + + private readonly MockDefaultUserAvatarProvider _mockDefaultUserAvatarProvider; + + private readonly LoggerFactory _loggerFactory; + private readonly TestDatabase _database; + + private readonly UserAvatarService _service; + + public UserAvatarServiceTest(ITestOutputHelper outputHelper, MockDefaultUserAvatarProvider mockDefaultUserAvatarProvider) + { + _mockDefaultUserAvatarProvider = mockDefaultUserAvatarProvider; + + _loggerFactory = MyTestLoggerFactory.Create(outputHelper); + _database = new TestDatabase(); + + _service = new UserAvatarService(_loggerFactory.CreateLogger(), _database.DatabaseContext, _mockDefaultUserAvatarProvider); + } + + public void Dispose() + { + _loggerFactory.Dispose(); + _database.Dispose(); + } + + [Fact] + public void GetAvatar_ShouldThrow_ArgumentException() + { + // no need to await because arguments are checked syncronizedly. + _service.Invoking(s => s.GetAvatar(null)).Should().Throw() + .Where(e => e.ParamName == "username" && e.Message.Contains("null", StringComparison.OrdinalIgnoreCase)); + _service.Invoking(s => s.GetAvatar("")).Should().Throw() + .Where(e => e.ParamName == "username" && e.Message.Contains("empty", StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public void GetAvatar_ShouldThrow_UserNotExistException() + { + const string username = "usernotexist"; + _service.Awaiting(s => s.GetAvatar(username)).Should().Throw() + .Where(e => e.Username == username); + } + + [Fact] + public async Task GetAvatar_ShouldReturn_Default() + { + const string username = MockUsers.UserUsername; + (await _service.GetAvatar(username)).Should().BeEquivalentTo(await _mockDefaultUserAvatarProvider.GetDefaultAvatar()); + } + + [Fact] + public async Task GetAvatar_ShouldReturn_Data() + { + const string username = MockUsers.UserUsername; + + { + // create mock data + var context = _database.DatabaseContext; + var user = await context.Users.Where(u => u.Name == username).Include(u => u.Avatar).SingleAsync(); + user.Avatar = new UserAvatar + { + Type = MockAvatar.Type, + Data = MockAvatar.Data + }; + await context.SaveChangesAsync(); + } + + (await _service.GetAvatar(username)).Should().BeEquivalentTo(MockAvatar); + } + + [Fact] + public void SetAvatar_ShouldThrow_ArgumentException() + { + // no need to await because arguments are checked syncronizedly. + _service.Invoking(s => s.SetAvatar(null, MockAvatar)).Should().Throw() + .Where(e => e.ParamName == "username" && e.Message.Contains("null", StringComparison.OrdinalIgnoreCase)); + _service.Invoking(s => s.SetAvatar("", MockAvatar)).Should().Throw() + .Where(e => e.ParamName == "username" && e.Message.Contains("empty", StringComparison.OrdinalIgnoreCase)); + + _service.Invoking(s => s.SetAvatar("aaa", new Avatar { Type = null, Data = new[] { (byte)0x00 } })).Should().Throw() + .Where(e => e.ParamName == "avatar" && e.Message.Contains("null", StringComparison.OrdinalIgnoreCase)); + _service.Invoking(s => s.SetAvatar("aaa", new Avatar { Type = "", Data = new[] { (byte)0x00 } })).Should().Throw() + .Where(e => e.ParamName == "avatar" && e.Message.Contains("empty", StringComparison.OrdinalIgnoreCase)); + + _service.Invoking(s => s.SetAvatar("aaa", new Avatar { Type = "aaa", Data = null })).Should().Throw() + .Where(e => e.ParamName == "avatar" && e.Message.Contains("null", StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public void SetAvatar_ShouldThrow_UserNotExistException() + { + const string username = "usernotexist"; + _service.Awaiting(s => s.SetAvatar(username, MockAvatar)).Should().Throw() + .Where(e => e.Username == username); + } + + [Fact] + public async Task SetAvatar_Should_Work() + { + const string username = MockUsers.UserUsername; + + var user = await _database.DatabaseContext.Users.Where(u => u.Name == username).Include(u => u.Avatar).SingleAsync(); + + // create + await _service.SetAvatar(username, MockAvatar); + user.Avatar.Should().NotBeNull(); + user.Avatar.Type.Should().Be(MockAvatar.Type); + user.Avatar.Data.Should().Equal(MockAvatar.Data); + + // modify + await _service.SetAvatar(username, MockAvatar2); + user.Avatar.Should().NotBeNull(); + user.Avatar.Type.Should().Be(MockAvatar2.Type); + user.Avatar.Data.Should().Equal(MockAvatar2.Data); + + // delete + await _service.SetAvatar(username, null); + user.Avatar.Should().BeNull(); + } + } +} -- cgit v1.2.3 From ee113b27d276524f29560f00e944851bf5bdf6a8 Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Mon, 19 Aug 2019 00:01:09 +0800 Subject: Add validator. --- Timeline.Tests/UserAvatarServiceTest.cs | 14 ++++++++--- Timeline/Services/UserAvatarService.cs | 44 +++++++++++++++++++++++++++++++-- Timeline/Timeline.csproj | 1 + nuget.config | 7 +++--- 4 files changed, 58 insertions(+), 8 deletions(-) (limited to 'Timeline.Tests/UserAvatarServiceTest.cs') diff --git a/Timeline.Tests/UserAvatarServiceTest.cs b/Timeline.Tests/UserAvatarServiceTest.cs index a8e0562b..d767958a 100644 --- a/Timeline.Tests/UserAvatarServiceTest.cs +++ b/Timeline.Tests/UserAvatarServiceTest.cs @@ -24,7 +24,15 @@ namespace Timeline.Tests } } - public class UserAvatarServiceTest : IDisposable, IClassFixture + public class MockUserAvatarValidator : IUserAvatarValidator + { + public Task<(bool, string)> Validate(Avatar avatar) + { + return Task.FromResult((true, "Validate succeed.")); + } + } + + public class UserAvatarServiceTest : IDisposable, IClassFixture, IClassFixture { private static Avatar MockAvatar { get; } = new Avatar { @@ -45,14 +53,14 @@ namespace Timeline.Tests private readonly UserAvatarService _service; - public UserAvatarServiceTest(ITestOutputHelper outputHelper, MockDefaultUserAvatarProvider mockDefaultUserAvatarProvider) + public UserAvatarServiceTest(ITestOutputHelper outputHelper, MockDefaultUserAvatarProvider mockDefaultUserAvatarProvider, MockUserAvatarValidator mockUserAvatarValidator) { _mockDefaultUserAvatarProvider = mockDefaultUserAvatarProvider; _loggerFactory = MyTestLoggerFactory.Create(outputHelper); _database = new TestDatabase(); - _service = new UserAvatarService(_loggerFactory.CreateLogger(), _database.DatabaseContext, _mockDefaultUserAvatarProvider); + _service = new UserAvatarService(_loggerFactory.CreateLogger(), _database.DatabaseContext, _mockDefaultUserAvatarProvider, mockUserAvatarValidator); } public void Dispose() diff --git a/Timeline/Services/UserAvatarService.cs b/Timeline/Services/UserAvatarService.cs index 4f11978c..2a73cde5 100644 --- a/Timeline/Services/UserAvatarService.cs +++ b/Timeline/Services/UserAvatarService.cs @@ -2,6 +2,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; using System; using System.IO; using System.Linq; @@ -45,6 +47,11 @@ namespace Timeline.Services Task GetDefaultAvatar(); } + public interface IUserAvatarValidator + { + Task<(bool valid, string message)> Validate(Avatar avatar); + } + public interface IUserAvatarService { /// @@ -87,6 +94,31 @@ namespace Timeline.Services } } + public class UserAvatarValidator : IUserAvatarValidator + { + public Task<(bool valid, string message)> Validate(Avatar avatar) + { + return Task.Run(() => + { + try + { + using (var image = Image.Load(avatar.Data, out IImageFormat format)) + { + if (!format.MimeTypes.Contains(avatar.Type)) + return (false, "Image's actual mime type is not the specified one."); + if (image.Width != image.Height) + return (false, "Image is not a square, aka width is not equal to height."); + } + return (true, "A good avatar."); + } + catch (UnknownImageFormatException e) + { + return (false, $"Failed to decode image. Exception: {e} ."); + } + }); + } + } + public class UserAvatarService : IUserAvatarService { @@ -95,12 +127,14 @@ namespace Timeline.Services private readonly DatabaseContext _database; private readonly IDefaultUserAvatarProvider _defaultUserAvatarProvider; + private readonly IUserAvatarValidator _avatarValidator; - public UserAvatarService(ILogger logger, DatabaseContext database, IDefaultUserAvatarProvider defaultUserAvatarProvider) + public UserAvatarService(ILogger logger, DatabaseContext database, IDefaultUserAvatarProvider defaultUserAvatarProvider, IUserAvatarValidator avatarValidator) { _logger = logger; _database = database; _defaultUserAvatarProvider = defaultUserAvatarProvider; + _avatarValidator = avatarValidator; } public async Task GetAvatar(string username) @@ -157,11 +191,15 @@ namespace Timeline.Services { _database.UserAvatars.Remove(avatarEntity); await _database.SaveChangesAsync(); + _logger.LogInformation("Removed an entry in user_avatars."); } } else { - // TODO: Use image library to check the format to prohibit bad data. + (bool valid, string message) = await _avatarValidator.Validate(avatar); + if (!valid) + throw new AvatarDataException(avatar, $"Failed to validate image. {message}"); + if (avatarEntity == null) { user.Avatar = new UserAvatar @@ -176,6 +214,7 @@ namespace Timeline.Services avatarEntity.Data = avatar.Data; } await _database.SaveChangesAsync(); + _logger.LogInformation("Added or modified an entry in user_avatars."); } } } @@ -186,6 +225,7 @@ namespace Timeline.Services { services.AddScoped(); services.AddSingleton(); + services.AddSingleton(); } } } diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj index 29ff3354..3855e0d1 100644 --- a/Timeline/Timeline.csproj +++ b/Timeline/Timeline.csproj @@ -12,5 +12,6 @@ + diff --git a/nuget.config b/nuget.config index 22519a86..e1fc7cfe 100644 --- a/nuget.config +++ b/nuget.config @@ -1,8 +1,9 @@ - + - - + + + -- cgit v1.2.3 From 69289d86b6f8ee54922920455066cf2f362ac6d8 Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Mon, 19 Aug 2019 00:25:42 +0800 Subject: Fix unit tests. --- Timeline.Tests/UserAvatarServiceTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Timeline.Tests/UserAvatarServiceTest.cs') diff --git a/Timeline.Tests/UserAvatarServiceTest.cs b/Timeline.Tests/UserAvatarServiceTest.cs index d767958a..fbec8228 100644 --- a/Timeline.Tests/UserAvatarServiceTest.cs +++ b/Timeline.Tests/UserAvatarServiceTest.cs @@ -26,9 +26,9 @@ namespace Timeline.Tests public class MockUserAvatarValidator : IUserAvatarValidator { - public Task<(bool, string)> Validate(Avatar avatar) + public Task Validate(Avatar avatar) { - return Task.FromResult((true, "Validate succeed.")); + return Task.CompletedTask; } } -- cgit v1.2.3 From cc646173d0667769c4f4ae78f51a626346d7e7ad Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Mon, 19 Aug 2019 00:41:47 +0800 Subject: Add unit tests for avatar validator. --- Timeline.Tests/IntegratedTests/UserAvatarTests.cs | 11 +++ Timeline.Tests/UserAvatarServiceTest.cs | 89 +++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 Timeline.Tests/IntegratedTests/UserAvatarTests.cs (limited to 'Timeline.Tests/UserAvatarServiceTest.cs') diff --git a/Timeline.Tests/IntegratedTests/UserAvatarTests.cs b/Timeline.Tests/IntegratedTests/UserAvatarTests.cs new file mode 100644 index 00000000..14781474 --- /dev/null +++ b/Timeline.Tests/IntegratedTests/UserAvatarTests.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Timeline.Tests.IntegratedTests +{ + public class UserAvatarTests + { + } +} diff --git a/Timeline.Tests/UserAvatarServiceTest.cs b/Timeline.Tests/UserAvatarServiceTest.cs index fbec8228..04f52c71 100644 --- a/Timeline.Tests/UserAvatarServiceTest.cs +++ b/Timeline.Tests/UserAvatarServiceTest.cs @@ -1,7 +1,11 @@ using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats.Png; using System; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -32,6 +36,91 @@ namespace Timeline.Tests } } + public class UserAvatarValidatorTest : IClassFixture + { + private readonly UserAvatarValidator _validator; + + public UserAvatarValidatorTest(UserAvatarValidator validator) + { + _validator = validator; + } + + [Fact] + public void CantDecode() + { + Avatar avatar = new Avatar + { + Data = Encoding.ASCII.GetBytes("This is not a image."), + Type = "image/png" + }; + _validator.Awaiting(v => v.Validate(avatar)) + .Should().Throw() + .Where(e => e.Avatar == avatar && e.Error == AvatarDataException.ErrorReason.CantDecode); + } + + [Fact] + public void UnmatchedFormat() + { + Avatar avatar; + using (var image = new Image(100, 100)) + { + using (var stream = new MemoryStream()) + { + image.SaveAsPng(stream); + avatar = new Avatar + { + Data = stream.ToArray(), + Type = "image/jpeg" + }; + } + } + _validator.Awaiting(v => v.Validate(avatar)) + .Should().Throw() + .Where(e => e.Avatar == avatar && e.Error == AvatarDataException.ErrorReason.UnmatchedFormat); + } + + [Fact] + public void BadSize() + { + Avatar avatar; + using (var image = new Image(100, 200)) + { + using (var stream = new MemoryStream()) + { + image.SaveAsPng(stream); + avatar = new Avatar + { + Data = stream.ToArray(), + Type = PngFormat.Instance.DefaultMimeType + }; + } + } + _validator.Awaiting(v => v.Validate(avatar)) + .Should().Throw() + .Where(e => e.Avatar == avatar && e.Error == AvatarDataException.ErrorReason.BadSize); + } + + [Fact] + public void Success() + { + Avatar avatar; + using (var image = new Image(100, 100)) + { + using (var stream = new MemoryStream()) + { + image.SaveAsPng(stream); + avatar = new Avatar + { + Data = stream.ToArray(), + Type = PngFormat.Instance.DefaultMimeType + }; + } + } + _validator.Awaiting(v => v.Validate(avatar)) + .Should().NotThrow(); + } + } + public class UserAvatarServiceTest : IDisposable, IClassFixture, IClassFixture { private static Avatar MockAvatar { get; } = new Avatar -- cgit v1.2.3 From 9a674f6f3a15ef260868049d945d7afc930f093c Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Mon, 19 Aug 2019 01:44:29 +0800 Subject: Add integrated tests. --- Timeline.Tests/Helpers/HttpClientExtensions.cs | 9 ++ Timeline.Tests/Helpers/ImageHelper.cs | 21 +++ Timeline.Tests/IntegratedTests/UserAvatarTests.cs | 153 +++++++++++++++++++++- Timeline.Tests/UserAvatarServiceTest.cs | 48 ++----- 4 files changed, 190 insertions(+), 41 deletions(-) create mode 100644 Timeline.Tests/Helpers/ImageHelper.cs (limited to 'Timeline.Tests/UserAvatarServiceTest.cs') diff --git a/Timeline.Tests/Helpers/HttpClientExtensions.cs b/Timeline.Tests/Helpers/HttpClientExtensions.cs index cd40d91e..b9204fcc 100644 --- a/Timeline.Tests/Helpers/HttpClientExtensions.cs +++ b/Timeline.Tests/Helpers/HttpClientExtensions.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; @@ -11,5 +12,13 @@ namespace Timeline.Tests.Helpers { return client.PatchAsync(url, new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json")); } + + public static Task PutByteArrayAsync(this HttpClient client, string url, byte[] body, string mimeType) + { + var content = new ByteArrayContent(body); + content.Headers.ContentLength = body.Length; + content.Headers.ContentType = new MediaTypeHeaderValue(mimeType); + return client.PutAsync(url, content); + } } } diff --git a/Timeline.Tests/Helpers/ImageHelper.cs b/Timeline.Tests/Helpers/ImageHelper.cs new file mode 100644 index 00000000..c5a9cf17 --- /dev/null +++ b/Timeline.Tests/Helpers/ImageHelper.cs @@ -0,0 +1,21 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using System.IO; + +namespace Timeline.Tests.Helpers +{ + public static class ImageHelper + { + public static byte[] CreatePngWithSize(int width, int height) + { + using (var image = new Image(width, height)) + { + using (var stream = new MemoryStream()) + { + image.SaveAsPng(stream); + return stream.ToArray(); + } + } + } + } +} diff --git a/Timeline.Tests/IntegratedTests/UserAvatarTests.cs b/Timeline.Tests/IntegratedTests/UserAvatarTests.cs index 14781474..e282da1b 100644 --- a/Timeline.Tests/IntegratedTests/UserAvatarTests.cs +++ b/Timeline.Tests/IntegratedTests/UserAvatarTests.cs @@ -1,11 +1,154 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using FluentAssertions; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using SixLabors.ImageSharp.Formats.Png; +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading.Tasks; +using Timeline.Controllers; +using Timeline.Services; +using Timeline.Tests.Helpers; +using Timeline.Tests.Helpers.Authentication; +using Xunit; +using Xunit.Abstractions; namespace Timeline.Tests.IntegratedTests { - public class UserAvatarTests + public class UserAvatarUnitTest : IClassFixture>, IDisposable { + private readonly WebApplicationFactory _factory; + private readonly Action _disposeAction; + + public UserAvatarUnitTest(MyWebApplicationFactory factory, ITestOutputHelper outputHelper) + { + _factory = factory.WithTestConfig(outputHelper, out _disposeAction); + } + + public void Dispose() + { + _disposeAction(); + } + + [Fact] + public async Task Test() + { + Avatar mockAvatar = new Avatar + { + Data = ImageHelper.CreatePngWithSize(100, 100), + Type = PngFormat.Instance.DefaultMimeType + }; + + using (var client = await _factory.CreateClientAsUser()) + { + { + var res = await client.GetAsync("users/usernotexist/avatar"); + res.Should().HaveStatusCodeNotFound() + .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Get_UserNotExist); + } + + var env = _factory.Server.Host.Services.GetRequiredService(); + var defaultAvatarData = await File.ReadAllBytesAsync(Path.Combine(env.ContentRootPath, "default-avatar.png")); + + async Task GetReturnDefault(string username = "user") + { + var res = await client.GetAsync($"users/{username}/avatar"); + res.Should().HaveStatusCodeOk(); + res.Content.Headers.ContentType.MediaType.Should().Be("image/png"); + var body = await res.Content.ReadAsByteArrayAsync(); + body.Should().Equal(defaultAvatarData); + } + + await GetReturnDefault(); + await GetReturnDefault("admin"); + + { + var res = await client.PutByteArrayAsync("users/user/avatar", new[] { (byte)0x00 }, "image/notaccept"); + res.Should().HaveStatusCode(HttpStatusCode.UnsupportedMediaType); + } + + { + var res = await client.PutByteArrayAsync("users/user/avatar", new[] { (byte)0x00 }, "image/notaccept"); + res.Should().HaveStatusCode(HttpStatusCode.UnsupportedMediaType); + } + + { + var res = await client.PutByteArrayAsync("users/user/avatar", new[] { (byte)0x00 }, "image/png"); + res.Should().HaveStatusCode(HttpStatusCode.BadRequest) + .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_BadFormat_CantDecode); + } + + { + var res = await client.PutByteArrayAsync("users/user/avatar", mockAvatar.Data, "image/jpeg"); + res.Should().HaveStatusCode(HttpStatusCode.BadRequest) + .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_BadFormat_UnmatchedFormat); + } + + { + var res = await client.PutByteArrayAsync("users/user/avatar", ImageHelper.CreatePngWithSize(100, 200), "image/png"); + res.Should().HaveStatusCode(HttpStatusCode.BadRequest) + .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_BadFormat_BadSize); + } + + { + var res = await client.PutByteArrayAsync("users/user/avatar", mockAvatar.Data, mockAvatar.Type); + res.Should().HaveStatusCode(HttpStatusCode.OK); + + var res2 = await client.GetAsync("users/user/avatar"); + res2.Should().HaveStatusCodeOk(); + res2.Content.Headers.ContentType.MediaType.Should().Be(mockAvatar.Type); + var body = await res2.Content.ReadAsByteArrayAsync(); + body.Should().Equal(mockAvatar.Data); + } + + { + var res = await client.PutByteArrayAsync("users/admin/avatar", new[] { (byte)0x00 }, "image/png"); + res.Should().HaveStatusCode(HttpStatusCode.Forbidden) + .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_Forbid); + } + + { + var res = await client.DeleteAsync("users/admin/avatar"); + res.Should().HaveStatusCode(HttpStatusCode.Forbidden) + .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Delete_Forbid); + } + + for (int i = 0; i < 2; i++) // double delete should work. + { + var res = await client.DeleteAsync("users/user/avatar"); + res.Should().HaveStatusCodeOk(); + await GetReturnDefault(); + } + } + + // Authorization check. + using (var client = await _factory.CreateClientAsAdmin()) + { + { + var res = await client.PutByteArrayAsync("users/user/avatar", mockAvatar.Data, mockAvatar.Type); + res.Should().HaveStatusCode(HttpStatusCode.OK); + } + + { + var res = await client.DeleteAsync("users/user/avatar"); + res.Should().HaveStatusCode(HttpStatusCode.OK); + } + + { + var res = await client.PutByteArrayAsync("users/usernotexist/avatar", new[] { (byte)0x00 }, "image/png"); + res.Should().HaveStatusCodeBadRequest() + .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Put_UserNotExist); + } + + { + var res = await client.DeleteAsync("users/usernotexist/avatar"); + res.Should().HaveStatusCodeBadRequest() + .And.Should().HaveBodyAsCommonResponseWithCode(UserAvatarController.ErrorCodes.Delete_UserNotExist); + } + } + } } -} +} \ No newline at end of file diff --git a/Timeline.Tests/UserAvatarServiceTest.cs b/Timeline.Tests/UserAvatarServiceTest.cs index 04f52c71..69d6b61c 100644 --- a/Timeline.Tests/UserAvatarServiceTest.cs +++ b/Timeline.Tests/UserAvatarServiceTest.cs @@ -61,19 +61,11 @@ namespace Timeline.Tests [Fact] public void UnmatchedFormat() { - Avatar avatar; - using (var image = new Image(100, 100)) + Avatar avatar = new Avatar { - using (var stream = new MemoryStream()) - { - image.SaveAsPng(stream); - avatar = new Avatar - { - Data = stream.ToArray(), - Type = "image/jpeg" - }; - } - } + Data = ImageHelper.CreatePngWithSize(100, 100), + Type = "image/jpeg" + }; _validator.Awaiting(v => v.Validate(avatar)) .Should().Throw() .Where(e => e.Avatar == avatar && e.Error == AvatarDataException.ErrorReason.UnmatchedFormat); @@ -82,19 +74,11 @@ namespace Timeline.Tests [Fact] public void BadSize() { - Avatar avatar; - using (var image = new Image(100, 200)) + Avatar avatar = new Avatar { - using (var stream = new MemoryStream()) - { - image.SaveAsPng(stream); - avatar = new Avatar - { - Data = stream.ToArray(), - Type = PngFormat.Instance.DefaultMimeType - }; - } - } + Data = ImageHelper.CreatePngWithSize(100, 200), + Type = PngFormat.Instance.DefaultMimeType + }; _validator.Awaiting(v => v.Validate(avatar)) .Should().Throw() .Where(e => e.Avatar == avatar && e.Error == AvatarDataException.ErrorReason.BadSize); @@ -103,19 +87,11 @@ namespace Timeline.Tests [Fact] public void Success() { - Avatar avatar; - using (var image = new Image(100, 100)) + Avatar avatar = new Avatar { - using (var stream = new MemoryStream()) - { - image.SaveAsPng(stream); - avatar = new Avatar - { - Data = stream.ToArray(), - Type = PngFormat.Instance.DefaultMimeType - }; - } - } + Data = ImageHelper.CreatePngWithSize(100, 100), + Type = PngFormat.Instance.DefaultMimeType + }; _validator.Awaiting(v => v.Validate(avatar)) .Should().NotThrow(); } -- cgit v1.2.3