Moved all UnitTests to a single project. Implemented parts of AspNetCore UnitTests.
This commit is contained in:
@@ -8,7 +8,7 @@ stages:
|
|||||||
- test
|
- test
|
||||||
- deploy
|
- deploy
|
||||||
|
|
||||||
build_job:
|
build:
|
||||||
stage: build
|
stage: build
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
@@ -20,22 +20,25 @@ build_job:
|
|||||||
- artifacts/*.snupkg
|
- artifacts/*.snupkg
|
||||||
expire_in: 1 day
|
expire_in: 1 day
|
||||||
|
|
||||||
test_job:
|
test:
|
||||||
stage: test
|
stage: test
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
coverage: '/Total[^|]*\|[^|]*\|\s*([0-9.%]+)/'
|
# branch-coverage
|
||||||
|
# coverage: '/Total[^|]*\|[^|]*\|\s*([0-9.%]+)/'
|
||||||
|
# line-coverage
|
||||||
|
coverage: '/Total[^|]*\|\s*([0-9.%]+)/'
|
||||||
script:
|
script:
|
||||||
- dotnet test -c Release
|
- dotnet test -c Release
|
||||||
dependencies:
|
dependencies:
|
||||||
- build_job
|
- build
|
||||||
|
|
||||||
deploy_job:
|
deploy:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
script:
|
script:
|
||||||
- dotnet nuget push -k $APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/*.nupkg
|
- dotnet nuget push -k $APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/*.nupkg
|
||||||
dependencies:
|
dependencies:
|
||||||
- build_job
|
- build
|
||||||
- test_job
|
- test
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Authorization
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A basic authentication as attribute to use for specific actions.
|
/// A basic authentication as attribute to use for specific actions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||||
public class BasicAuthenticationAttribute : Attribute, IAsyncAuthorizationFilter
|
public class BasicAuthenticationAttribute : Attribute, IAsyncAuthorizationFilter
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -53,11 +54,14 @@ namespace Microsoft.AspNetCore.Authorization
|
|||||||
var authHeader = AuthenticationHeaderValue.Parse(context.HttpContext.Request.Headers["Authorization"]);
|
var authHeader = AuthenticationHeaderValue.Parse(context.HttpContext.Request.Headers["Authorization"]);
|
||||||
byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
|
byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
|
||||||
string plain = Encoding.UTF8.GetString(decoded);
|
string plain = Encoding.UTF8.GetString(decoded);
|
||||||
string[] credentials = plain.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
// See: https://www.rfc-editor.org/rfc/rfc2617, page 6
|
||||||
|
string username = plain.Split(':').First();
|
||||||
|
string password = plain[(username.Length + 1)..];
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password))
|
if (!string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password))
|
||||||
{
|
{
|
||||||
if (Username == credentials.First() && Password == credentials.Last())
|
if (Username == username && Password == password)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +86,7 @@ namespace Microsoft.AspNetCore.Authorization
|
|||||||
|
|
||||||
context.HttpContext.Response.Headers["WWW-Authenticate"] = "Basic";
|
context.HttpContext.Response.Headers["WWW-Authenticate"] = "Basic";
|
||||||
if (!string.IsNullOrWhiteSpace(realm))
|
if (!string.IsNullOrWhiteSpace(realm))
|
||||||
context.HttpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{realm.Trim().Replace("\"", "")}\"";
|
context.HttpContext.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{realm.Trim().Replace("\"", "")}\"";
|
||||||
|
|
||||||
context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
|
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
|
||||||
@@ -98,22 +102,29 @@ namespace Microsoft.AspNetCore.Authorization
|
|||||||
var authHeader = AuthenticationHeaderValue.Parse(context.HttpContext.Request.Headers["Authorization"]);
|
var authHeader = AuthenticationHeaderValue.Parse(context.HttpContext.Request.Headers["Authorization"]);
|
||||||
byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
|
byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
|
||||||
string plain = Encoding.UTF8.GetString(decoded);
|
string plain = Encoding.UTF8.GetString(decoded);
|
||||||
string[] credentials = plain.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
// See: https://www.rfc-editor.org/rfc/rfc2617, page 6
|
||||||
|
string username = plain.Split(':').First();
|
||||||
|
string password = plain[(username.Length + 1)..];
|
||||||
|
|
||||||
var validator = context.HttpContext.RequestServices.GetService<IBasicAuthenticationValidator>();
|
var validator = context.HttpContext.RequestServices.GetService<IBasicAuthenticationValidator>();
|
||||||
var result = await validator?.ValidateAsync(credentials.First(), credentials.Last(), context.HttpContext.GetRemoteIpAddress());
|
if (validator == null)
|
||||||
if (result != null)
|
return null;
|
||||||
context.HttpContext.User = result;
|
|
||||||
|
|
||||||
|
var result = await validator.ValidateAsync(username, password, context.HttpContext.GetRemoteIpAddress());
|
||||||
|
if (result == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
context.HttpContext.User = result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger?.LogError(ex, $"Using validator to get HTTP user failed: {ex.InnerException?.Message ?? ex.Message}");
|
logger?.LogError(ex, $"Using validator to get HTTP user failed: {ex.InnerException?.Message ?? ex.Message}");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.Filters
|
|||||||
/// <br/>
|
/// <br/>
|
||||||
/// The score from google can be found on HttpContext.Items[GoogleReCaptchaAttribute.ScoreKey].
|
/// The score from google can be found on HttpContext.Items[GoogleReCaptchaAttribute.ScoreKey].
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public class GoogleReCaptchaAttribute : ActionFilterAttribute
|
public class GoogleReCaptchaAttribute : ActionFilterAttribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements the <see cref="AuthenticationHandler{TOptions}"/> for Basic Authentication.
|
/// Implements the <see cref="AuthenticationHandler{TOptions}"/> for Basic Authentication.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||||
{
|
{
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
@@ -51,10 +52,13 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
|
|||||||
{
|
{
|
||||||
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
|
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
|
||||||
string plain = Encoding.UTF8.GetString(Convert.FromBase64String(authHeader.Parameter));
|
string plain = Encoding.UTF8.GetString(Convert.FromBase64String(authHeader.Parameter));
|
||||||
string[] credentials = plain.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
// See: https://www.rfc-editor.org/rfc/rfc2617, page 6
|
||||||
|
string username = plain.Split(':').First();
|
||||||
|
string password = plain[(username.Length + 1)..];
|
||||||
|
|
||||||
var ipAddress = Context.GetRemoteIpAddress();
|
var ipAddress = Context.GetRemoteIpAddress();
|
||||||
principal = await validator.ValidateAsync(credentials.First(), credentials.Last(), ipAddress);
|
principal = await validator.ValidateAsync(username, password, ipAddress);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Net.Http.Headers;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace AMWD.Common.AspNetCore.BasicAuthentication
|
namespace AMWD.Common.AspNetCore.BasicAuthentication
|
||||||
{
|
{
|
||||||
@@ -45,9 +46,12 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
|
|||||||
var authHeader = AuthenticationHeaderValue.Parse(httpContext.Request.Headers["Authorization"]);
|
var authHeader = AuthenticationHeaderValue.Parse(httpContext.Request.Headers["Authorization"]);
|
||||||
byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
|
byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
|
||||||
string plain = Encoding.UTF8.GetString(decoded);
|
string plain = Encoding.UTF8.GetString(decoded);
|
||||||
string[] credentials = plain.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
var principal = await validator.ValidateAsync(credentials.First(), credentials.Last(), httpContext.GetRemoteIpAddress());
|
// See: https://www.rfc-editor.org/rfc/rfc2617, page 6
|
||||||
|
string username = plain.Split(':').First();
|
||||||
|
string password = plain[(username.Length + 1)..];
|
||||||
|
|
||||||
|
var principal = await validator.ValidateAsync(username, password, httpContext.GetRemoteIpAddress());
|
||||||
if (principal == null)
|
if (principal == null)
|
||||||
{
|
{
|
||||||
SetAuthenticateRequest(httpContext, validator.Realm);
|
SetAuthenticateRequest(httpContext, validator.Realm);
|
||||||
@@ -56,9 +60,11 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
|
|||||||
|
|
||||||
await next.Invoke(httpContext);
|
await next.Invoke(httpContext);
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
SetAuthenticateRequest(httpContext, validator.Realm);
|
var logger = (ILogger<BasicAuthenticationMiddleware>)httpContext.RequestServices.GetService(typeof(ILogger<BasicAuthenticationMiddleware>));
|
||||||
|
logger?.LogError(ex, $"Falied to execute basic authentication middleware: {ex.InnerException?.Message ?? ex.Message}");
|
||||||
|
httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +72,7 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
|
|||||||
{
|
{
|
||||||
httpContext.Response.Headers["WWW-Authenticate"] = "Basic";
|
httpContext.Response.Headers["WWW-Authenticate"] = "Basic";
|
||||||
if (!string.IsNullOrWhiteSpace(realm))
|
if (!string.IsNullOrWhiteSpace(realm))
|
||||||
httpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{realm.Replace("\"", "")}\"";
|
httpContext.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{realm.Replace("\"", "")}\"";
|
||||||
|
|
||||||
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extensions for the <see cref="IApplicationBuilder"/>.
|
/// Extensions for the <see cref="IApplicationBuilder"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public static class ApplicationBuilderExtensions
|
public static class ApplicationBuilderExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace AMWD.Common.AspNetCore.Extensions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extensions for the HTML (e.g. <see cref="IHtmlHelper"/>).
|
/// Extensions for the HTML (e.g. <see cref="IHtmlHelper"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public static class HtmlExtensions
|
public static class HtmlExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -30,13 +30,11 @@ namespace Microsoft.AspNetCore.Http
|
|||||||
/// <returns>The ip address of the client.</returns>
|
/// <returns>The ip address of the client.</returns>
|
||||||
public static IPAddress GetRemoteIpAddress(this HttpContext httpContext, string headerName = "X-Forwarded-For")
|
public static IPAddress GetRemoteIpAddress(this HttpContext httpContext, string headerName = "X-Forwarded-For")
|
||||||
{
|
{
|
||||||
var remote = httpContext.Connection.RemoteIpAddress;
|
|
||||||
|
|
||||||
string forwardedHeader = httpContext.Request.Headers[headerName].ToString();
|
string forwardedHeader = httpContext.Request.Headers[headerName].ToString();
|
||||||
if (!string.IsNullOrWhiteSpace(forwardedHeader) && IPAddress.TryParse(forwardedHeader, out var forwarded))
|
if (!string.IsNullOrWhiteSpace(forwardedHeader) && IPAddress.TryParse(forwardedHeader, out var forwarded))
|
||||||
return forwarded;
|
return forwarded;
|
||||||
|
|
||||||
return remote;
|
return httpContext.Connection.RemoteIpAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -58,11 +56,13 @@ namespace Microsoft.AspNetCore.Http
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string GetReturnUrl(this HttpContext httpContext)
|
public static string GetReturnUrl(this HttpContext httpContext)
|
||||||
{
|
{
|
||||||
string url = httpContext.Items["OriginalRequest"]?.ToString();
|
if (httpContext.Items.ContainsKey("OriginalRequest"))
|
||||||
if (string.IsNullOrWhiteSpace(url))
|
return httpContext.Items["OriginalRequest"].ToString();
|
||||||
url = httpContext.Request.Query["ReturnUrl"].ToString();
|
|
||||||
|
|
||||||
return url;
|
if (httpContext.Request.Query.ContainsKey("ReturnUrl"))
|
||||||
|
return httpContext.Request.Query["ReturnUrl"].ToString();
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -70,6 +70,6 @@ namespace Microsoft.AspNetCore.Http
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
|
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
|
||||||
public static void ClearSession(this HttpContext httpContext)
|
public static void ClearSession(this HttpContext httpContext)
|
||||||
=> httpContext?.Session?.Clear();
|
=> httpContext.Session?.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace Microsoft.Extensions.Logging
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extensions for the <see cref="ILogger"/>.
|
/// Extensions for the <see cref="ILogger"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
internal static class LoggerExtensions
|
internal static class LoggerExtensions
|
||||||
{
|
{
|
||||||
// Found here:
|
// Found here:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extensions for the <see cref="IServiceCollection"/>.
|
/// Extensions for the <see cref="IServiceCollection"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public static class ServiceCollectionExtensions
|
public static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom floating point ModelBinder as the team of Microsoft is not capable of fixing their <see href="https://github.com/dotnet/aspnetcore/issues/6566">issue</see> with other cultures than en-US.
|
/// Custom floating point ModelBinder as the team of Microsoft is not capable of fixing their <see href="https://github.com/dotnet/aspnetcore/issues/6566">issue</see> with other cultures than en-US.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public class InvariantFloatingPointModelBinder : IModelBinder
|
public class InvariantFloatingPointModelBinder : IModelBinder
|
||||||
{
|
{
|
||||||
private readonly NumberStyles supportedNumberStyles;
|
private readonly NumberStyles supportedNumberStyles;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||||||
/// options.ModelBinderProviders.Insert(0, new CustomFloatingPointModelBinderProvider());<br/>
|
/// options.ModelBinderProviders.Insert(0, new CustomFloatingPointModelBinderProvider());<br/>
|
||||||
/// });</code>
|
/// });</code>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public class InvariantFloatingPointModelBinderProvider : IModelBinderProvider
|
public class InvariantFloatingPointModelBinderProvider : IModelBinderProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A tag helper that adds a CSS class attribute based on a condition.
|
/// A tag helper that adds a CSS class attribute based on a condition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
[HtmlTargetElement(Attributes = ClassPrefix + "*")]
|
[HtmlTargetElement(Attributes = ClassPrefix + "*")]
|
||||||
public class ConditionClassTagHelper : TagHelper
|
public class ConditionClassTagHelper : TagHelper
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A tag helper to create a obfuscated email link.
|
/// A tag helper to create a obfuscated email link.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
[HtmlTargetElement("email", TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("email", TagStructure = TagStructure.WithoutEndTag)]
|
||||||
public class EmailTagHelper : TagHelper
|
public class EmailTagHelper : TagHelper
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A tag helper to dynamically create integrity checks for linked sources.
|
/// A tag helper to dynamically create integrity checks for linked sources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
[HtmlTargetElement("link")]
|
[HtmlTargetElement("link")]
|
||||||
[HtmlTargetElement("script")]
|
[HtmlTargetElement("script")]
|
||||||
public class IntegrityHashTagHelper : TagHelper
|
public class IntegrityHashTagHelper : TagHelper
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds additional behavior to the modelbinding for numeric properties.
|
/// Adds additional behavior to the modelbinding for numeric properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
[HtmlTargetElement("input", Attributes = "asp-for")]
|
[HtmlTargetElement("input", Attributes = "asp-for")]
|
||||||
public class NumberInputTagHelper : InputTagHelper
|
public class NumberInputTagHelper : InputTagHelper
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace Microsoft.Extensions.Hosting
|
|||||||
/// Wrapper class to start a background service.
|
/// Wrapper class to start a background service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TService">The service type.</typeparam>
|
/// <typeparam name="TService">The service type.</typeparam>
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
public class BackgroundServiceStarter<TService> : IHostedService
|
public class BackgroundServiceStarter<TService> : IHostedService
|
||||||
where TService : class, IHostedService
|
where TService : class, IHostedService
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace AMWD.Common.AspNetCore.Utilities
|
|||||||
public static bool IsDarkColor(string color)
|
public static bool IsDarkColor(string color)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(color))
|
if (string.IsNullOrWhiteSpace(color))
|
||||||
return false;
|
throw new ArgumentNullException(nameof(color));
|
||||||
|
|
||||||
int r, g, b;
|
int r, g, b;
|
||||||
|
|
||||||
@@ -27,9 +27,9 @@ namespace AMWD.Common.AspNetCore.Utilities
|
|||||||
|
|
||||||
if (rgbMatch.Success)
|
if (rgbMatch.Success)
|
||||||
{
|
{
|
||||||
r = Convert.ToInt32(rgbMatch.Groups[1].Value);
|
r = Convert.ToInt32(rgbMatch.Groups[1].Value, 10);
|
||||||
g = Convert.ToInt32(rgbMatch.Groups[2].Value);
|
g = Convert.ToInt32(rgbMatch.Groups[2].Value, 10);
|
||||||
b = Convert.ToInt32(rgbMatch.Groups[3].Value);
|
b = Convert.ToInt32(rgbMatch.Groups[3].Value, 10);
|
||||||
}
|
}
|
||||||
else if (hexMatchFull.Success)
|
else if (hexMatchFull.Success)
|
||||||
{
|
{
|
||||||
@@ -45,7 +45,7 @@ namespace AMWD.Common.AspNetCore.Utilities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
throw new NotSupportedException($"Unknown color value '{color}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
double luminance = (r * 0.299 + g * 0.587 + b * 0.114) / 255;
|
double luminance = (r * 0.299 + g * 0.587 + b * 0.114) / 255;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("AMWD.Common.Tests")]
|
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
|
||||||
|
|
||||||
namespace System
|
namespace System
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased](https://git.am-wd.de/AM.WD/common/compare/v1.6.1...master) - 0000-00-00
|
## [Unreleased](https://git.am-wd.de/AM.WD/common/compare/v1.6.1...master) - 0000-00-00
|
||||||
|
### Added
|
||||||
|
- UnitTests for `AspNetCore` as far as testable without massive work.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- `BasicAuthenticationAttribute` now respects the `IBasicAuthenticationValidator.Realm` when the own `Realm` property is not set.
|
- `BasicAuthenticationAttribute` now respects the `IBasicAuthenticationValidator.Realm` when the own `Realm` property is not set.
|
||||||
- CI scripts
|
- CI scripts
|
||||||
|
|||||||
14
Common.sln
14
Common.sln
@@ -21,14 +21,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
README.md = README.md
|
README.md = README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.Tests", "AMWD.Common.Tests\AMWD.Common.Tests.csproj", "{086E3C11-454A-4C8F-AEAA-215BAE9C443F}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F2C7556A-99EB-43EB-8954-56A24AFE928F}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F2C7556A-99EB-43EB-8954-56A24AFE928F}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.Moq", "AMWD.Common.Moq\AMWD.Common.Moq.csproj", "{6EBA2792-0B66-4C90-89A1-4E1D26D16443}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.Moq", "AMWD.Common.Moq\AMWD.Common.Moq.csproj", "{6EBA2792-0B66-4C90-89A1-4E1D26D16443}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "UnitTests\UnitTests.csproj", "{9469D87B-126E-4338-92E3-701F762CB54D}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -47,14 +47,14 @@ Global
|
|||||||
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.Build.0 = Release|Any CPU
|
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{086E3C11-454A-4C8F-AEAA-215BAE9C443F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{086E3C11-454A-4C8F-AEAA-215BAE9C443F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{086E3C11-454A-4C8F-AEAA-215BAE9C443F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{086E3C11-454A-4C8F-AEAA-215BAE9C443F}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Release|Any CPU.Build.0 = Release|Any CPU
|
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9469D87B-126E-4338-92E3-701F762CB54D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9469D87B-126E-4338-92E3-701F762CB54D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -63,8 +63,8 @@ Global
|
|||||||
{F512C474-B670-4E47-911E-7C0674AA8E7E} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
{F512C474-B670-4E47-911E-7C0674AA8E7E} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
||||||
{725F40C9-8172-487F-B3D0-D7E38B4DB197} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
{725F40C9-8172-487F-B3D0-D7E38B4DB197} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
||||||
{7091CECF-C981-4FB9-9CC6-91C4E65A6356} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
{7091CECF-C981-4FB9-9CC6-91C4E65A6356} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
||||||
{086E3C11-454A-4C8F-AEAA-215BAE9C443F} = {E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}
|
|
||||||
{6EBA2792-0B66-4C90-89A1-4E1D26D16443} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
{6EBA2792-0B66-4C90-89A1-4E1D26D16443} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
|
||||||
|
{9469D87B-126E-4338-92E3-701F762CB54D} = {E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {961E8DF8-DDF5-4D10-A510-CE409E9962AC}
|
SolutionGuid = {961E8DF8-DDF5-4D10-A510-CE409E9962AC}
|
||||||
|
|||||||
@@ -0,0 +1,346 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AMWD.Common.AspNetCore.BasicAuthentication;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace UnitTests.AspNetCore.Attributes
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class BasicAuthenticationAttributeTests
|
||||||
|
{
|
||||||
|
private Mock<IHeaderDictionary> requestHeaderMock;
|
||||||
|
private Mock<IHeaderDictionary> responseHeaderMock;
|
||||||
|
|
||||||
|
private Mock<HttpRequest> requestMock;
|
||||||
|
private Mock<HttpResponse> responseMock;
|
||||||
|
|
||||||
|
private Mock<HttpContext> contextMock;
|
||||||
|
|
||||||
|
private Dictionary<string, string> requestHeaders;
|
||||||
|
private string validatorRealm;
|
||||||
|
private ClaimsPrincipal validatorResult;
|
||||||
|
|
||||||
|
private string responseHeaderAuthCallback;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void InitializeTest()
|
||||||
|
{
|
||||||
|
requestHeaders = new Dictionary<string, string>();
|
||||||
|
validatorRealm = null;
|
||||||
|
validatorResult = null;
|
||||||
|
|
||||||
|
responseHeaderAuthCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldValidateViaUsernamePassword()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new BasicAuthenticationAttribute
|
||||||
|
{
|
||||||
|
Username = "user",
|
||||||
|
Password = "password"
|
||||||
|
};
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}"))}");
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
Assert.IsTrue(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldValidateViaValidator()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new BasicAuthenticationAttribute();
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}"))}");
|
||||||
|
validatorResult = new ClaimsPrincipal();
|
||||||
|
|
||||||
|
var context = GetContext(hasValidator: true);
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
Assert.IsTrue(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldAllowAnonymous()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new BasicAuthenticationAttribute
|
||||||
|
{
|
||||||
|
Username = "user",
|
||||||
|
Password = "password"
|
||||||
|
};
|
||||||
|
var context = GetContext(isAnonymousAllowed: true);
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
Assert.IsTrue(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldAskOnUsernamePasswordWithoutRealm()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new BasicAuthenticationAttribute
|
||||||
|
{
|
||||||
|
Username = "user",
|
||||||
|
Password = "password"
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||||
|
Assert.AreEqual("Basic", responseHeaderAuthCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldAskOnUsernamePasswordWithRealm()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new BasicAuthenticationAttribute
|
||||||
|
{
|
||||||
|
Username = "user",
|
||||||
|
Password = "password",
|
||||||
|
Realm = "re:al\"m"
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||||
|
Assert.AreEqual("Basic realm=\"re:alm\"", responseHeaderAuthCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldAskOnUsernamePasswordWrongUser()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new BasicAuthenticationAttribute
|
||||||
|
{
|
||||||
|
Username = "user",
|
||||||
|
Password = "password"
|
||||||
|
};
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}a:{attribute.Password}"))}");
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||||
|
Assert.AreEqual("Basic", responseHeaderAuthCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldAskOnUsernamePasswordWrongPassword()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new BasicAuthenticationAttribute
|
||||||
|
{
|
||||||
|
Username = "user",
|
||||||
|
Password = "password"
|
||||||
|
};
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}a"))}");
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||||
|
Assert.AreEqual("Basic", responseHeaderAuthCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldAskOnValidatorWithRealmOnAttribute()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new BasicAuthenticationAttribute
|
||||||
|
{
|
||||||
|
Realm = "attribute"
|
||||||
|
};
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}"))}");
|
||||||
|
var context = GetContext(hasValidator: true);
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||||
|
Assert.AreEqual("Basic realm=\"attribute\"", responseHeaderAuthCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldAskOnValidatorWithRealmOnValidator()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
validatorRealm = "validator";
|
||||||
|
var attribute = new BasicAuthenticationAttribute();
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}"))}");
|
||||||
|
var context = GetContext(hasValidator: true);
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||||
|
Assert.AreEqual("Basic realm=\"validator\"", responseHeaderAuthCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldReturnInternalError()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new BasicAuthenticationAttribute();
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}"))}");
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
await attribute.OnAuthorizationAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(500, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthorizationFilterContext GetContext(bool isAnonymousAllowed = false, bool hasValidator = false)
|
||||||
|
{
|
||||||
|
requestHeaderMock = new Mock<IHeaderDictionary>();
|
||||||
|
foreach (var header in requestHeaders)
|
||||||
|
{
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h.ContainsKey(header.Key))
|
||||||
|
.Returns(true);
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h[header.Key])
|
||||||
|
.Returns(header.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseHeaderMock = new Mock<IHeaderDictionary>();
|
||||||
|
responseHeaderMock
|
||||||
|
.SetupSet(h => h["WWW-Authenticate"] = It.IsAny<StringValues>())
|
||||||
|
.Callback<string, StringValues>((key, value) =>
|
||||||
|
{
|
||||||
|
responseHeaderAuthCallback = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
requestMock = new Mock<HttpRequest>();
|
||||||
|
requestMock
|
||||||
|
.Setup(r => r.Headers)
|
||||||
|
.Returns(requestHeaderMock.Object);
|
||||||
|
|
||||||
|
responseMock = new Mock<HttpResponse>();
|
||||||
|
responseMock
|
||||||
|
.Setup(r => r.Headers)
|
||||||
|
.Returns(responseHeaderMock.Object);
|
||||||
|
|
||||||
|
var requestServicesMock = new Mock<IServiceProvider>();
|
||||||
|
|
||||||
|
if (hasValidator)
|
||||||
|
{
|
||||||
|
var validatorMock = new Mock<IBasicAuthenticationValidator>();
|
||||||
|
validatorMock
|
||||||
|
.Setup(v => v.Realm)
|
||||||
|
.Returns(validatorRealm);
|
||||||
|
validatorMock
|
||||||
|
.Setup(v => v.ValidateAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IPAddress>()))
|
||||||
|
.ReturnsAsync(validatorResult);
|
||||||
|
|
||||||
|
requestServicesMock
|
||||||
|
.Setup(rs => rs.GetService(typeof(IBasicAuthenticationValidator)))
|
||||||
|
.Returns(validatorMock.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
var connectionInfoMock = new Mock<ConnectionInfo>();
|
||||||
|
connectionInfoMock
|
||||||
|
.Setup(ci => ci.RemoteIpAddress)
|
||||||
|
.Returns(IPAddress.Loopback);
|
||||||
|
|
||||||
|
contextMock = new Mock<HttpContext>();
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Request)
|
||||||
|
.Returns(requestMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Response)
|
||||||
|
.Returns(responseMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.RequestServices)
|
||||||
|
.Returns(requestServicesMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Connection)
|
||||||
|
.Returns(connectionInfoMock.Object);
|
||||||
|
|
||||||
|
var routeDataMock = new Mock<RouteData>();
|
||||||
|
|
||||||
|
var actionDescriptor = new ActionDescriptor
|
||||||
|
{
|
||||||
|
EndpointMetadata = new List<object>()
|
||||||
|
};
|
||||||
|
if (isAnonymousAllowed)
|
||||||
|
actionDescriptor.EndpointMetadata.Add(new AllowAnonymousAttribute());
|
||||||
|
|
||||||
|
return new AuthorizationFilterContext(new ActionContext
|
||||||
|
{
|
||||||
|
HttpContext = contextMock.Object,
|
||||||
|
RouteData = routeDataMock.Object,
|
||||||
|
ActionDescriptor = actionDescriptor,
|
||||||
|
}, new List<IFilterMetadata>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
340
UnitTests/AspNetCore/Attributes/IPBlacklistAttributeTests.cs
Normal file
340
UnitTests/AspNetCore/Attributes/IPBlacklistAttributeTests.cs
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace UnitTests.AspNetCore.Attributes
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class IPBlacklistAttributeTests
|
||||||
|
{
|
||||||
|
private Dictionary<string, string> requestHeaders;
|
||||||
|
private Dictionary<object, object> itemsCallback;
|
||||||
|
private string configKey;
|
||||||
|
private bool configExists;
|
||||||
|
private List<string> restrictedIpsConfig;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void InitializeTest()
|
||||||
|
{
|
||||||
|
requestHeaders = new Dictionary<string, string>();
|
||||||
|
itemsCallback = new Dictionary<object, object>();
|
||||||
|
configKey = null;
|
||||||
|
configExists = false;
|
||||||
|
restrictedIpsConfig = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldAllowOnNoConfiguration()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var remote = IPAddress.Parse("192.168.178.1");
|
||||||
|
var attribute = new IPBlacklistAttribute();
|
||||||
|
var context = GetContext(remote);
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldAllowOnWrongConfiguration()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var remote = IPAddress.Parse("192.168.178.1");
|
||||||
|
var attribute = new IPBlacklistAttribute
|
||||||
|
{
|
||||||
|
RestrictedIpAddresses = "192.168.178:1"
|
||||||
|
};
|
||||||
|
var context = GetContext(remote);
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldAllowLocalAccess()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new IPBlacklistAttribute
|
||||||
|
{
|
||||||
|
RestrictLocalAccess = false,
|
||||||
|
RestrictedIpAddresses = "127.0.0.0/8"
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldBlockLocalAccess()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new IPBlacklistAttribute
|
||||||
|
{
|
||||||
|
RestrictLocalAccess = true,
|
||||||
|
RestrictedIpAddresses = ",127.0.0.0/8"
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("192.168.178.10")]
|
||||||
|
[DataRow("192.168.178.20")]
|
||||||
|
public void ShouldBlockSpecificAddress(string address)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var remote = IPAddress.Parse(address);
|
||||||
|
var attribute = new IPBlacklistAttribute
|
||||||
|
{
|
||||||
|
RestrictLocalAccess = true,
|
||||||
|
RestrictedIpAddresses = "127.0.0.0/8,192.168.178.10"
|
||||||
|
};
|
||||||
|
var context = GetContext(remote);
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
if (address == "192.168.178.10")
|
||||||
|
{
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldAllowLocalAccessConfig()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
configKey = "Black:List";
|
||||||
|
configExists = true;
|
||||||
|
restrictedIpsConfig.Add("127.0.0.0/8");
|
||||||
|
restrictedIpsConfig.Add("192.168.178.10");
|
||||||
|
var attribute = new IPBlacklistAttribute
|
||||||
|
{
|
||||||
|
RestrictLocalAccess = false,
|
||||||
|
ConfigurationKey = configKey
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldBlockLocalAccessConfig()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
configKey = "Black:List";
|
||||||
|
configExists = true;
|
||||||
|
restrictedIpsConfig.Add("");
|
||||||
|
restrictedIpsConfig.Add("127.0.0.0/8");
|
||||||
|
restrictedIpsConfig.Add("192.168.178.10");
|
||||||
|
var attribute = new IPBlacklistAttribute
|
||||||
|
{
|
||||||
|
RestrictLocalAccess = true,
|
||||||
|
ConfigurationKey = configKey
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("192.168.178.10")]
|
||||||
|
[DataRow("192.168.178.20")]
|
||||||
|
public void ShouldBlockSpecificAddressConfig(string address)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
configKey = "Black:List";
|
||||||
|
configExists = true;
|
||||||
|
restrictedIpsConfig.Add("127.0.0.0/8");
|
||||||
|
restrictedIpsConfig.Add("192.168.178.10");
|
||||||
|
var attribute = new IPBlacklistAttribute
|
||||||
|
{
|
||||||
|
RestrictLocalAccess = true,
|
||||||
|
ConfigurationKey = configKey
|
||||||
|
};
|
||||||
|
var remote = IPAddress.Parse(address);
|
||||||
|
var context = GetContext(remote);
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
if (address == "192.168.178.10")
|
||||||
|
{
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldAllowOnMissingConfiguration()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
configKey = "Black:List";
|
||||||
|
configExists = false;
|
||||||
|
var attribute = new IPBlacklistAttribute
|
||||||
|
{
|
||||||
|
RestrictLocalAccess = true,
|
||||||
|
ConfigurationKey = configKey
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionExecutingContext GetContext(IPAddress remote = null)
|
||||||
|
{
|
||||||
|
var requestHeaderMock = new Mock<IHeaderDictionary>();
|
||||||
|
foreach (var header in requestHeaders)
|
||||||
|
{
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h.ContainsKey(header.Key))
|
||||||
|
.Returns(true);
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h[header.Key])
|
||||||
|
.Returns(header.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestMock = new Mock<HttpRequest>();
|
||||||
|
requestMock
|
||||||
|
.Setup(r => r.Headers)
|
||||||
|
.Returns(requestHeaderMock.Object);
|
||||||
|
|
||||||
|
var connectionInfoMock = new Mock<ConnectionInfo>();
|
||||||
|
connectionInfoMock
|
||||||
|
.Setup(ci => ci.LocalIpAddress)
|
||||||
|
.Returns(IPAddress.Loopback);
|
||||||
|
connectionInfoMock
|
||||||
|
.Setup(ci => ci.RemoteIpAddress)
|
||||||
|
.Returns(remote ?? IPAddress.Loopback);
|
||||||
|
|
||||||
|
var itemsMock = new Mock<IDictionary<object, object>>();
|
||||||
|
itemsMock
|
||||||
|
.SetupSet(i => i[It.IsAny<object>()] = It.IsAny<object>())
|
||||||
|
.Callback<object, object>((key, val) => itemsCallback.Add(key, val));
|
||||||
|
|
||||||
|
var configurationMock = new Mock<IConfiguration>();
|
||||||
|
var children = new List<IConfigurationSection>();
|
||||||
|
foreach (string ipAddress in restrictedIpsConfig)
|
||||||
|
{
|
||||||
|
var csm = new Mock<IConfigurationSection>();
|
||||||
|
csm.Setup(cs => cs.Value).Returns(ipAddress);
|
||||||
|
|
||||||
|
children.Add(csm.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
var configSectionMock = new Mock<IConfigurationSection>();
|
||||||
|
configSectionMock
|
||||||
|
.Setup(cs => cs.GetChildren())
|
||||||
|
.Returns(children);
|
||||||
|
|
||||||
|
configurationMock
|
||||||
|
.Setup(c => c.GetSection(configKey))
|
||||||
|
.Returns(configExists ? configSectionMock.Object : null);
|
||||||
|
|
||||||
|
var requestServicesMock = new Mock<IServiceProvider>();
|
||||||
|
requestServicesMock
|
||||||
|
.Setup(s => s.GetService(typeof(IConfiguration)))
|
||||||
|
.Returns(configurationMock.Object);
|
||||||
|
|
||||||
|
var contextMock = new Mock<HttpContext>();
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Request)
|
||||||
|
.Returns(requestMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.RequestServices)
|
||||||
|
.Returns(requestServicesMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Items)
|
||||||
|
.Returns(itemsMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Connection)
|
||||||
|
.Returns(connectionInfoMock.Object);
|
||||||
|
|
||||||
|
var routeDataMock = new Mock<RouteData>();
|
||||||
|
var actionDescriptorMock = new Mock<ActionDescriptor>();
|
||||||
|
|
||||||
|
return new ActionExecutingContext(new ActionContext
|
||||||
|
{
|
||||||
|
HttpContext = contextMock.Object,
|
||||||
|
RouteData = routeDataMock.Object,
|
||||||
|
ActionDescriptor = actionDescriptorMock.Object,
|
||||||
|
}, new List<IFilterMetadata>(), new Dictionary<string, object>(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
341
UnitTests/AspNetCore/Attributes/IPWhitelistAttributeTests.cs
Normal file
341
UnitTests/AspNetCore/Attributes/IPWhitelistAttributeTests.cs
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace UnitTests.AspNetCore.Attributes
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class IPWhitelistAttributeTests
|
||||||
|
{
|
||||||
|
private Dictionary<string, string> requestHeaders;
|
||||||
|
private Dictionary<object, object> itemsCallback;
|
||||||
|
private string configKey;
|
||||||
|
private bool configExists;
|
||||||
|
private List<string> allowedIpsConfig;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void InitializeTest()
|
||||||
|
{
|
||||||
|
requestHeaders = new Dictionary<string, string>();
|
||||||
|
itemsCallback = new Dictionary<object, object>();
|
||||||
|
configKey = null;
|
||||||
|
configExists = false;
|
||||||
|
allowedIpsConfig = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldDenyOnNoConfiguration()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var remote = IPAddress.Parse("192.168.178.1");
|
||||||
|
var attribute = new IPWhitelistAttribute();
|
||||||
|
var context = GetContext(remote);
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldDenyOnWrongConfiguration()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var remote = IPAddress.Parse("192.168.178.1");
|
||||||
|
var attribute = new IPWhitelistAttribute
|
||||||
|
{
|
||||||
|
AllowedIpAddresses = "192.168.178:1"
|
||||||
|
};
|
||||||
|
var context = GetContext(remote);
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldAllowLocalAccess()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new IPWhitelistAttribute();
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldDenyLocalAccess()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var attribute = new IPWhitelistAttribute
|
||||||
|
{
|
||||||
|
AllowLocalAccess = false
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("192.168.178.10")]
|
||||||
|
[DataRow("192.168.178.20")]
|
||||||
|
public void ShouldAllowSpecificAddress(string address)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var remote = IPAddress.Parse(address);
|
||||||
|
var attribute = new IPWhitelistAttribute
|
||||||
|
{
|
||||||
|
AllowLocalAccess = false,
|
||||||
|
AllowedIpAddresses = ",127.0.0.0/8,192.168.178.10"
|
||||||
|
};
|
||||||
|
var context = GetContext(remote);
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
if (address == "192.168.178.10")
|
||||||
|
{
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldAllowLocalAccessConfig()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
configKey = "White:List";
|
||||||
|
configExists = true;
|
||||||
|
allowedIpsConfig.Add("127.0.0.0/8");
|
||||||
|
allowedIpsConfig.Add("192.168.178.10");
|
||||||
|
var attribute = new IPWhitelistAttribute
|
||||||
|
{
|
||||||
|
AllowLocalAccess = true,
|
||||||
|
ConfigurationKey = configKey
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldDenyLocalAccessConfig()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
configKey = "White:List";
|
||||||
|
configExists = true;
|
||||||
|
allowedIpsConfig.Add("");
|
||||||
|
allowedIpsConfig.Add("192.168.178.10");
|
||||||
|
var attribute = new IPWhitelistAttribute
|
||||||
|
{
|
||||||
|
AllowLocalAccess = false,
|
||||||
|
ConfigurationKey = configKey
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("192.168.178.10")]
|
||||||
|
[DataRow("192.168.178.20")]
|
||||||
|
public void ShouldAllowSpecificAddressConfig(string address)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
configKey = "White:List";
|
||||||
|
configExists = true;
|
||||||
|
allowedIpsConfig.Add("192.168.178.10");
|
||||||
|
var attribute = new IPWhitelistAttribute
|
||||||
|
{
|
||||||
|
AllowLocalAccess = false,
|
||||||
|
ConfigurationKey = configKey
|
||||||
|
};
|
||||||
|
var remote = IPAddress.Parse(address);
|
||||||
|
var context = GetContext(remote);
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
if (address == "192.168.178.10")
|
||||||
|
{
|
||||||
|
Assert.IsNull(context.Result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldDenyOnMissingConfiguration()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
configKey = "White:List";
|
||||||
|
configExists = false;
|
||||||
|
var attribute = new IPWhitelistAttribute
|
||||||
|
{
|
||||||
|
AllowLocalAccess = false,
|
||||||
|
ConfigurationKey = configKey
|
||||||
|
};
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
attribute.OnActionExecuting(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(context.Result);
|
||||||
|
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||||
|
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, itemsCallback.Count);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionExecutingContext GetContext(IPAddress remote = null)
|
||||||
|
{
|
||||||
|
var requestHeaderMock = new Mock<IHeaderDictionary>();
|
||||||
|
foreach (var header in requestHeaders)
|
||||||
|
{
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h.ContainsKey(header.Key))
|
||||||
|
.Returns(true);
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h[header.Key])
|
||||||
|
.Returns(header.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestMock = new Mock<HttpRequest>();
|
||||||
|
requestMock
|
||||||
|
.Setup(r => r.Headers)
|
||||||
|
.Returns(requestHeaderMock.Object);
|
||||||
|
|
||||||
|
var connectionInfoMock = new Mock<ConnectionInfo>();
|
||||||
|
connectionInfoMock
|
||||||
|
.Setup(ci => ci.LocalIpAddress)
|
||||||
|
.Returns(IPAddress.Loopback);
|
||||||
|
connectionInfoMock
|
||||||
|
.Setup(ci => ci.RemoteIpAddress)
|
||||||
|
.Returns(remote ?? IPAddress.Loopback);
|
||||||
|
|
||||||
|
var itemsMock = new Mock<IDictionary<object, object>>();
|
||||||
|
itemsMock
|
||||||
|
.SetupSet(i => i[It.IsAny<object>()] = It.IsAny<object>())
|
||||||
|
.Callback<object, object>((key, val) => itemsCallback.Add(key, val));
|
||||||
|
|
||||||
|
var configurationMock = new Mock<IConfiguration>();
|
||||||
|
var children = new List<IConfigurationSection>();
|
||||||
|
foreach (string ipAddress in allowedIpsConfig)
|
||||||
|
{
|
||||||
|
var csm = new Mock<IConfigurationSection>();
|
||||||
|
csm.Setup(cs => cs.Value).Returns(ipAddress);
|
||||||
|
|
||||||
|
children.Add(csm.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
var configSectionMock = new Mock<IConfigurationSection>();
|
||||||
|
configSectionMock
|
||||||
|
.Setup(cs => cs.GetChildren())
|
||||||
|
.Returns(children);
|
||||||
|
|
||||||
|
configurationMock
|
||||||
|
.Setup(c => c.GetSection(configKey))
|
||||||
|
.Returns(configExists ? configSectionMock.Object : null);
|
||||||
|
|
||||||
|
var requestServicesMock = new Mock<IServiceProvider>();
|
||||||
|
requestServicesMock
|
||||||
|
.Setup(s => s.GetService(typeof(IConfiguration)))
|
||||||
|
.Returns(configurationMock.Object);
|
||||||
|
|
||||||
|
var contextMock = new Mock<HttpContext>();
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Request)
|
||||||
|
.Returns(requestMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.RequestServices)
|
||||||
|
.Returns(requestServicesMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Items)
|
||||||
|
.Returns(itemsMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Connection)
|
||||||
|
.Returns(connectionInfoMock.Object);
|
||||||
|
|
||||||
|
var routeDataMock = new Mock<RouteData>();
|
||||||
|
var actionDescriptorMock = new Mock<ActionDescriptor>();
|
||||||
|
|
||||||
|
return new ActionExecutingContext(new ActionContext
|
||||||
|
{
|
||||||
|
HttpContext = contextMock.Object,
|
||||||
|
RouteData = routeDataMock.Object,
|
||||||
|
ActionDescriptor = actionDescriptorMock.Object,
|
||||||
|
}, new List<IFilterMetadata>(), new Dictionary<string, object>(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AMWD.Common.AspNetCore.BasicAuthentication;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace UnitTests.AspNetCore.BasicAuthentication
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class BasicAuthenticationMiddlewareTests
|
||||||
|
{
|
||||||
|
private Dictionary<string, string> requestHeaders;
|
||||||
|
|
||||||
|
private Dictionary<string, string> responseHeadersCallback;
|
||||||
|
private int responseStatusCodeCallback;
|
||||||
|
|
||||||
|
private string validatorRealm;
|
||||||
|
private ClaimsPrincipal validatorResponse;
|
||||||
|
private List<(string username, string password, IPAddress ipAddr)> validatorCallback;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void InitializeTests()
|
||||||
|
{
|
||||||
|
requestHeaders = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
responseHeadersCallback = new Dictionary<string, string>();
|
||||||
|
responseStatusCodeCallback = 0;
|
||||||
|
|
||||||
|
validatorRealm = null;
|
||||||
|
validatorResponse = null;
|
||||||
|
validatorCallback = new List<(string username, string password, IPAddress ipAddr)>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldAllowAccess()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string username = "user";
|
||||||
|
string password = "pass:word";
|
||||||
|
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"))}");
|
||||||
|
validatorResponse = new ClaimsPrincipal();
|
||||||
|
|
||||||
|
var middleware = GetMiddleware();
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
await middleware.InvokeAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(0, responseStatusCodeCallback); // not triggered
|
||||||
|
Assert.AreEqual(0, responseHeadersCallback.Count);
|
||||||
|
Assert.AreEqual(1, validatorCallback.Count);
|
||||||
|
|
||||||
|
Assert.AreEqual(username, validatorCallback.First().username);
|
||||||
|
Assert.AreEqual(password, validatorCallback.First().password);
|
||||||
|
Assert.AreEqual(IPAddress.Loopback, validatorCallback.First().ipAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldDenyMissingHeader()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var middleware = GetMiddleware();
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
await middleware.InvokeAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(401, responseStatusCodeCallback);
|
||||||
|
|
||||||
|
Assert.AreEqual(0, validatorCallback.Count);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, responseHeadersCallback.Count);
|
||||||
|
Assert.AreEqual("WWW-Authenticate", responseHeadersCallback.Keys.First());
|
||||||
|
Assert.AreEqual("Basic", responseHeadersCallback.Values.First());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldDenyNoResult()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string username = "user";
|
||||||
|
string password = "pw";
|
||||||
|
|
||||||
|
validatorRealm = "TEST";
|
||||||
|
var remote = IPAddress.Parse("1.2.3.4");
|
||||||
|
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"))}");
|
||||||
|
|
||||||
|
var middleware = GetMiddleware();
|
||||||
|
var context = GetContext(remote);
|
||||||
|
|
||||||
|
// act
|
||||||
|
await middleware.InvokeAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(401, responseStatusCodeCallback);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, responseHeadersCallback.Count);
|
||||||
|
Assert.AreEqual("WWW-Authenticate", responseHeadersCallback.Keys.First());
|
||||||
|
Assert.AreEqual($"Basic realm=\"{validatorRealm}\"", responseHeadersCallback.Values.First());
|
||||||
|
|
||||||
|
Assert.AreEqual(1, validatorCallback.Count);
|
||||||
|
Assert.AreEqual(username, validatorCallback.First().username);
|
||||||
|
Assert.AreEqual(password, validatorCallback.First().password);
|
||||||
|
Assert.AreEqual(remote, validatorCallback.First().ipAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task ShouldBreakOnException()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string username = "user";
|
||||||
|
|
||||||
|
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}"))}");
|
||||||
|
|
||||||
|
var middleware = GetMiddleware();
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
await middleware.InvokeAsync(context);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(500, responseStatusCodeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BasicAuthenticationMiddleware GetMiddleware()
|
||||||
|
{
|
||||||
|
var nextMock = new Mock<RequestDelegate>();
|
||||||
|
var validatorMock = new Mock<IBasicAuthenticationValidator>();
|
||||||
|
validatorMock
|
||||||
|
.Setup(v => v.Realm)
|
||||||
|
.Returns(validatorRealm);
|
||||||
|
validatorMock
|
||||||
|
.Setup(v => v.ValidateAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IPAddress>()))
|
||||||
|
.Callback<string, string, IPAddress>((username, password, ipAddress) => validatorCallback.Add((username, password, ipAddress)))
|
||||||
|
.ReturnsAsync(validatorResponse);
|
||||||
|
|
||||||
|
return new BasicAuthenticationMiddleware(nextMock.Object, validatorMock.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpContext GetContext(IPAddress remote = null)
|
||||||
|
{
|
||||||
|
// Request
|
||||||
|
var requestHeaderMock = new Mock<IHeaderDictionary>();
|
||||||
|
foreach (var header in requestHeaders)
|
||||||
|
{
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h.ContainsKey(header.Key))
|
||||||
|
.Returns(true);
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h[header.Key])
|
||||||
|
.Returns(header.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestMock = new Mock<HttpRequest>();
|
||||||
|
requestMock
|
||||||
|
.Setup(r => r.Headers)
|
||||||
|
.Returns(requestHeaderMock.Object);
|
||||||
|
|
||||||
|
// Response
|
||||||
|
var responseHeaderMock = new Mock<IHeaderDictionary>();
|
||||||
|
responseHeaderMock
|
||||||
|
.SetupSet(h => h[It.IsAny<string>()] = It.IsAny<StringValues>())
|
||||||
|
.Callback<string, StringValues>((key, value) => responseHeadersCallback[key] = value);
|
||||||
|
|
||||||
|
var responseMock = new Mock<HttpResponse>();
|
||||||
|
responseMock
|
||||||
|
.Setup(r => r.Headers)
|
||||||
|
.Returns(responseHeaderMock.Object);
|
||||||
|
responseMock
|
||||||
|
.SetupSet(r => r.StatusCode = It.IsAny<int>())
|
||||||
|
.Callback<int>((code) => responseStatusCodeCallback = code);
|
||||||
|
|
||||||
|
// Connection
|
||||||
|
var connectionInfoMock = new Mock<ConnectionInfo>();
|
||||||
|
connectionInfoMock
|
||||||
|
.Setup(ci => ci.LocalIpAddress)
|
||||||
|
.Returns(IPAddress.Loopback);
|
||||||
|
connectionInfoMock
|
||||||
|
.Setup(ci => ci.RemoteIpAddress)
|
||||||
|
.Returns(remote ?? IPAddress.Loopback);
|
||||||
|
|
||||||
|
// Request Services
|
||||||
|
var requestServicesMock = new Mock<IServiceProvider>();
|
||||||
|
|
||||||
|
var contextMock = new Mock<HttpContext>();
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Request)
|
||||||
|
.Returns(requestMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Response)
|
||||||
|
.Returns(responseMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Connection)
|
||||||
|
.Returns(connectionInfoMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.RequestServices)
|
||||||
|
.Returns(requestServicesMock.Object);
|
||||||
|
|
||||||
|
return contextMock.Object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
430
UnitTests/AspNetCore/Extensions/HttpContextExtensionsTests.cs
Normal file
430
UnitTests/AspNetCore/Extensions/HttpContextExtensionsTests.cs
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Antiforgery;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace UnitTests.AspNetCore.Extensions
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class HttpContextExtensionsTests
|
||||||
|
{
|
||||||
|
private Mock<ISession> sessionMock;
|
||||||
|
|
||||||
|
private string tokenName;
|
||||||
|
private string tokenValue;
|
||||||
|
|
||||||
|
private Dictionary<string, string> requestHeaders;
|
||||||
|
private Dictionary<string, string> requestQueries;
|
||||||
|
private Dictionary<object, object> items;
|
||||||
|
|
||||||
|
private IPAddress remote;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void InitializeTests()
|
||||||
|
{
|
||||||
|
tokenName = null;
|
||||||
|
tokenValue = null;
|
||||||
|
|
||||||
|
requestHeaders = new Dictionary<string, string>();
|
||||||
|
requestQueries = new Dictionary<string, string>();
|
||||||
|
items = new Dictionary<object, object>();
|
||||||
|
|
||||||
|
remote = IPAddress.Loopback;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Antiforgery
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnAntiforgery()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
tokenName = "af-token";
|
||||||
|
tokenValue = "security_first";
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = context.GetAntiforgeryToken();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(tokenName, result.Name);
|
||||||
|
Assert.AreEqual(tokenValue, result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnAntiforgeryNullService()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
tokenName = "af-token";
|
||||||
|
tokenValue = "security_first";
|
||||||
|
|
||||||
|
var context = GetContext(hasAntiforgery: false);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = context.GetAntiforgeryToken();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(null, result.Name);
|
||||||
|
Assert.AreEqual(null, result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnAntiforgeryNullToken()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = context.GetAntiforgeryToken();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(null, result.Name);
|
||||||
|
Assert.AreEqual(null, result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Antiforgery
|
||||||
|
|
||||||
|
#region RemoteAddres
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnRemoteAddress()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
remote = IPAddress.Parse("1.2.3.4");
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = context.GetRemoteIpAddress();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(remote, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnDefaultHeader()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
remote = IPAddress.Parse("1.2.3.4");
|
||||||
|
var header = IPAddress.Parse("5.6.7.8");
|
||||||
|
requestHeaders.Add("X-Forwarded-For", header.ToString());
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = context.GetRemoteIpAddress();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreNotEqual(remote, result);
|
||||||
|
Assert.AreEqual(header, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnCustomHeader()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
remote = IPAddress.Parse("1.2.3.4");
|
||||||
|
string headerName = "FooBar";
|
||||||
|
var headerIp = IPAddress.Parse("5.6.7.8");
|
||||||
|
requestHeaders.Add(headerName, headerIp.ToString());
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = context.GetRemoteIpAddress(headerName: headerName);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreNotEqual(remote, result);
|
||||||
|
Assert.AreEqual(headerIp, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnAddressInvalidHeader()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
remote = IPAddress.Parse("1.2.3.4");
|
||||||
|
requestHeaders.Add("X-Forwarded-For", "1.2.3:4");
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = context.GetRemoteIpAddress();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(remote, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion RemoteAddres
|
||||||
|
|
||||||
|
#region Local Request
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnTrueOnLocal()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
remote = IPAddress.Loopback;
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool result = context.IsLocalRequest();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnFalseOnRemote()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
remote = IPAddress.Parse("1.2.3.4");
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool result = context.IsLocalRequest();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnTrueOnDefaultHeader()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
remote = IPAddress.Parse("1.2.3.4");
|
||||||
|
var headerIp = IPAddress.Loopback;
|
||||||
|
requestHeaders.Add("X-Forwarded-For", headerIp.ToString());
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool result = context.IsLocalRequest();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnTrueOnCustomHeader()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
remote = IPAddress.Parse("1.2.3.4");
|
||||||
|
string headerName = "FooBar";
|
||||||
|
var headerIp = IPAddress.Loopback;
|
||||||
|
requestHeaders.Add(headerName, headerIp.ToString());
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool result = context.IsLocalRequest(headerName: headerName);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnFalseOnDefaultHeader()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var headerIp = IPAddress.Parse("1.2.3.4");
|
||||||
|
requestHeaders.Add("X-Forwarded-For", headerIp.ToString());
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool result = context.IsLocalRequest();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnFalseOnCustomHeader()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string headerName = "FooBar";
|
||||||
|
var headerIp = IPAddress.Parse("1.2.3.4");
|
||||||
|
requestHeaders.Add(headerName, headerIp.ToString());
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool result = context.IsLocalRequest(headerName: headerName);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Local Request
|
||||||
|
|
||||||
|
#region ReturnUrl
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnNull()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
string result = context.GetReturnUrl();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnOriginalRequest()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string request = "abc";
|
||||||
|
string query = "def";
|
||||||
|
|
||||||
|
items.Add("OriginalRequest", request);
|
||||||
|
requestQueries.Add("ReturnUrl", query);
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
string result = context.GetReturnUrl();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(request, result);
|
||||||
|
Assert.AreNotEqual(query, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnUrl()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string query = "def";
|
||||||
|
requestQueries.Add("ReturnUrl", query);
|
||||||
|
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
string result = context.GetReturnUrl();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(query, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion ReturnUrl
|
||||||
|
|
||||||
|
#region Session
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldClearSession()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var context = GetContext();
|
||||||
|
|
||||||
|
// act
|
||||||
|
context.ClearSession();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
sessionMock.Verify(s => s.Clear(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldSkipWhenNoSession()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var context = GetContext(hasSession: false);
|
||||||
|
|
||||||
|
// act
|
||||||
|
context.ClearSession();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
sessionMock.Verify(s => s.Clear(), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Session
|
||||||
|
|
||||||
|
private HttpContext GetContext(bool hasAntiforgery = true, bool hasSession = true)
|
||||||
|
{
|
||||||
|
// Request
|
||||||
|
var requestHeaderMock = new Mock<IHeaderDictionary>();
|
||||||
|
foreach (var header in requestHeaders)
|
||||||
|
{
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h.ContainsKey(header.Key))
|
||||||
|
.Returns(true);
|
||||||
|
requestHeaderMock
|
||||||
|
.Setup(h => h[header.Key])
|
||||||
|
.Returns(header.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestQueryMock = new Mock<IQueryCollection>();
|
||||||
|
foreach (var query in requestQueries)
|
||||||
|
{
|
||||||
|
requestQueryMock
|
||||||
|
.Setup(h => h.ContainsKey(query.Key))
|
||||||
|
.Returns(true);
|
||||||
|
requestQueryMock
|
||||||
|
.Setup(h => h[query.Key])
|
||||||
|
.Returns(query.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestMock = new Mock<HttpRequest>();
|
||||||
|
requestMock
|
||||||
|
.Setup(r => r.Headers)
|
||||||
|
.Returns(requestHeaderMock.Object);
|
||||||
|
requestMock
|
||||||
|
.Setup(r => r.Query)
|
||||||
|
.Returns(requestQueryMock.Object);
|
||||||
|
|
||||||
|
// Request Services
|
||||||
|
var requestServicesMock = new Mock<IServiceProvider>();
|
||||||
|
if (hasAntiforgery)
|
||||||
|
{
|
||||||
|
var antiforgeryMock = new Mock<IAntiforgery>();
|
||||||
|
antiforgeryMock
|
||||||
|
.Setup(af => af.GetAndStoreTokens(It.IsAny<HttpContext>()))
|
||||||
|
.Returns(string.IsNullOrWhiteSpace(tokenName) ? null : new AntiforgeryTokenSet(tokenValue, tokenValue, tokenName, tokenName));
|
||||||
|
|
||||||
|
requestServicesMock
|
||||||
|
.Setup(rs => rs.GetService(typeof(IAntiforgery)))
|
||||||
|
.Returns(antiforgeryMock.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection
|
||||||
|
var connectionInfoMock = new Mock<ConnectionInfo>();
|
||||||
|
connectionInfoMock
|
||||||
|
.Setup(ci => ci.LocalIpAddress)
|
||||||
|
.Returns(IPAddress.Loopback);
|
||||||
|
connectionInfoMock
|
||||||
|
.Setup(ci => ci.RemoteIpAddress)
|
||||||
|
.Returns(remote);
|
||||||
|
|
||||||
|
// Session
|
||||||
|
sessionMock = new Mock<ISession>();
|
||||||
|
|
||||||
|
var contextMock = new Mock<HttpContext>();
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Request)
|
||||||
|
.Returns(requestMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.RequestServices)
|
||||||
|
.Returns(requestServicesMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Connection)
|
||||||
|
.Returns(connectionInfoMock.Object);
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Items)
|
||||||
|
.Returns(items);
|
||||||
|
if (hasSession)
|
||||||
|
{
|
||||||
|
contextMock
|
||||||
|
.Setup(c => c.Session)
|
||||||
|
.Returns(sessionMock.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
return contextMock.Object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace UnitTests.AspNetCore.Extensions
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class ModelStateDictionaryExtensionsTests
|
||||||
|
{
|
||||||
|
private TestModel testModel;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void InitializeTests()
|
||||||
|
{
|
||||||
|
testModel = new TestModel
|
||||||
|
{
|
||||||
|
ValueA = "A",
|
||||||
|
ValueB = "B",
|
||||||
|
SubModel = new TestSubModel
|
||||||
|
{
|
||||||
|
SubValueA = "a",
|
||||||
|
SubValueB = "b"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldAddNormalModelError()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var modelState = new ModelStateDictionary();
|
||||||
|
|
||||||
|
// act
|
||||||
|
modelState.AddModelError(testModel, m => m.ValueA, "ShitHappens");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(1, modelState.Count);
|
||||||
|
Assert.AreEqual("ValueA", modelState.Keys.First());
|
||||||
|
Assert.AreEqual("ShitHappens", modelState.Values.First().Errors.First().ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldAddExtendedModelError()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var modelState = new ModelStateDictionary();
|
||||||
|
|
||||||
|
// act
|
||||||
|
modelState.AddModelError(testModel, m => m.SubModel.SubValueB, "ShitHappens");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(1, modelState.Count);
|
||||||
|
Assert.AreEqual("SubModel.SubValueB", modelState.Keys.First());
|
||||||
|
Assert.AreEqual("ShitHappens", modelState.Values.First().Errors.First().ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(ArgumentNullException))]
|
||||||
|
public void ShouldThrowArgumentNull()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
ModelStateDictionary modelState = null;
|
||||||
|
|
||||||
|
// act
|
||||||
|
modelState.AddModelError(testModel, m => m.SubModel.SubValueB, "ShitHappens");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(InvalidOperationException))]
|
||||||
|
public void ShouldThrowInvalidOperation()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var modelState = new ModelStateDictionary();
|
||||||
|
|
||||||
|
// act
|
||||||
|
modelState.AddModelError(testModel, m => m, "ShitHappens");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class TestModel
|
||||||
|
{
|
||||||
|
public string ValueA { get; set; }
|
||||||
|
|
||||||
|
public string ValueB { get; set; }
|
||||||
|
|
||||||
|
public TestSubModel SubModel { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class TestSubModel
|
||||||
|
{
|
||||||
|
public string SubValueA { get; set; }
|
||||||
|
|
||||||
|
public string SubValueB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
159
UnitTests/AspNetCore/Extensions/SessionExtensionsTests.cs
Normal file
159
UnitTests/AspNetCore/Extensions/SessionExtensionsTests.cs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace UnitTests.AspNetCore.Extensions
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class SessionExtensionsTests
|
||||||
|
{
|
||||||
|
private Mock<ISession> sessionMock;
|
||||||
|
|
||||||
|
private string sessionKey;
|
||||||
|
private byte[] sessionValue;
|
||||||
|
|
||||||
|
private TestModel model;
|
||||||
|
|
||||||
|
internal class TestModel
|
||||||
|
{
|
||||||
|
public string ValueA { get; set; }
|
||||||
|
|
||||||
|
public string ValueB { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void InitializeTests()
|
||||||
|
{
|
||||||
|
sessionKey = null;
|
||||||
|
sessionValue = null;
|
||||||
|
|
||||||
|
model = new TestModel
|
||||||
|
{
|
||||||
|
ValueA = "A",
|
||||||
|
ValueB = "B"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldCheckKeyExists()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
sessionKey = "exists";
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool result1 = session.HasKey("exists");
|
||||||
|
bool result2 = session.HasKey("somewhere");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(result1);
|
||||||
|
Assert.IsFalse(result2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldGetValue()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
sessionKey = "test";
|
||||||
|
sessionValue = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(model));
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = session.GetValue<TestModel>(sessionKey);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
Assert.AreEqual(model.ValueA, result.ValueA);
|
||||||
|
Assert.AreEqual(model.ValueB, result.ValueB);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldGetNull()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
sessionKey = "foo";
|
||||||
|
sessionValue = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(model));
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = session.GetValue<TestModel>("bar");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldGetValueWithFallback()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
sessionKey = "test";
|
||||||
|
sessionValue = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(model));
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = session.GetValue(sessionKey, new TestModel());
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
Assert.AreEqual(model.ValueA, result.ValueA);
|
||||||
|
Assert.AreEqual(model.ValueB, result.ValueB);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldGetFallback()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
sessionKey = "foo";
|
||||||
|
sessionValue = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(model));
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result = session.GetValue("bar", new TestModel());
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
Assert.AreEqual(null, result.ValueA);
|
||||||
|
Assert.AreEqual(null, result.ValueB);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldSetValue()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string key = "foobar";
|
||||||
|
var session = GetSession();
|
||||||
|
|
||||||
|
// act
|
||||||
|
session.SetValue(key, model);
|
||||||
|
|
||||||
|
// arrange
|
||||||
|
Assert.AreEqual(key, sessionKey);
|
||||||
|
Assert.AreEqual(JsonConvert.SerializeObject(model), Encoding.UTF8.GetString(sessionValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISession GetSession()
|
||||||
|
{
|
||||||
|
string[] keys = new[] { sessionKey };
|
||||||
|
|
||||||
|
sessionMock = new Mock<ISession>();
|
||||||
|
sessionMock
|
||||||
|
.Setup(s => s.TryGetValue(It.IsAny<string>(), out sessionValue))
|
||||||
|
.Returns<string, byte[]>((key, value) => sessionKey == key);
|
||||||
|
sessionMock
|
||||||
|
.Setup(s => s.Set(It.IsAny<string>(), It.IsAny<byte[]>()))
|
||||||
|
.Callback<string, byte[]>((key, value) =>
|
||||||
|
{
|
||||||
|
sessionKey = key;
|
||||||
|
sessionValue = value;
|
||||||
|
});
|
||||||
|
sessionMock
|
||||||
|
.Setup(s => s.Keys)
|
||||||
|
.Returns(keys);
|
||||||
|
|
||||||
|
return sessionMock.Object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
155
UnitTests/AspNetCore/Utilities/HtmlHelperTests.cs
Normal file
155
UnitTests/AspNetCore/Utilities/HtmlHelperTests.cs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
using System;
|
||||||
|
using AMWD.Common.AspNetCore.Utilities;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace UnitTests.AspNetCore.Utilities
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class HtmlHelperTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(ArgumentNullException))]
|
||||||
|
public void ShouldThrowErrorOnEmptyColor()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
HtmlHelper.IsDarkColor("");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
// exception thrown
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(NotSupportedException))]
|
||||||
|
public void ShouldThrowErrorOnUnsupportedColor()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
HtmlHelper.IsDarkColor("hsv(1, 2, 3)");
|
||||||
|
|
||||||
|
// assert
|
||||||
|
// exception thrown
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("rgb(255, 255, 255)")]
|
||||||
|
[DataRow("rgba(255, 255, 255, .5)")]
|
||||||
|
public void ShouldReturnLightColorForWhiteRgb(string white)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isDark = HtmlHelper.IsDarkColor(white);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(isDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("#ffFFff")]
|
||||||
|
[DataRow("FFffFF")]
|
||||||
|
public void ShouldReturnLightColorForWhiteFullHex(string white)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isDark = HtmlHelper.IsDarkColor(white);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(isDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("#fFf")]
|
||||||
|
[DataRow("FfF")]
|
||||||
|
public void ShouldReturnLightColorForWhiteShortHex(string white)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isDark = HtmlHelper.IsDarkColor(white);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(isDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("rgb(0, 0, 0)")]
|
||||||
|
[DataRow("rgba(0, 0, 0, .5)")]
|
||||||
|
public void ShouldReturnDarkColorForBlackRgb(string black)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isDark = HtmlHelper.IsDarkColor(black);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(isDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("#000000")]
|
||||||
|
[DataRow("000000")]
|
||||||
|
public void ShouldReturnDarkColorForBlackFullHex(string black)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isDark = HtmlHelper.IsDarkColor(black);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(isDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("#000")]
|
||||||
|
[DataRow("000")]
|
||||||
|
public void ShouldReturnDarkColorForBlackShortHex(string black)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isDark = HtmlHelper.IsDarkColor(black);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(isDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("rgb(255, 88, 0)")]
|
||||||
|
[DataRow("rgb(0, 218, 0)")]
|
||||||
|
[DataRow("rgb(0, 168, 255)")]
|
||||||
|
[DataRow("rgb(255, 38, 255)")]
|
||||||
|
[DataRow("rgb(128, 128, 128)")]
|
||||||
|
public void ShouldReturnLightColorForBorderColors(string color)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isDark = HtmlHelper.IsDarkColor(color);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(isDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataTestMethod]
|
||||||
|
[DataRow("rgb(253, 88, 0)")]
|
||||||
|
[DataRow("rgb(0, 217, 0)")]
|
||||||
|
[DataRow("rgb(0, 168, 253)")]
|
||||||
|
[DataRow("rgb(254, 38, 254)")]
|
||||||
|
[DataRow("rgb(127, 127, 127)")]
|
||||||
|
public void ShouldReturnDarkColorForBorderColors(string color)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isDark = HtmlHelper.IsDarkColor(color);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(isDark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
164
UnitTests/AspNetCore/Utilities/PasswordHelperTests.cs
Normal file
164
UnitTests/AspNetCore/Utilities/PasswordHelperTests.cs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace UnitTests.AspNetCore.Utilities
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
|
public class PasswordHelperTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnNullHashWhenNullProvided()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string password = null;
|
||||||
|
|
||||||
|
// act
|
||||||
|
string hash = PasswordHelper.HashPassword(password);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsNull(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnEmptyHashWhenSpacesProvided()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string password = " ";
|
||||||
|
|
||||||
|
// act
|
||||||
|
string hash = PasswordHelper.HashPassword(password);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual("", hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnHashWhenTextProvided()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string password = "password";
|
||||||
|
|
||||||
|
// act
|
||||||
|
string hash = PasswordHelper.HashPassword(password);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(!string.IsNullOrWhiteSpace(hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnFalseOnNullPassword()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string password = null;
|
||||||
|
string hash = PasswordHelper.HashPassword(password);
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isValid = PasswordHelper.VerifyPassword(password, hash, out bool rehashNeeded);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(isValid);
|
||||||
|
Assert.IsFalse(rehashNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnFalseOnEmptyPassword()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string password = " ";
|
||||||
|
string hash = PasswordHelper.HashPassword(password);
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isValid = PasswordHelper.VerifyPassword(password, hash, out bool rehashNeeded);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(isValid);
|
||||||
|
Assert.IsFalse(rehashNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnFalseOnNullHash()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string password = "password";
|
||||||
|
string hash = null;
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isValid = PasswordHelper.VerifyPassword(password, hash, out bool rehashNeeded);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(isValid);
|
||||||
|
Assert.IsFalse(rehashNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnFalseOnEmptyHash()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string password = "password";
|
||||||
|
string hash = "";
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isValid = PasswordHelper.VerifyPassword(password, hash, out bool rehashNeeded);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(isValid);
|
||||||
|
Assert.IsFalse(rehashNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnTrueOnSuccess()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string password = "password";
|
||||||
|
string hash = PasswordHelper.HashPassword(password);
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isValid = PasswordHelper.VerifyPassword(password, hash, out bool rehashNeeded);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(isValid);
|
||||||
|
Assert.IsFalse(rehashNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnFalseOnError()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
string password = "pass";
|
||||||
|
string hash = PasswordHelper.HashPassword(password + "word");
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isValid = PasswordHelper.VerifyPassword(password, hash, out bool rehashNeeded);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsFalse(isValid);
|
||||||
|
Assert.IsFalse(rehashNeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnTrueWithRehash()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var devHasher = new PasswordHasher<object>();
|
||||||
|
var field = devHasher.GetType().GetField("_compatibilityMode", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
field.SetValue(devHasher, PasswordHasherCompatibilityMode.IdentityV2);
|
||||||
|
|
||||||
|
string password = "password";
|
||||||
|
string hash = devHasher.HashPassword(null, password);
|
||||||
|
|
||||||
|
// act
|
||||||
|
bool isValid = PasswordHelper.VerifyPassword(password, hash, out bool rehashNeeded);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(isValid);
|
||||||
|
Assert.IsTrue(rehashNeeded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Extensions
|
namespace UnitTests.Common.Extensions
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using AMWD.Common.Tests.Utils;
|
using UnitTests.Common.Utils;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Extensions
|
namespace UnitTests.Common.Extensions
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -13,7 +13,7 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||||
using var mock = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
||||||
|
|
||||||
var utc = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Utc);
|
var utc = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Utc);
|
||||||
var local = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Local);
|
var local = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Local);
|
||||||
@@ -39,7 +39,7 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||||
using var mock = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
||||||
|
|
||||||
var utc = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Utc);
|
var utc = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Utc);
|
||||||
var local = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Local);
|
var local = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Local);
|
||||||
@@ -64,7 +64,7 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
public void ShouldReturnCorrectUtcAlignmentDaylightSavingEnd()
|
public void ShouldReturnCorrectUtcAlignmentDaylightSavingEnd()
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var utcNow = new DateTime(2021, 10, 30, 12, 15, 30, 45, DateTimeKind.Utc);
|
var dateTime = new DateTime(2021, 10, 30, 12, 15, 30, 45, DateTimeKind.Utc);
|
||||||
|
|
||||||
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
||||||
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
||||||
@@ -75,13 +75,13 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
var offsetFourHours = TimeSpan.FromHours(4);
|
var offsetFourHours = TimeSpan.FromHours(4);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(utcNow);
|
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
|
||||||
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(utcNow);
|
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
|
||||||
var diffTwoHours = intervalTwoHours.GetAlignedInterval(utcNow);
|
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
|
||||||
var diffDay = intervalDay.GetAlignedInterval(utcNow);
|
var diffDay = intervalDay.GetAlignedInterval(dateTime);
|
||||||
|
|
||||||
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(utcNow, offsetTwoMinutes);
|
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
|
||||||
var diffDayOffset = intervalDay.GetAlignedInterval(utcNow, offsetFourHours);
|
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
||||||
@@ -97,7 +97,7 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
public void ShouldReturnCorrectUtcAlignmentDaylightSavingStart()
|
public void ShouldReturnCorrectUtcAlignmentDaylightSavingStart()
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var utcNow = new DateTime(2022, 3, 26, 12, 15, 30, 45, DateTimeKind.Utc);
|
var dateTime = new DateTime(2022, 3, 26, 12, 15, 30, 45, DateTimeKind.Utc);
|
||||||
|
|
||||||
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
||||||
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
||||||
@@ -108,13 +108,13 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
var offsetFourHours = TimeSpan.FromHours(4);
|
var offsetFourHours = TimeSpan.FromHours(4);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(utcNow);
|
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
|
||||||
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(utcNow);
|
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
|
||||||
var diffTwoHours = intervalTwoHours.GetAlignedInterval(utcNow);
|
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
|
||||||
var diffDay = intervalDay.GetAlignedInterval(utcNow);
|
var diffDay = intervalDay.GetAlignedInterval(dateTime);
|
||||||
|
|
||||||
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(utcNow, offsetTwoMinutes);
|
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
|
||||||
var diffDayOffset = intervalDay.GetAlignedInterval(utcNow, offsetFourHours);
|
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
||||||
@@ -131,9 +131,9 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||||
using var mock = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
||||||
|
|
||||||
var utcNow = new DateTime(2021, 10, 30, 12, 15, 30, 45, DateTimeKind.Local);
|
var dateTime = new DateTime(2021, 10, 30, 12, 15, 30, 45, DateTimeKind.Local);
|
||||||
|
|
||||||
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
||||||
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
||||||
@@ -144,13 +144,13 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
var offsetFourHours = TimeSpan.FromHours(4);
|
var offsetFourHours = TimeSpan.FromHours(4);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(utcNow);
|
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
|
||||||
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(utcNow);
|
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
|
||||||
var diffTwoHours = intervalTwoHours.GetAlignedInterval(utcNow);
|
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
|
||||||
var diffDay = intervalDay.GetAlignedInterval(utcNow);
|
var diffDay = intervalDay.GetAlignedInterval(dateTime);
|
||||||
|
|
||||||
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(utcNow, offsetTwoMinutes);
|
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
|
||||||
var diffDayOffset = intervalDay.GetAlignedInterval(utcNow, offsetFourHours);
|
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
||||||
@@ -167,9 +167,9 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||||
using var mock = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
||||||
|
|
||||||
var utcNow = new DateTime(2022, 3, 26, 12, 15, 30, 45, DateTimeKind.Local);
|
var dateTime = new DateTime(2022, 3, 26, 12, 15, 30, 45, DateTimeKind.Local);
|
||||||
|
|
||||||
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
||||||
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
||||||
@@ -180,13 +180,13 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
var offsetFourHours = TimeSpan.FromHours(4);
|
var offsetFourHours = TimeSpan.FromHours(4);
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(utcNow);
|
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
|
||||||
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(utcNow);
|
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
|
||||||
var diffTwoHours = intervalTwoHours.GetAlignedInterval(utcNow);
|
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
|
||||||
var diffDay = intervalDay.GetAlignedInterval(utcNow);
|
var diffDay = intervalDay.GetAlignedInterval(dateTime);
|
||||||
|
|
||||||
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(utcNow, offsetTwoMinutes);
|
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
|
||||||
var diffDayOffset = intervalDay.GetAlignedInterval(utcNow, offsetFourHours);
|
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
||||||
@@ -198,6 +198,26 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
Assert.AreEqual(TimeSpan.Parse("16:44:29.955"), diffDayOffset); // has to be plus one hour due to daylight saving started
|
Assert.AreEqual(TimeSpan.Parse("16:44:29.955"), diffDayOffset); // has to be plus one hour due to daylight saving started
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ShouldReturnCorrectAlignment()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||||
|
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
||||||
|
|
||||||
|
var interval = TimeSpan.FromDays(1);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var intervalUtc = interval.GetAlignedIntervalUtc();
|
||||||
|
var intervalLocal = interval.GetAlignedIntervalLocal();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.AreEqual(DateTime.UtcNow.TimeOfDay.RoundToSecond(), (interval - intervalUtc).RoundToSecond());
|
||||||
|
Assert.AreEqual(DateTime.Now.TimeOfDay.RoundToSecond(), (interval - intervalLocal).RoundToSecond());
|
||||||
|
|
||||||
|
Assert.AreEqual((DateTime.Now - DateTime.UtcNow).RoundToSecond(), (intervalUtc - intervalLocal).RoundToSecond());
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void ShouldReturnCorrectShortStringForTimeSpan()
|
public void ShouldReturnCorrectShortStringForTimeSpan()
|
||||||
{
|
{
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AMWD.Common.Tests.Utils;
|
using UnitTests.Common.Utils;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using DescriptionAttribute = System.ComponentModel.DescriptionAttribute;
|
using DescriptionAttribute = System.ComponentModel.DescriptionAttribute;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Extensions
|
namespace UnitTests.Common.Extensions
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Extensions
|
namespace UnitTests.Common.Extensions
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using AMWD.Common.Tests.Utils;
|
using UnitTests.Common.Utils;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Extensions
|
namespace UnitTests.Common.Extensions
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -91,7 +91,7 @@ namespace AMWD.Common.Tests.Extensions
|
|||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
var testObject = new JsonTestClass();
|
var testObject = new JsonTestClass();
|
||||||
string expected = @"{""$type"":""AMWD.Common.Tests.Utils.JsonTestClass, AMWD.Common.Tests"",""stringValue"":""Hello World!"",""isBoolTrue"":true,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":42.21,""localTimestamp"":""2021-11-16T20:15:34+01:00"",""utcTimestamp"":""2021-11-16T20:15:34Z"",""object"":{""$type"":""AMWD.Common.Tests.Utils.JsonTestSubClass, AMWD.Common.Tests"",""integerValue"":42,""stringValue"":""Foo-Bar""}}";
|
string expected = @"{""$type"":""UnitTests.Common.Utils.JsonTestClass, UnitTests"",""stringValue"":""Hello World!"",""isBoolTrue"":true,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":42.21,""localTimestamp"":""2021-11-16T20:15:34+01:00"",""utcTimestamp"":""2021-11-16T20:15:34Z"",""object"":{""$type"":""UnitTests.Common.Utils.JsonTestSubClass, UnitTests"",""integerValue"":42,""stringValue"":""Foo-Bar""}}";
|
||||||
|
|
||||||
// act
|
// act
|
||||||
string json = testObject.SerializeJson(includeType: true);
|
string json = testObject.SerializeJson(includeType: true);
|
||||||
@@ -3,7 +3,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Extensions
|
namespace UnitTests.Common.Extensions
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -4,7 +4,7 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Extensions
|
namespace UnitTests.Common.Extensions
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -4,7 +4,7 @@ using System.Net;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Extensions
|
namespace UnitTests.Common.Extensions
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using AMWD.Common.Tests.Utils;
|
using UnitTests.Common.Utils;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Utilities
|
namespace UnitTests.Common.Utilities
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -39,7 +38,7 @@ namespace AMWD.Common.Tests.Utilities
|
|||||||
public void ShouldEncryptAesWithoutSalt() // required to test the encryption itself
|
public void ShouldEncryptAesWithoutSalt() // required to test the encryption itself
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
using var disposable = CryptographyHelperSaltMock.Create(0);
|
using var _ = CryptographyHelperSaltMock.Create(0);
|
||||||
|
|
||||||
byte[] bytes = new byte[] { 0xaf, 0xfe };
|
byte[] bytes = new byte[] { 0xaf, 0xfe };
|
||||||
string str = "ABC";
|
string str = "ABC";
|
||||||
@@ -66,7 +65,7 @@ namespace AMWD.Common.Tests.Utilities
|
|||||||
public void ShouldDecryptAesWithoutSalt() // required to test the decryption itself
|
public void ShouldDecryptAesWithoutSalt() // required to test the decryption itself
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
using var disposable = CryptographyHelperSaltMock.Create(0);
|
using var _ = CryptographyHelperSaltMock.Create(0);
|
||||||
|
|
||||||
string cipherStr = "ueLuhFNpCuYmx8v3hczHtg==";
|
string cipherStr = "ueLuhFNpCuYmx8v3hczHtg==";
|
||||||
byte[] cipherBytes = new byte[] { 0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc };
|
byte[] cipherBytes = new byte[] { 0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc };
|
||||||
@@ -153,7 +152,7 @@ namespace AMWD.Common.Tests.Utilities
|
|||||||
public void ShouldEncryptTdesWithoutSalt() // required to test the encryption itself
|
public void ShouldEncryptTdesWithoutSalt() // required to test the encryption itself
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
using var disposable = CryptographyHelperSaltMock.Create(0);
|
using var _ = CryptographyHelperSaltMock.Create(0);
|
||||||
|
|
||||||
byte[] bytes = new byte[] { 0xaf, 0xfe };
|
byte[] bytes = new byte[] { 0xaf, 0xfe };
|
||||||
string str = "ABC";
|
string str = "ABC";
|
||||||
@@ -180,7 +179,7 @@ namespace AMWD.Common.Tests.Utilities
|
|||||||
public void ShouldDecryptTdesWithoutSalt() // required to test the decryption itself
|
public void ShouldDecryptTdesWithoutSalt() // required to test the decryption itself
|
||||||
{
|
{
|
||||||
// arrange
|
// arrange
|
||||||
using var disposable = CryptographyHelperSaltMock.Create(0);
|
using var _ = CryptographyHelperSaltMock.Create(0);
|
||||||
|
|
||||||
string cipherStr = "1l74soBuuEI=";
|
string cipherStr = "1l74soBuuEI=";
|
||||||
byte[] cipherBytes = new byte[] { 0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7 };
|
byte[] cipherBytes = new byte[] { 0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7 };
|
||||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using AMWD.Common.Utilities;
|
using AMWD.Common.Utilities;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Utilities
|
namespace UnitTests.Common.Utilities
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using AMWD.Common.Utilities;
|
using AMWD.Common.Utilities;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Utilities
|
namespace UnitTests.Common.Utilities
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using ReflectionMagic;
|
using ReflectionMagic;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Utils
|
namespace UnitTests.Common.Utils
|
||||||
{
|
{
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
internal class CryptographyHelperSaltMock : IDisposable
|
internal class CryptographyHelperSaltMock : IDisposable
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Utils
|
namespace UnitTests.Common.Utils
|
||||||
{
|
{
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
|
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Utils
|
namespace UnitTests.Common.Utils
|
||||||
{
|
{
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
internal class JsonTestClass
|
internal class JsonTestClass
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using ReflectionMagic;
|
using ReflectionMagic;
|
||||||
|
|
||||||
namespace AMWD.Common.Tests.Utils
|
namespace UnitTests.Common.Utils
|
||||||
{
|
{
|
||||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||||
internal class TimeZoneInfoLocalMock : IDisposable
|
internal class TimeZoneInfoLocalMock : IDisposable
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
@@ -12,12 +12,15 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||||
|
<PackageReference Include="Moq" Version="4.18.1" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||||
<PackageReference Include="ReflectionMagic" Version="4.1.0" />
|
<PackageReference Include="ReflectionMagic" Version="4.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\AMWD.Common.AspNetCore\AMWD.Common.AspNetCore.csproj" />
|
||||||
|
<!--ProjectReference Include="..\AMWD.Common.EntityFrameworkCore\AMWD.Common.EntityFrameworkCore.csproj" /-->
|
||||||
<ProjectReference Include="..\AMWD.Common\AMWD.Common.csproj" />
|
<ProjectReference Include="..\AMWD.Common\AMWD.Common.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
Reference in New Issue
Block a user