From a9f248ad817683e911348cd168c570db3d07757f Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 21 Apr 2019 00:08:59 +0800 Subject: Reorgnize api. Add basic unit test. --- .../Helpers/WebApplicationFactoryExtensions.cs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) (limited to 'Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs') diff --git a/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs b/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs index 4a7f87fb..a34217f4 100644 --- a/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs +++ b/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs @@ -4,7 +4,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Timeline.Models; -using Timeline.Services; using Xunit.Abstractions; namespace Timeline.Tests.Helpers @@ -42,28 +41,10 @@ namespace Timeline.Tests.Helpers var scopedServices = scope.ServiceProvider; var db = scopedServices.GetRequiredService(); - var passwordService = new PasswordService(null); - // Ensure the database is created. db.Database.EnsureCreated(); - db.Users.AddRange(new User[] { - new User - { - Id = 0, - Name = "user", - EncryptedPassword = passwordService.HashPassword("user"), - RoleString = "user" - }, - new User - { - Id = 0, - Name = "admin", - EncryptedPassword = passwordService.HashPassword("admin"), - RoleString = "user,admin" - } - }); - + db.Users.AddRange(TestMockUsers.MockUsers); db.SaveChanges(); } }); -- cgit v1.2.3 From 3d6938ca60691f73bc0b570e7ca4af4f8251741c Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 24 Apr 2019 00:22:25 +0800 Subject: Add ObjectExists implementation in cos. Remove Test host environment. --- .../Helpers/WebApplicationFactoryExtensions.cs | 1 - Timeline.Tests/QCloudCosServiceUnitTest.cs | 33 +++++++- Timeline/EnvironmentConstants.cs | 14 ---- Timeline/Services/TencentCloudCosService.cs | 88 +++++++++++++++++++--- Timeline/Startup.cs | 11 +-- Timeline/appsettings.Test.json | 14 ---- 6 files changed, 112 insertions(+), 49 deletions(-) delete mode 100644 Timeline/EnvironmentConstants.cs delete mode 100644 Timeline/appsettings.Test.json (limited to 'Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs') diff --git a/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs b/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs index a34217f4..a7616b41 100644 --- a/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs +++ b/Timeline.Tests/Helpers/WebApplicationFactoryExtensions.cs @@ -15,7 +15,6 @@ namespace Timeline.Tests.Helpers return factory.WithWebHostBuilder(builder => { builder - .UseEnvironment(EnvironmentConstants.TestEnvironmentName) .ConfigureLogging(logging => { logging.AddXunit(outputHelper); diff --git a/Timeline.Tests/QCloudCosServiceUnitTest.cs b/Timeline.Tests/QCloudCosServiceUnitTest.cs index c02f70be..b99352b9 100644 --- a/Timeline.Tests/QCloudCosServiceUnitTest.cs +++ b/Timeline.Tests/QCloudCosServiceUnitTest.cs @@ -1,12 +1,24 @@ -using System; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using System; using System.Collections.Generic; +using System.Threading.Tasks; using Timeline.Services; +using Timeline.Tests.Helpers; using Xunit; +using Xunit.Abstractions; namespace Timeline.Tests { - public class QCloudCosServiceUnitTest + public class QCloudCosServiceUnitTest : IClassFixture> { + private readonly WebApplicationFactory _factory; + + public QCloudCosServiceUnitTest(WebApplicationFactory factory, ITestOutputHelper outputHelper) + { + _factory = factory.WithTestConfig(outputHelper); + } + [Fact] public void GenerateSignatureTest() { @@ -37,5 +49,22 @@ namespace Timeline.Tests Assert.Equal("q-sign-algorithm=sha1&q-ak=AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5q&q-sign-time=1417773892;1417853898&q-key-time=1417773892;1417853898&q-header-list=host;x-cos-content-sha1;x-cos-storage-class&q-url-param-list=&q-signature=0ab12f43e74cbe148d705cd9fae8adc9a6d39cc1", QCloudCosService.GenerateSign(credential, request, signValidTime)); } + + /* + [Fact] + public async Task ObjectExistsTest() + { + _factory.CreateDefaultClient().Dispose(); + + using (var serviceScope = _factory.Server.Host.Services.CreateScope()) + { + var services = serviceScope.ServiceProvider; + var service = services.GetRequiredService(); + Assert.True(await service.ObjectExists("avatar", "__default")); + Assert.False(await service.ObjectExists("avatar", "haha")); + Assert.False(await service.ObjectExists("haha", "haha")); + } + } + */ } } diff --git a/Timeline/EnvironmentConstants.cs b/Timeline/EnvironmentConstants.cs deleted file mode 100644 index 5ffc3623..00000000 --- a/Timeline/EnvironmentConstants.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.AspNetCore.Hosting; - -namespace Timeline -{ - public static class EnvironmentConstants - { - public const string TestEnvironmentName = "Test"; - - public static bool IsTest(this IHostingEnvironment environment) - { - return environment.EnvironmentName == TestEnvironmentName; - } - } -} diff --git a/Timeline/Services/TencentCloudCosService.cs b/Timeline/Services/TencentCloudCosService.cs index 1bfcf745..8dbd3614 100644 --- a/Timeline/Services/TencentCloudCosService.cs +++ b/Timeline/Services/TencentCloudCosService.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; +using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; @@ -20,13 +21,15 @@ namespace Timeline.Services public class QCloudCosService : IQCloudCosService { - private readonly QCloudCosConfig _config; + private readonly IOptionsMonitor _config; private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; - public QCloudCosService(IOptions config, ILogger logger) + public QCloudCosService(IOptionsMonitor config, ILogger logger, IHttpClientFactory httpClientFactory) { - _config = config.Value; + _config = config; _logger = logger; + _httpClientFactory = httpClientFactory; } public class QCloudCredentials @@ -45,6 +48,17 @@ namespace Timeline.Services public class TimeDuration { + public TimeDuration() + { + + } + + public TimeDuration(DateTimeOffset start, DateTimeOffset end) + { + Start = start; + End = end; + } + public DateTimeOffset Start { get; set; } public DateTimeOffset End { get; set; } } @@ -57,14 +71,15 @@ namespace Timeline.Services Debug.Assert(request != null); Debug.Assert(request.Method != null); Debug.Assert(request.Uri != null); - Debug.Assert(request.Parameters != null); - Debug.Assert(request.Headers != null); Debug.Assert(signValidTime != null); Debug.Assert(signValidTime.Start < signValidTime.End, "Start must be before End in sign valid time."); List<(string key, string value)> Transform(IEnumerable> raw) { - var sorted= raw.Select(p => (key: p.Key.ToLower(), value: WebUtility.UrlEncode(p.Value))).ToList(); + if (raw == null) + return new List<(string key, string value)>(); + + var sorted = raw.Select(p => (key: p.Key.ToLower(), value: WebUtility.UrlEncode(p.Value))).ToList(); sorted.Sort((left, right) => string.CompareOrdinal(left.key, right.key)); return sorted; } @@ -103,7 +118,7 @@ namespace Timeline.Services } var httpString = new StringBuilder() - .Append(request.Method).Append('\n') + .Append(request.Method.ToLower()).Append('\n') .Append(request.Uri).Append('\n') .Append(Join(transformedParameters)).Append('\n') .Append(Join(transformedHeaders)).Append('\n') @@ -130,9 +145,64 @@ namespace Timeline.Services return Join(result); } - public Task ObjectExists(string bucket, string key) + private QCloudCredentials GetCredentials() { - throw new NotImplementedException(); + var config = _config.CurrentValue; + return new QCloudCredentials + { + SecretId = config.SecretId, + SecretKey = config.SecretKey + }; + } + + private string GetHost(string bucket) + { + var config = _config.CurrentValue; + return $"{bucket}-{config.AppId}.cos.{config.Region}.myqcloud.com"; + } + + public async Task ObjectExists(string bucket, string key) + { + if (bucket == null) + throw new ArgumentNullException(nameof(bucket)); + if (key == null) + throw new ArgumentNullException(nameof(key)); + + var client = _httpClientFactory.CreateClient(); + + var host = GetHost(bucket); + + var request = new HttpRequestMessage(); + request.Method = HttpMethod.Head; + request.RequestUri = new Uri($"https://{host}/{key}"); + request.Headers.Host = host; + request.Headers.Date = DateTimeOffset.Now; + request.Headers.TryAddWithoutValidation("Authorization", GenerateSign(GetCredentials(), new RequestInfo + { + Method = "head", + Uri = "/" + key, + Headers = new Dictionary + { + ["Host"] = host + } + }, new TimeDuration(DateTimeOffset.Now, DateTimeOffset.Now.AddMinutes(2)))); + + try + { + var response = await client.SendAsync(request); + + if (response.IsSuccessStatusCode) + return true; + if (response.StatusCode == HttpStatusCode.NotFound) + return false; + + throw new Exception($"Unknown response code. {response.ToString()}"); + } + catch (Exception e) + { + _logger.LogError(e, "An error occured when test a cos object existence."); + return false; + } } public string GetObjectUrl(string bucket, string key) diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs index 12d60843..46d0afe5 100644 --- a/Timeline/Startup.cs +++ b/Timeline/Startup.cs @@ -81,6 +81,8 @@ namespace Timeline }); }); + services.AddHttpClient(); + services.Configure(Configuration.GetSection(nameof(QCloudCosConfig))); services.AddSingleton(); } @@ -88,15 +90,6 @@ namespace Timeline // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app) { - if (Environment.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Error"); - } - app.UseCors(corsPolicyName); app.UseForwardedHeaders(new ForwardedHeadersOptions diff --git a/Timeline/appsettings.Test.json b/Timeline/appsettings.Test.json deleted file mode 100644 index ea32348b..00000000 --- a/Timeline/appsettings.Test.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information", - "Microsoft.AspNetCore.Authentication": "Debug", - "Microsoft.AspNetCore.Authorization": "Debug" - } - }, - "JwtConfig": { - "SigningKey": "crupest hahahahahahahhahahahahaha" - } -} -- cgit v1.2.3