diff --git a/appveyor.test.ps1 b/appveyor.test.ps1 index 4fa1c7b0..d7223364 100644 --- a/appveyor.test.ps1 +++ b/appveyor.test.ps1 @@ -38,7 +38,7 @@ function runUnitTest() { write-host "dotnet test test\UnitTest" -ForegroundColor Cyan - dotnet test test\UnitTest --filter "FullyQualifiedName!~MySql" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Include="[Bootstrap*]*" /p:ExcludeByFile="..\..\src\admin\Bootstrap.Admin\Program.cs%2c..\..\src\admin\Bootstrap.Admin\Startup.cs%2c..\..\src\admin\Bootstrap.Admin\HttpHeaderOperation.cs" /p:CoverletOutput=..\..\ + dotnet test test\UnitTest --filter="FullyQualifiedName!~MySql" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Include="[Bootstrap.Admin*]*%2c[Bootstrap.DataAccess*]*" /p:Exclude="[*]*Program%2c[*]*Startup%2c[Bootstrap.DataAccess]*AutoDB%2c[Bootstrap.DataAccess]*WeChatHelper%2c[Bootstrap.DataAccess]*DbLogTask" /p:ExcludeByFile="**/SMSExtensions.cs%2c**/Helper/OAuthHelper.cs" /p:CoverletOutput=..\..\ } function coverallUnitTest() { diff --git a/appveyor.yml b/appveyor.yml index c44b5534..cf7a0c69 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,7 +24,11 @@ services: - mongodb install: - ps: >- + #copy my.ini into mysql folder + dotnet --version + + #xcopy "$($env:appveyor_build_folder)\db\MySQL\my.ini" "C:\Program Files\MySQL\MySQL Server 5.7" /y build_script: - ps: >- .\appveyor.build.ps1 diff --git a/db/MySQL/my.ini b/db/MySQL/my.ini index eb91891f..9f4ad7e2 100644 --- a/db/MySQL/my.ini +++ b/db/MySQL/my.ini @@ -1,7 +1,9 @@ -[client] +[client] default-character-set=utf8 [mysqld] charcater_set_client=utf8 character_set_connection=utf8 -character_set_results=utf8 \ No newline at end of file +character_set_results=utf8 +character_set_server=utf8 +character_set_database=utf8 diff --git a/src/admin/Bootstrap.Admin/Bootstrap.Admin.csproj b/src/admin/Bootstrap.Admin/Bootstrap.Admin.csproj index 09a783d9..d03fcff4 100644 --- a/src/admin/Bootstrap.Admin/Bootstrap.Admin.csproj +++ b/src/admin/Bootstrap.Admin/Bootstrap.Admin.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/admin/Bootstrap.Admin/Controllers/Api/LoginController.cs b/src/admin/Bootstrap.Admin/Controllers/Api/LoginController.cs index d0f3cb03..5aaea6cf 100644 --- a/src/admin/Bootstrap.Admin/Controllers/Api/LoginController.cs +++ b/src/admin/Bootstrap.Admin/Controllers/Api/LoginController.cs @@ -52,11 +52,7 @@ namespace Bootstrap.Admin.Controllers.Api /// /// [HttpPut] - public async Task Put([FromServices]ISMSProvider provider, [FromQuery]string phone) - { - if (string.IsNullOrEmpty(phone)) return false; - return await provider.SendCodeAsync(phone); - } + public async Task Put([FromServices]ISMSProvider provider, [FromQuery]string phone) => string.IsNullOrEmpty(phone) ? false : await provider.SendCodeAsync(phone); /// /// 跨域握手协议 diff --git a/src/admin/Bootstrap.Admin/Extensions/HttpHeaderOperation.cs b/src/admin/Bootstrap.Admin/Extensions/HttpHeaderOperation.cs index 5c9fae1f..f479233d 100644 --- a/src/admin/Bootstrap.Admin/Extensions/HttpHeaderOperation.cs +++ b/src/admin/Bootstrap.Admin/Extensions/HttpHeaderOperation.cs @@ -17,8 +17,6 @@ namespace Bootstrap.Admin /// public void Apply(OpenApiOperation operation, OperationFilterContext context) { - if (operation.Parameters == null) operation.Parameters = new List(); - if (context.MethodInfo.GetCustomAttributes(typeof(AllowAnonymousAttribute), true).Length == 0) { operation.Parameters.Add(new OpenApiParameter() diff --git a/src/admin/Bootstrap.Admin/Extensions/SMSExtensions.cs b/src/admin/Bootstrap.Admin/Extensions/SMSExtensions.cs index 33b31aac..331dc57a 100644 --- a/src/admin/Bootstrap.Admin/Extensions/SMSExtensions.cs +++ b/src/admin/Bootstrap.Admin/Extensions/SMSExtensions.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -241,7 +240,7 @@ namespace Microsoft.Extensions.DependencyInjection /// /// 获得/设置 默认授权 App /// - public string App { get; set; } + public string App { get; set; } = "0"; /// /// 获得/设置 短信下发网关地址 diff --git a/src/admin/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj b/src/admin/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj index 69f28f73..236d7d29 100644 --- a/src/admin/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj +++ b/src/admin/Bootstrap.DataAccess/Bootstrap.DataAccess.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/admin/Bootstrap.DataAccess/DbManager.cs b/src/admin/Bootstrap.DataAccess/DbManager.cs index de5d18f9..5b482aa5 100644 --- a/src/admin/Bootstrap.DataAccess/DbManager.cs +++ b/src/admin/Bootstrap.DataAccess/DbManager.cs @@ -18,7 +18,7 @@ namespace Bootstrap.DataAccess /// /// /// - /// 是否记录日志 + /// 是否记录日志 /// public static IDatabase Create(string connectionName = null, bool keepAlive = false, bool enableLog = true) { diff --git a/src/admin/Bootstrap.DataAccess/Helper/LogHelper.cs b/src/admin/Bootstrap.DataAccess/Helper/LogHelper.cs index 5d7ce218..cc7feaf0 100644 --- a/src/admin/Bootstrap.DataAccess/Helper/LogHelper.cs +++ b/src/admin/Bootstrap.DataAccess/Helper/LogHelper.cs @@ -81,7 +81,7 @@ namespace Bootstrap.DataAccess while (_messageQueue.TryTake(out var log)) { logs.Add(log); - }; + } if (logs.Any()) { using (var db = DbManager.Create(enableLog: false)) diff --git a/src/admin/Bootstrap.DataAccess/Helper/OAuthHelper.cs b/src/admin/Bootstrap.DataAccess/Helper/OAuthHelper.cs index 0b143b83..42156b66 100644 --- a/src/admin/Bootstrap.DataAccess/Helper/OAuthHelper.cs +++ b/src/admin/Bootstrap.DataAccess/Helper/OAuthHelper.cs @@ -74,7 +74,7 @@ namespace Bootstrap.DataAccess /// /// 插入 Gitee 授权用户到数据库中 /// - /// + /// /// private static User ParseUser(OAuthCreatingTicketContext context) { diff --git a/src/admin/Bootstrap.DataAccess/Helper/WeChatHelper.cs b/src/admin/Bootstrap.DataAccess/Helper/WeChatHelper.cs index eeb5b89a..a0400dd5 100644 --- a/src/admin/Bootstrap.DataAccess/Helper/WeChatHelper.cs +++ b/src/admin/Bootstrap.DataAccess/Helper/WeChatHelper.cs @@ -4,6 +4,9 @@ using Longbow.WeChatAuth; using Microsoft.AspNetCore.Authentication.OAuth; using System; using System.Linq; +#if NETCOREAPP3_0 +using System.Text.Json; +#endif namespace Bootstrap.DataAccess { @@ -35,7 +38,7 @@ namespace Bootstrap.DataAccess /// /// 插入 Gitee 授权用户到数据库中 /// - /// + /// /// private static User ParseUser(OAuthCreatingTicketContext context) { @@ -53,7 +56,7 @@ namespace Bootstrap.DataAccess } #if NETCOREAPP3_0 - private static T ToObject(this System.Text.Json.JsonElement element) where T : WeChatUser + private static T ToObject(this JsonElement element) where T : WeChatUser { var user = new WeChatUser(); var target = element.EnumerateObject(); @@ -70,7 +73,7 @@ namespace Bootstrap.DataAccess return user as T; } - private static string TryGetValue(this System.Text.Json.JsonElement.ObjectEnumerator target, string propertyName) + private static string TryGetValue(this JsonElement.ObjectEnumerator target, string propertyName) { var ret = string.Empty; var property = target.FirstOrDefault(t => t.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); diff --git a/test/UnitTest/BAWebHost.cs b/test/UnitTest/BAWebHost.cs index fafbc137..f8aa41c7 100644 --- a/test/UnitTest/BAWebHost.cs +++ b/test/UnitTest/BAWebHost.cs @@ -10,6 +10,8 @@ using System.Net; using System.Net.Http; using UnitTest; using Xunit; +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; namespace Bootstrap.Admin { @@ -119,6 +121,39 @@ namespace Bootstrap.Admin builder.ConfigureAppConfiguration(app => app.AddJsonFile(TestHelper.RetrievePath($"UnitTest{Path.DirectorySeparatorChar}appsettings.appveyor.json"), false, true)); } TestHelper.ConfigureWebHost(builder); + + // 替换 SMS 服务 + builder.ConfigureServices(services => + { + services.AddTransient(); + }); + } + + + /// + /// 手机号登陆帮助类 + /// + class DefaultSMSProvider : ISMSProvider + { + /// + /// 获得 短信配置信息 + /// + public SMSOptions Option { get; protected set; } = new SMSOptions(); + + /// + /// 下发验证码方法 + /// + /// + /// + public Task SendCodeAsync(string phoneNumber) => Task.FromResult(true); + + /// + /// 验证验证码方法 + /// + /// 手机号 + /// 验证码 + /// + public bool Validate(string phone, string code) => code == "1234"; } } } diff --git a/test/UnitTest/Bootstrap.Admin/Api/HttpHeaderOperationTest.cs b/test/UnitTest/Bootstrap.Admin/Api/HttpHeaderOperationTest.cs new file mode 100644 index 00000000..12b34cec --- /dev/null +++ b/test/UnitTest/Bootstrap.Admin/Api/HttpHeaderOperationTest.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using Xunit; + +namespace Bootstrap.Admin.Api +{ + public class HttpHeaderOperationTest + { + [Fact] + public void Apply_Ok() + { + var oper = new HttpHeaderOperation(); + var api = new OpenApiOperation(); + var desc = new ApiDescription(); + var mi = typeof(HttpHeaderOperationTest).GetMethod("Apply_Ok"); + var context = new OperationFilterContext(desc, null, null, mi); + oper.Apply(api, context); + } + } +} diff --git a/test/UnitTest/Bootstrap.Admin/Api/SQLServer/LoginTest.cs b/test/UnitTest/Bootstrap.Admin/Api/SQLServer/LoginTest.cs index df7d7e2d..e8b6dbd8 100644 --- a/test/UnitTest/Bootstrap.Admin/Api/SQLServer/LoginTest.cs +++ b/test/UnitTest/Bootstrap.Admin/Api/SQLServer/LoginTest.cs @@ -39,10 +39,9 @@ namespace Bootstrap.Admin.Api.SqlServer var _token = await resq.Content.ReadAsStringAsync(); Assert.Equal("false", _token); - // UNDONE: 重构短信登陆后完善 - //resq = await Client.PutAsync("?phone=", new StringContent("")); - //_token = await resq.Content.ReadAsStringAsync(); - //Assert.Equal("true", _token); + resq = await Client.PutAsync("?phone=18910001000", new StringContent("")); + _token = await resq.Content.ReadAsStringAsync(); + Assert.Equal("true", _token); } [Fact] diff --git a/test/UnitTest/Bootstrap.Admin/Controllers/SQLServer/AccountTest.cs b/test/UnitTest/Bootstrap.Admin/Controllers/SQLServer/AccountTest.cs index 1ebe11f9..e682c273 100644 --- a/test/UnitTest/Bootstrap.Admin/Controllers/SQLServer/AccountTest.cs +++ b/test/UnitTest/Bootstrap.Admin/Controllers/SQLServer/AccountTest.cs @@ -1,10 +1,8 @@ using Bootstrap.DataAccess; using System; -using System.Collections.Concurrent; using System.Linq; using System.Net; using System.Net.Http; -using System.Reflection; using Xunit; namespace Bootstrap.Admin.Controllers.SqlServer @@ -123,22 +121,54 @@ namespace Bootstrap.Admin.Controllers.SqlServer } [Fact] - public void Mobile_Ok() + public async void Mobile_Ok() { - // UNDONE: Mobile 单元测试未完成 + using (var db = DbManager.Create()) db.Execute("delete from Users where UserName = @0", "18910001000"); + var client = Host.CreateClient(); + var r = await client.GetAsync("/Account/Login"); + var view = await r.Content.ReadAsStringAsync(); + var tokenTag = ""); + var antiToken = view.Substring(0, index); - // 反射设置 SMSHelper 内部验证码保证 Validate 方法返回真 - var validateCodeInstance = Activator.CreateInstance(Type.GetType("Bootstrap.DataAccess.SMSHelper+AutoExpireValidateCode, Bootstrap.DataAccess"), new object[] { "18910001000", "1234", TimeSpan.FromSeconds(10)}); - var _poolInstance = typeof(SMSHelper).GetField("_pool", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); - //_pool.AddOrUpdate(option.Phone, key => new AutoExpireValidateCode(option.Phone, result.Data, option.Expires), (key, v) => v.Reset(result.Data)); - //var addMethod = _poolInstance.GetType().GetMethod("AddOrUpdate"); - //addMethod.Invoke(_poolInstance, new object[] { "18910001000", validateCodeInstance, null }); + var content = new MultipartFormDataContent + { + { new StringContent("18910001000"), "phone" }, + { new StringContent("1234"), "code" }, + { new StringContent(antiToken), "__RequestVerificationToken" } + }; + var m = await client.PostAsync("/Account/Mobile", content); + Assert.True(m.IsSuccessStatusCode); + var payload = await r.Content.ReadAsStringAsync(); + Assert.Contains("登 录", payload); + } - //var client = Host.CreateClient(); - //var r = await client.GetAsync($"/Account/Mobile?phone=18910001000&code=1234"); - //Assert.True(r.IsSuccessStatusCode); - //var content = await r.Content.ReadAsStringAsync(); - //Assert.Contains("登 录", content); + [Fact] + public async void Mobile_Fail() + { + using (var db = DbManager.Create()) db.Execute("delete from Users where UserName = @0", "18910001000"); + + var client = Host.CreateClient(); + var r = await client.GetAsync("/Account/Login"); + var view = await r.Content.ReadAsStringAsync(); + var tokenTag = ""); + var antiToken = view.Substring(0, index); + + var content = new MultipartFormDataContent + { + { new StringContent("18910001000"), "phone" }, + { new StringContent("1000"), "code" }, + { new StringContent(antiToken), "__RequestVerificationToken" } + }; + var m = await client.PostAsync("/Account/Mobile?AppId=0", content); + Assert.True(m.IsSuccessStatusCode); + var payload = await r.Content.ReadAsStringAsync(); + Assert.Contains("登 录", payload); } } } diff --git a/test/UnitTest/Bootstrap.DataAccess/MongoDB/DBLogTest.cs b/test/UnitTest/Bootstrap.DataAccess/MongoDB/DBLogTest.cs new file mode 100644 index 00000000..47ff8061 --- /dev/null +++ b/test/UnitTest/Bootstrap.DataAccess/MongoDB/DBLogTest.cs @@ -0,0 +1,14 @@ +using Xunit; + +namespace Bootstrap.DataAccess.MongoDB +{ + [Collection("MongoContext")] + public class DBLogTest : SqlServer.DBLogTest + { + [Fact] + public override void Save_Ok() + { + Assert.True(new DBLog().Save(null)); + } + } +} diff --git a/test/UnitTest/Bootstrap.DataAccess/MySql/DBLogTest.cs b/test/UnitTest/Bootstrap.DataAccess/MySql/DBLogTest.cs new file mode 100644 index 00000000..3692ecf8 --- /dev/null +++ b/test/UnitTest/Bootstrap.DataAccess/MySql/DBLogTest.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Bootstrap.DataAccess.MySql +{ + [Collection("MySqlContext")] + public class DBLogTest : SqlServer.DBLogTest + { + + } +} diff --git a/test/UnitTest/Bootstrap.DataAccess/SQLServer/DBLogTest.cs b/test/UnitTest/Bootstrap.DataAccess/SQLServer/DBLogTest.cs new file mode 100644 index 00000000..9c2bf32a --- /dev/null +++ b/test/UnitTest/Bootstrap.DataAccess/SQLServer/DBLogTest.cs @@ -0,0 +1,28 @@ +using System; +using Xunit; + +namespace Bootstrap.DataAccess.SqlServer +{ + [Collection("SQLServerContext")] + public class DBLogTest + { + [Fact] + public virtual void Save_Ok() + { + var log = new DBLog() + { + Id = "", + LogTime = DateTime.Now, + SQL = "UnitTest", + UserName = "UniTest" + }; + Assert.True(log.Save(log)); + } + + [Fact] + public void Save_Exception() + { + Assert.Throws(() => new DBLog().Save(null)); + } + } +} diff --git a/test/UnitTest/Bootstrap.DataAccess/SQLite/DBLogTest.cs b/test/UnitTest/Bootstrap.DataAccess/SQLite/DBLogTest.cs new file mode 100644 index 00000000..40ded307 --- /dev/null +++ b/test/UnitTest/Bootstrap.DataAccess/SQLite/DBLogTest.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Bootstrap.DataAccess.SQLite +{ + [Collection("SQLiteContext")] + public class DBLogTest : SqlServer.DBLogTest + { + + } +}