1
0

Moved all UnitTests to a single project. Implemented parts of AspNetCore UnitTests.

This commit is contained in:
2022-07-17 12:21:05 +02:00
parent 73038bbe5a
commit a26d6a0036
46 changed files with 2411 additions and 105 deletions

View File

@@ -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>());
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View File

@@ -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;
}
}
}

View 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;
}
}
}

View File

@@ -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; }
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View File

@@ -0,0 +1,74 @@
using System.Security.Cryptography;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Common.Extensions
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class CryptographyHelperExtensionsTests
{
[TestMethod]
public void ShouldReturnMd5Hash()
{
// arrange
string str = "Hello World!";
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string strHash = str.Md5();
string byteHash = bytes.Md5();
// assert
Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", strHash);
Assert.AreEqual("63c983de427ce9e2430ba8554d2a822f", byteHash);
}
[TestMethod]
public void ShouldReturnSha1Hash()
{
// arrange
string str = "Hello World!";
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string strHash = str.Sha1();
string byteHash = bytes.Sha1();
// assert
Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", strHash);
Assert.AreEqual("ec2c39d500316044fa49f6c8f471ddec8b4fb9d1", byteHash);
}
[TestMethod]
public void ShouldReturnSha256Hash()
{
// arrange
string str = "Hello World!";
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string strHash = str.Sha256();
string byteHash = bytes.Sha256();
// assert
Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", strHash);
Assert.AreEqual("4e0da689dc7a51957be426d6cfb1bd860169cb25dd1ac946a2f141228217804a", byteHash);
}
[TestMethod]
public void ShouldReturnSha512Hash()
{
// arrange
string str = "Hello World!";
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string strHash = str.Sha512();
string byteHash = bytes.Sha512();
// assert
Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", strHash);
Assert.AreEqual("591098c5d470a09f0ff48a4fdb7769ab89f803eae9e23b6f9f69dd228cca46c074bbc11a5fceaa8a5f48d14d2bf19a83a629266c2c5b7d9ef34623b64cb2f8e7", byteHash);
}
}
}

View File

@@ -0,0 +1,355 @@
using System;
using UnitTests.Common.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Common.Extensions
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class DateTimeExtensionsTests
{
[TestMethod]
public void ShouldReturnUtc()
{
// arrange
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
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 unspecified = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Unspecified);
// act
var utcCorrected = utc.AsUtc();
var localCorrected = local.AsUtc();
var unspecifiedCorrected = unspecified.AsUtc();
// assert
Assert.AreEqual(DateTimeKind.Utc, utcCorrected.Kind);
Assert.AreEqual(DateTimeKind.Utc, localCorrected.Kind);
Assert.AreEqual(DateTimeKind.Utc, unspecifiedCorrected.Kind);
Assert.AreEqual(utc, utcCorrected);
Assert.AreEqual(utc.AddHours(-1), localCorrected);
Assert.AreEqual(utc, unspecifiedCorrected);
}
[TestMethod]
public void ShouldReturnLocal()
{
// arrange
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
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 unspecified = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Unspecified);
// act
var utcCorrected = utc.AsLocal();
var localCorrected = local.AsLocal();
var unspecifiedCorrected = unspecified.AsLocal();
// assert
Assert.AreEqual(DateTimeKind.Local, utcCorrected.Kind);
Assert.AreEqual(DateTimeKind.Local, localCorrected.Kind);
Assert.AreEqual(DateTimeKind.Local, unspecifiedCorrected.Kind);
Assert.AreEqual(local.AddHours(1), utcCorrected);
Assert.AreEqual(local, localCorrected);
Assert.AreEqual(local, unspecifiedCorrected);
}
[TestMethod]
public void ShouldReturnCorrectUtcAlignmentDaylightSavingEnd()
{
// arrange
var dateTime = new DateTime(2021, 10, 30, 12, 15, 30, 45, DateTimeKind.Utc);
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
var intervalSixMinutes = TimeSpan.FromMinutes(6);
var intervalTwoHours = TimeSpan.FromHours(2);
var intervalDay = TimeSpan.FromDays(1);
var offsetTwoMinutes = TimeSpan.FromMinutes(2);
var offsetFourHours = TimeSpan.FromHours(4);
// act
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
var diffDay = intervalDay.GetAlignedInterval(dateTime);
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
// assert
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
Assert.AreEqual(TimeSpan.Parse("00:02:29.955"), diffSixMinutes);
Assert.AreEqual(TimeSpan.Parse("01:44:29.955"), diffTwoHours);
Assert.AreEqual(TimeSpan.Parse("11:44:29.955"), diffDay);
Assert.AreEqual(TimeSpan.Parse("01:46:29.955"), diffTwoHoursOffset);
Assert.AreEqual(TimeSpan.Parse("15:44:29.955"), diffDayOffset); // must be the same whether daylight saving has ended
}
[TestMethod]
public void ShouldReturnCorrectUtcAlignmentDaylightSavingStart()
{
// arrange
var dateTime = new DateTime(2022, 3, 26, 12, 15, 30, 45, DateTimeKind.Utc);
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
var intervalSixMinutes = TimeSpan.FromMinutes(6);
var intervalTwoHours = TimeSpan.FromHours(2);
var intervalDay = TimeSpan.FromDays(1);
var offsetTwoMinutes = TimeSpan.FromMinutes(2);
var offsetFourHours = TimeSpan.FromHours(4);
// act
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
var diffDay = intervalDay.GetAlignedInterval(dateTime);
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
// assert
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
Assert.AreEqual(TimeSpan.Parse("00:02:29.955"), diffSixMinutes);
Assert.AreEqual(TimeSpan.Parse("01:44:29.955"), diffTwoHours);
Assert.AreEqual(TimeSpan.Parse("11:44:29.955"), diffDay);
Assert.AreEqual(TimeSpan.Parse("01:46:29.955"), diffTwoHoursOffset);
Assert.AreEqual(TimeSpan.Parse("15:44:29.955"), diffDayOffset); // must be the same whether daylight saving has started
}
[TestMethod]
public void ShouldReturnCorrectLocalAlignmentDaylightSavingEnd()
{
// arrange
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
var dateTime = new DateTime(2021, 10, 30, 12, 15, 30, 45, DateTimeKind.Local);
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
var intervalSixMinutes = TimeSpan.FromMinutes(6);
var intervalTwoHours = TimeSpan.FromHours(2);
var intervalDay = TimeSpan.FromDays(1);
var offsetTwoMinutes = TimeSpan.FromMinutes(2);
var offsetFourHours = TimeSpan.FromHours(4);
// act
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
var diffDay = intervalDay.GetAlignedInterval(dateTime);
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
// assert
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
Assert.AreEqual(TimeSpan.Parse("00:02:29.955"), diffSixMinutes);
Assert.AreEqual(TimeSpan.Parse("01:44:29.955"), diffTwoHours);
Assert.AreEqual(TimeSpan.Parse("11:44:29.955"), diffDay);
Assert.AreEqual(TimeSpan.Parse("01:46:29.955"), diffTwoHoursOffset);
Assert.AreEqual(TimeSpan.Parse("14:44:29.955"), diffDayOffset); // has to be minus one hour due to daylight saving ended
}
[TestMethod]
public void ShouldReturnCorrectLocalAlignmentDaylightSavingStart()
{
// arrange
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
var dateTime = new DateTime(2022, 3, 26, 12, 15, 30, 45, DateTimeKind.Local);
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
var intervalSixMinutes = TimeSpan.FromMinutes(6);
var intervalTwoHours = TimeSpan.FromHours(2);
var intervalDay = TimeSpan.FromDays(1);
var offsetTwoMinutes = TimeSpan.FromMinutes(2);
var offsetFourHours = TimeSpan.FromHours(4);
// act
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
var diffDay = intervalDay.GetAlignedInterval(dateTime);
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
// assert
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
Assert.AreEqual(TimeSpan.Parse("00:02:29.955"), diffSixMinutes);
Assert.AreEqual(TimeSpan.Parse("01:44:29.955"), diffTwoHours);
Assert.AreEqual(TimeSpan.Parse("11:44:29.955"), diffDay);
Assert.AreEqual(TimeSpan.Parse("01:46:29.955"), diffTwoHoursOffset);
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]
public void ShouldReturnCorrectShortStringForTimeSpan()
{
// arrange
var timeSpan = TimeSpan.Parse("1.10:11:12.345");
var negativeTimeSpan = TimeSpan.FromDays(-1.234);
// act
string shortString = timeSpan.ToShortString(withMilliseconds: false);
string shortStringWithMillis = timeSpan.ToShortString(withMilliseconds: true);
string shortStringNegative = negativeTimeSpan.ToShortString(withMilliseconds: true);
// assert
Assert.AreEqual("1d 10h 11m 12s", shortString);
Assert.AreEqual("1d 10h 11m 12s 345ms", shortStringWithMillis);
Assert.AreEqual("-1d 5h 36m 57s 600ms", shortStringNegative);
}
[TestMethod]
public void ShouldRoundToSecond()
{
// arrange
var timestampUp = new DateTime(2021, 11, 16, 20, 15, 30, 500);
var timestampDown = new DateTime(2021, 11, 16, 20, 15, 30, 499);
var timespanUp = new TimeSpan(1, 2, 3, 4, 500);
var timespanDown = new TimeSpan(1, 2, 3, 4, 499);
// act
var timestampUpRounded = timestampUp.RoundToSecond();
var timestampDownRounded = timestampDown.RoundToSecond();
var timespanUpRounded = timespanUp.RoundToSecond();
var timespanDownRounded = timespanDown.RoundToSecond();
// assert
Assert.AreNotEqual(timestampUp, timestampUpRounded);
Assert.AreEqual(DateTime.Parse("2021-11-16 20:15:31.000"), timestampUpRounded);
Assert.AreNotEqual(timestampDown, timestampDownRounded);
Assert.AreEqual(DateTime.Parse("2021-11-16 20:15:30.000"), timestampDownRounded);
Assert.AreNotEqual(timespanUp, timespanUpRounded);
Assert.AreEqual(TimeSpan.Parse("1.02:03:05.000"), timespanUpRounded);
Assert.AreNotEqual(timespanDown, timespanDownRounded);
Assert.AreEqual(TimeSpan.Parse("1.02:03:04.000"), timespanDownRounded);
}
[TestMethod]
public void ShouldRoundToMinute()
{
// arrange
var timestampUp = new DateTime(2021, 11, 16, 20, 15, 30, 0);
var timestampDown = new DateTime(2021, 11, 16, 20, 15, 29, 999);
var timespanUp = new TimeSpan(1, 2, 3, 30, 0);
var timespanDown = new TimeSpan(1, 2, 3, 29, 999);
// act
var timestampUpRounded = timestampUp.RoundToMinute();
var timestampDownRounded = timestampDown.RoundToMinute();
var timespanUpRounded = timespanUp.RoundToMinute();
var timespanDownRounded = timespanDown.RoundToMinute();
// assert
Assert.AreNotEqual(timestampUp, timestampUpRounded);
Assert.AreEqual(DateTime.Parse("2021-11-16 20:16:00.000"), timestampUpRounded);
Assert.AreNotEqual(timestampDown, timestampDownRounded);
Assert.AreEqual(DateTime.Parse("2021-11-16 20:15:00.000"), timestampDownRounded);
Assert.AreNotEqual(timespanUp, timespanUpRounded);
Assert.AreEqual(TimeSpan.Parse("1.02:04:00.000"), timespanUpRounded);
Assert.AreNotEqual(timespanDown, timespanDownRounded);
Assert.AreEqual(TimeSpan.Parse("1.02:03:00.000"), timespanDownRounded);
}
[TestMethod]
public void ShouldRoundToHour()
{
// arrange
var timestampUp = new DateTime(2021, 11, 16, 20, 30, 0, 0);
var timestampDown = new DateTime(2021, 11, 16, 20, 29, 59, 999);
var timespanUp = new TimeSpan(1, 2, 30, 0, 0);
var timespanDown = new TimeSpan(1, 2, 29, 59, 999);
// act
var timestampUpRounded = timestampUp.RoundToHour();
var timestampDownRounded = timestampDown.RoundToHour();
var timespanUpRounded = timespanUp.RoundToHour();
var timespanDownRounded = timespanDown.RoundToHour();
// assert
Assert.AreNotEqual(timestampUp, timestampUpRounded);
Assert.AreEqual(DateTime.Parse("2021-11-16 21:00:00.000"), timestampUpRounded);
Assert.AreNotEqual(timestampDown, timestampDownRounded);
Assert.AreEqual(DateTime.Parse("2021-11-16 20:00:00.000"), timestampDownRounded);
Assert.AreNotEqual(timespanUp, timespanUpRounded);
Assert.AreEqual(TimeSpan.Parse("1.03:00:00.000"), timespanUpRounded);
Assert.AreNotEqual(timespanDown, timespanDownRounded);
Assert.AreEqual(TimeSpan.Parse("1.02:00:00.000"), timespanDownRounded);
}
[TestMethod]
public void ShouldRoundToDay()
{
// arrange
var timestampUp = new DateTime(2021, 11, 16, 12, 0, 0, 0);
var timestampDown = new DateTime(2021, 11, 16, 11, 59, 59, 999);
var timespanUp = new TimeSpan(1, 12, 0, 0, 0);
var timespanDown = new TimeSpan(1, 11, 59, 59, 999);
// act
var timestampUpRounded = timestampUp.RoundToDay();
var timestampDownRounded = timestampDown.RoundToDay();
var timespanUpRounded = timespanUp.RoundToDay();
var timespanDownRounded = timespanDown.RoundToDay();
// assert
Assert.AreNotEqual(timestampUp, timestampUpRounded);
Assert.AreEqual(DateTime.Parse("2021-11-17 00:00:00.000"), timestampUpRounded);
Assert.AreNotEqual(timestampDown, timestampDownRounded);
Assert.AreEqual(DateTime.Parse("2021-11-16 00:00:00.000"), timestampDownRounded);
Assert.AreNotEqual(timespanUp, timespanUpRounded);
Assert.AreEqual(TimeSpan.Parse("2.00:00:00.000"), timespanUpRounded);
Assert.AreNotEqual(timespanDown, timespanDownRounded);
Assert.AreEqual(TimeSpan.Parse("1.00:00:00.000"), timespanDownRounded);
}
}
}

View File

@@ -0,0 +1,121 @@
using System;
using System.Linq;
using UnitTests.Common.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using DescriptionAttribute = System.ComponentModel.DescriptionAttribute;
namespace UnitTests.Common.Extensions
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class EnumExtensionsTests
{
[TestMethod]
public void ShouldReturnEmptyList()
{
// arrange
var enumValue = TestEnum.Two;
// act
var customList = enumValue.GetAttributes<CustomMultipleAttribute>();
var descriptionList = enumValue.GetAttributes<DescriptionAttribute>();
// assert
Assert.IsNotNull(customList);
Assert.IsFalse(customList.Any());
Assert.IsNotNull(descriptionList);
Assert.IsFalse(descriptionList.Any());
}
[TestMethod]
public void ShouldReturnList()
{
// arrange
var enumValue = TestEnum.Zero;
// act
var customList = enumValue.GetAttributes<CustomMultipleAttribute>();
var descriptionList = enumValue.GetAttributes<DescriptionAttribute>();
// assert
Assert.IsNotNull(customList);
Assert.IsTrue(customList.Any());
Assert.AreEqual(2, customList.Count());
Assert.IsNotNull(descriptionList);
Assert.IsTrue(descriptionList.Any());
Assert.AreEqual(1, descriptionList.Count());
}
[TestMethod]
public void ShouldReturnNothing()
{
// arrange
var enumValue = TestEnum.Two;
// act
var customAttribute = enumValue.GetAttribute<CustomMultipleAttribute>();
var descriptionAttribute = enumValue.GetAttribute<DescriptionAttribute>();
// assert
Assert.IsNull(customAttribute);
Assert.IsNull(descriptionAttribute);
}
[TestMethod]
public void ShouldReturnFirstAttribute()
{
// arrange
var enumValue = TestEnum.Zero;
// act
var customAttribute = enumValue.GetAttribute<CustomMultipleAttribute>();
var descriptionAttribute = enumValue.GetAttribute<DescriptionAttribute>();
// assert
Assert.IsNotNull(customAttribute);
Assert.AreEqual("nix", customAttribute.Name);
Assert.IsNotNull(descriptionAttribute);
Assert.AreEqual("Null", descriptionAttribute.Description);
}
[TestMethod]
public void ShouldReturnDescriptionOrStringRepresentation()
{
// arrange
var enumWithDescription = TestEnum.One;
var enumWithoutDescripton = TestEnum.Two;
// act
string description = enumWithDescription.GetDescription();
string noDescription = enumWithoutDescripton.GetDescription();
// assert
Assert.AreEqual("Eins", description);
Assert.AreEqual(enumWithoutDescripton.ToString(), noDescription);
}
[TestMethod]
public void ShouldReturnEmptyListOnNotDefinedEnumValue()
{
// arrange
var notDefinedEnum = (TestEnum)10;
// act
var list = notDefinedEnum.GetAttributes<DescriptionAttribute>();
// assert
Assert.IsFalse(list.Any());
}
internal enum TestEnum
{
[CustomMultiple("nix")]
[CustomMultiple("Null")]
[Description("Null")]
Zero,
[Description("Eins")]
One,
Two,
}
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Common.Extensions
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class ExceptionExtensionsTests
{
[TestMethod]
public void ShouldReturnExceptionMessage()
{
// arrange
var exception = new Exception("This is a message.");
// act
string message = exception.GetMessage();
// assert
Assert.AreEqual(exception.Message, message);
}
[TestMethod]
public void ShouldReturnInnerExceptionMessage()
{
// arrange
var innerException = new Exception("Message from the inner side.");
var outerException = new Exception("Message from the outer side.", innerException);
// act
string message = outerException.GetMessage();
// assert
Assert.AreEqual(innerException.Message, message);
}
[TestMethod]
public void ShouldReturnRecursiveExceptionMessageFoInnerException()
{
// arrange
var innerException = new Exception("Message from the inner side.");
var outerException = new Exception("Message from the outer side. See the inner exception for details.", innerException);
string expectedMessage = $"Message from the outer side. Message from the inner side.";
// act
string message = outerException.GetRecursiveMessage();
// assert
Assert.AreEqual(expectedMessage, message);
}
[TestMethod]
public void ShouldReturnRecursiveExceptionMessageFoInnerExceptions()
{
// arrange
var innerExceptions = new List<Exception>
{
new Exception("Inner Exception 1."),
new Exception("Inner Exception 2. See the inner exception for details.", new Exception("Inner Exception of Exception 2.")),
new Exception("Inner Exception 3."),
new Exception("Inner Exception 4."),
new Exception("Inner Exception 5.")
};
var aggregateException = new AggregateException("Lots of exceptions.", innerExceptions);
string expectedMessage = "Inner Exception 1. Inner Exception 2. Inner Exception of Exception 2. Inner Exception 3.";
// act
string message = aggregateException.GetRecursiveMessage();
// assert
Assert.AreEqual(expectedMessage, message);
}
}
}

View File

@@ -0,0 +1,341 @@
using System;
using System.Collections;
using UnitTests.Common.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace UnitTests.Common.Extensions
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class JsonExtensionsTests
{
[TestMethod]
public void ShouldReturnJson()
{
// arrange
var testObject = new JsonTestClass();
string expected = @"{""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"":{""integerValue"":42,""stringValue"":""Foo-Bar""}}";
// act
string json = testObject.SerializeJson();
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected, json);
}
[TestMethod]
public void ShouldReturnJsonIndented()
{
// arrange
var testObject = new JsonTestClass();
string expected = @"{
""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"": {
""integerValue"": 42,
""stringValue"": ""Foo-Bar""
}
}";
// act
string json = testObject.SerializeJson(indented: true);
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected.Replace("\r", ""), json.Replace("\r", ""));
}
[TestMethod]
public void ShouldReturnJsonWithSingleQuotes()
{
// arrange
var testObject = new JsonTestClass
{
StringValue = "Sam's Pub"
};
string expected = "{'stringValue':'Sam\\'s Pub','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':{'integerValue':42,'stringValue':'Foo-Bar'}}";
// act
string json = testObject.SerializeJson(useSingleQuotes: true);
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected, json);
}
[TestMethod]
public void ShouldReturnJsonWithoutCamelCase()
{
// arrange
var testObject = new JsonTestClass();
string expected = @"{""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"":{""IntegerValue"":42,""StringValue"":""Foo-Bar""}}";
// act
string json = testObject.SerializeJson(useCamelCase: false);
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected, json);
}
[TestMethod]
public void ShouldReturnJsonWithType()
{
// arrange
var testObject = new JsonTestClass();
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
string json = testObject.SerializeJson(includeType: true);
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected, json);
}
[TestMethod]
public void ShouldDeserializeWithoutFallback()
{
// arrange
string workingJson = @"{""stringValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21,""stringValue"":""FooBar""}}";
string brokenJson = @"{""strValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21.12,""stringValue"":""FooBar""}}";
string emptyString = "";
// act
var workingObj = workingJson.DeserializeJson<JsonTestClass>();
var emptyObj = emptyString.DeserializeJson<JsonTestClass>();
try
{
var brokenObj = brokenJson.DeserializeJson<JsonTestClass>();
Assert.Fail();
}
catch (JsonReaderException)
{ }
// assert
Assert.IsNotNull(workingObj);
Assert.IsNull(emptyObj);
Assert.AreEqual("Some fancy string", workingObj.StringValue);
Assert.IsFalse(workingObj.IsBoolTrue);
Assert.AreEqual(123.45m, workingObj.DecimalValue);
Assert.AreEqual(DateTimeKind.Local, workingObj.LocalTimestamp.Kind);
Assert.AreEqual("15.11.2021 20:15:34", workingObj.LocalTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
Assert.AreEqual(DateTimeKind.Utc, workingObj.UtcTimestamp.Kind);
Assert.AreEqual("16.10.2021 20:15:34", workingObj.UtcTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
}
[TestMethod]
public void ShouldDeserializeWithFallback()
{
// arrange
string workingJson = @"{""stringValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21,""stringValue"":""FooBar""}}";
string brokenJson = @"{""strValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21.12,""stringValue"":""FooBar""}}";
string emptyString = "";
var fallback = new JsonTestClass
{
DoubleValue = 0.815
};
// act
var workingObj = workingJson.DeserializeJson(fallback);
var brokenObj = brokenJson.DeserializeJson(fallback);
var emptyObj = emptyString.DeserializeJson(fallback);
// assert
Assert.IsNotNull(workingObj);
Assert.IsNotNull(brokenObj);
Assert.IsNull(emptyObj);
Assert.AreEqual("Some fancy string", workingObj.StringValue);
Assert.IsFalse(workingObj.IsBoolTrue);
Assert.AreEqual(123.45m, workingObj.DecimalValue);
Assert.AreEqual(21.42, workingObj.DoubleValue);
Assert.AreEqual(DateTimeKind.Local, workingObj.LocalTimestamp.Kind);
Assert.AreEqual("15.11.2021 20:15:34", workingObj.LocalTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
Assert.AreEqual(DateTimeKind.Utc, workingObj.UtcTimestamp.Kind);
Assert.AreEqual("16.10.2021 20:15:34", workingObj.UtcTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
Assert.AreEqual(fallback.StringValue, brokenObj.StringValue);
Assert.AreEqual(fallback.IsBoolTrue, brokenObj.IsBoolTrue);
Assert.AreEqual(fallback.DecimalValue, brokenObj.DecimalValue);
Assert.AreEqual(fallback.DoubleValue, brokenObj.DoubleValue);
Assert.AreEqual(fallback.LocalTimestamp.Kind, brokenObj.LocalTimestamp.Kind);
Assert.AreEqual(fallback.LocalTimestamp, brokenObj.LocalTimestamp);
Assert.AreEqual(fallback.UtcTimestamp.Kind, brokenObj.UtcTimestamp.Kind);
Assert.AreEqual(fallback.UtcTimestamp, brokenObj.UtcTimestamp);
}
[TestMethod]
public void ShouldDeserializeUsingPopulate()
{
// arrange
string json = @"{""stringValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21,""stringValue"":""FooBar""}}";
var obj = new JsonTestClass();
// act
obj.DeserializeJson(json);
// assert
Assert.AreEqual("Some fancy string", obj.StringValue);
Assert.IsFalse(obj.IsBoolTrue);
Assert.AreEqual(123.45m, obj.DecimalValue);
Assert.AreEqual(21.42, obj.DoubleValue);
Assert.AreEqual(DateTimeKind.Local, obj.LocalTimestamp.Kind);
Assert.AreEqual("15.11.2021 20:15:34", obj.LocalTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
Assert.AreEqual(DateTimeKind.Utc, obj.UtcTimestamp.Kind);
Assert.AreEqual("16.10.2021 20:15:34", obj.UtcTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
}
[TestMethod]
public void ShouldConvertToJObject()
{
// arrange
var obj = new JsonTestClass
{
StringValue = "Hello JSON",
DecimalValue = 0.815m
};
// act
var jObj = obj.ConvertToJObject();
// assert
Assert.IsNotNull(jObj);
Assert.AreEqual(typeof(JObject), jObj.GetType());
Assert.AreEqual(obj.StringValue, jObj.Value<string>("stringValue"));
Assert.AreEqual(obj.DecimalValue, jObj.Value<decimal>("decimalValue"));
Assert.AreEqual(obj.LocalTimestamp.Kind, jObj.Value<DateTime>("localTimestamp").Kind);
Assert.AreEqual(obj.LocalTimestamp, jObj.Value<DateTime>("localTimestamp"));
Assert.AreEqual(obj.UtcTimestamp.Kind, jObj.Value<DateTime>("utcTimestamp").Kind);
Assert.AreEqual(obj.UtcTimestamp, jObj.Value<DateTime>("utcTimestamp"));
}
[TestMethod]
public void ShouldConvertToJArray()
{
// arrange
string[] stringArray = new[] { "one", "two", "three" };
var objectArray = new[]
{
new JsonTestClass { StringValue = "One" },
new JsonTestClass { StringValue = "Two" },
new JsonTestClass { StringValue = "Three" }
};
// act
var stringJArray = stringArray.ConvertToJArray();
var objectJArray = objectArray.ConvertToJArray();
// assert
Assert.IsNotNull(stringJArray);
Assert.AreEqual(typeof(JArray), stringJArray.GetType());
Assert.AreEqual(stringArray[0], stringJArray[0]);
Assert.AreEqual(stringArray[1], stringJArray[1]);
Assert.AreEqual(stringArray[2], stringJArray[2]);
Assert.IsNotNull(objectJArray);
Assert.AreEqual(typeof(JArray), objectJArray.GetType());
Assert.AreEqual(objectArray[0].StringValue, objectJArray[0].Value<string>("stringValue"));
Assert.AreEqual(objectArray[1].StringValue, objectJArray[1].Value<string>("stringValue"));
Assert.AreEqual(objectArray[2].StringValue, objectJArray[2].Value<string>("stringValue"));
}
[TestMethod]
public void ShouldRunJsonTreeWithoutDefault()
{
// arrange
var obj = new JsonTestClass { StringValue = "Running Json", Object = new JsonTestSubClass { IntegerValue = 4711 } };
var jObj = obj.ConvertToJObject();
// act
string topLevelString = jObj.GetValue<string>("stringValue");
decimal topLevelDecimal = jObj.GetValue<decimal>("decimalValue");
int subLevelInteger = jObj.GetValue<int>("object:integerValue");
string subLevelString = jObj.GetValue<string>("object:stringValue");
string notExistingOnTopLevel = jObj.GetValue<string>("fancyValue");
string notExistingOnSubLevel = jObj.GetValue<string>("object:fancyValue");
int? notExistingLevel = jObj.GetValue<int?>("fancy:int");
// assert
Assert.AreEqual(obj.StringValue, topLevelString);
Assert.AreEqual(obj.DecimalValue, topLevelDecimal);
Assert.AreEqual(obj.Object.IntegerValue, subLevelInteger);
Assert.AreEqual(obj.Object.StringValue, subLevelString);
Assert.IsNull(notExistingOnTopLevel);
Assert.IsNull(notExistingOnSubLevel);
Assert.IsNull(notExistingLevel);
}
[TestMethod]
public void ShouldRunJsonTreeWithDefault()
{
// arrange
var obj = new JsonTestClass { StringValue = "Running Json", Object = new JsonTestSubClass { IntegerValue = 4711 } };
var jObj = obj.ConvertToJObject();
// act
string topLevelString = jObj.GetValue("stringValue", "Test String");
decimal topLevelDecimal = jObj.GetValue("decimalValue", 13.24m);
int subLevelInteger = jObj.GetValue("object:integerValue", 55);
string subLevelString = jObj.GetValue("object:stringValue", "Yeah!");
string notExistingOnTopLevel = jObj.GetValue("fancyValue", "Party!");
string notExistingOnSubLevel = jObj.GetValue("object:fancyValue", "Well Done");
int? notExistingLevel = jObj.GetValue<int?>("fancy:int", 13);
// assert
Assert.AreEqual(obj.StringValue, topLevelString);
Assert.AreEqual(obj.DecimalValue, topLevelDecimal);
Assert.AreEqual(obj.Object.IntegerValue, subLevelInteger);
Assert.AreEqual(obj.Object.StringValue, subLevelString);
Assert.AreEqual("Party!", notExistingOnTopLevel);
Assert.AreEqual("Well Done", notExistingOnSubLevel);
Assert.AreEqual(13, notExistingLevel);
}
[TestMethod]
public void ShouldReturnNull()
{
// arrange
object obj = null;
IEnumerable list = null;
JObject jObj = null;
// act
var objTest = obj.ConvertToJObject();
var listTest = list.ConvertToJArray();
object getTest = jObj.GetValue<object>("Nothing");
// assert
Assert.IsNull(objTest);
Assert.IsNull(listTest);
Assert.IsNull(getTest);
}
[TestMethod]
public void ShouldHandleSerializationError()
{
// arrange
var obj = new JsonErrorClass();
// act
string json = obj.SerializeJson();
// assert
Assert.AreEqual("{}", json);
}
}
}

View File

@@ -0,0 +1,221 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Common.Extensions
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class ReaderWriterLockSlimExtensionsTests
{
[TestMethod]
public void ShouldEnterReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposable = rwLock.GetReadLock();
// assert
Assert.IsNotNull(disposable);
Assert.IsTrue(rwLock.IsReadLockHeld);
Assert.IsFalse(rwLock.IsUpgradeableReadLockHeld);
Assert.IsFalse(rwLock.IsWriteLockHeld);
}
[TestMethod]
public void ShouldEnterUpgradeableReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposable = rwLock.GetUpgradeableReadLock();
// assert
Assert.IsNotNull(disposable);
Assert.IsFalse(rwLock.IsReadLockHeld);
Assert.IsTrue(rwLock.IsUpgradeableReadLockHeld);
Assert.IsFalse(rwLock.IsWriteLockHeld);
}
[TestMethod]
public void ShouldAllowWriteLockAfterUpgradableReadLock()
{
// arrange
var rwLockUsing = new ReaderWriterLockSlim();
var rwLockClassic = new ReaderWriterLockSlim();
// act
using var disposableReadUsing = rwLockUsing.GetUpgradeableReadLock();
using (rwLockUsing.GetWriteLock())
{
// assert
Assert.IsTrue(rwLockUsing.IsUpgradeableReadLockHeld);
Assert.IsTrue(rwLockUsing.IsWriteLockHeld);
}
// assert
Assert.IsTrue(rwLockUsing.IsUpgradeableReadLockHeld);
Assert.IsFalse(rwLockUsing.IsWriteLockHeld);
// act
using (rwLockClassic.GetUpgradeableReadLock())
{
rwLockClassic.EnterWriteLock();
// assert
Assert.IsTrue(rwLockClassic.IsUpgradeableReadLockHeld);
Assert.IsTrue(rwLockClassic.IsWriteLockHeld);
}
// assert
Assert.IsFalse(rwLockClassic.IsUpgradeableReadLockHeld);
Assert.IsFalse(rwLockClassic.IsWriteLockHeld);
}
[TestMethod]
public void ShouldEnterWriteLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposable = rwLock.GetWriteLock();
// assert
Assert.IsNotNull(disposable);
Assert.IsFalse(rwLock.IsReadLockHeld);
Assert.IsFalse(rwLock.IsUpgradeableReadLockHeld);
Assert.IsTrue(rwLock.IsWriteLockHeld);
}
[TestMethod]
public void ShouldNotAllowWriteLockAfterReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposableRead = rwLock.GetReadLock();
try
{
using var disposaleWrite = rwLock.GetWriteLock();
Assert.Fail();
}
catch (LockRecursionException)
{ }
// assert
Assert.IsNotNull(disposableRead);
Assert.IsTrue(rwLock.IsReadLockHeld);
Assert.IsFalse(rwLock.IsUpgradeableReadLockHeld);
Assert.IsFalse(rwLock.IsWriteLockHeld);
}
[TestMethod]
public void ShouldAllowWriteLockAfterUpgradeableReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposableRead = rwLock.GetUpgradeableReadLock();
using var disposableWrite = rwLock.GetWriteLock();
// assert
Assert.IsNotNull(disposableRead);
Assert.IsNotNull(disposableWrite);
Assert.IsFalse(rwLock.IsReadLockHeld);
Assert.IsTrue(rwLock.IsUpgradeableReadLockHeld);
Assert.IsTrue(rwLock.IsWriteLockHeld);
}
[TestMethod]
public void ShouldGetTimeoutOnReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
bool isTimeout = false;
// act
using var disposableRead = rwLock.GetWriteLock();
var awaitableTask = Task.Run(() =>
{
try
{
using var disposableRead = rwLock.GetReadLock(10);
Assert.Fail();
}
catch (TimeoutException)
{
isTimeout = true;
}
catch (Exception)
{ /* keep it quiet */ }
});
Task.WaitAll(awaitableTask);
// assert
Assert.IsTrue(isTimeout);
}
[TestMethod]
public void ShouldGetTimeoutOnUpgradeableReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
bool isTimeout = false;
// act
using var disposableRead = rwLock.GetWriteLock();
var awaitableTask = Task.Run(() =>
{
try
{
using var disposableRead = rwLock.GetUpgradeableReadLock(10);
Assert.Fail();
}
catch (TimeoutException)
{
isTimeout = true;
}
catch (Exception)
{ /* keep it quiet */ }
});
Task.WaitAll(awaitableTask);
// assert
Assert.IsTrue(isTimeout);
}
[TestMethod]
public void ShouldGetTimeoutOnWriteLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
bool isTimeout = false;
// act
using var disposableRead = rwLock.GetReadLock();
var awaitableTask = Task.Run(() =>
{
try
{
using var disposableRead = rwLock.GetWriteLock(10);
Assert.Fail();
}
catch (TimeoutException)
{
isTimeout = true;
}
catch (Exception)
{ /* keep it quiet */ }
});
Task.WaitAll(awaitableTask);
// assert
Assert.IsTrue(isTimeout);
}
}
}

View File

@@ -0,0 +1,139 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Common.Extensions
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class StreamExtensionsTests
{
[TestMethod]
public void ShouldReadLineFromStreamSynchronous()
{
// arrange
var sb = new StringBuilder();
sb.AppendLine("First Line");
sb.AppendLine("Second Line");
byte[] buffer = Encoding.UTF8.GetBytes(sb.ToString().Trim());
var stream = new MemoryStream(buffer);
// act
string line = stream.ReadLine();
// assert
Assert.AreEqual("First Line", line);
stream.Dispose();
}
[TestMethod]
public void ShouldReadUntilEndAsLineSynchronous()
{
// arrange
byte[] buffer = Encoding.UTF8.GetBytes("Single Line");
var stream = new MemoryStream(buffer);
// act
string line = stream.ReadLine();
// assert
Assert.AreEqual("Single Line", line);
stream.Dispose();
}
[TestMethod]
public void ShouldReturnNullWhenNotReadableSynchronous()
{
// arrange
var stream = new WriteOnlyStream();
// act
string line = stream.ReadLine();
// assert
Assert.IsNull(line);
stream.Dispose();
}
[TestMethod]
public async Task ShouldReadLineFromStreamAsynchronous()
{
// arrange
var sb = new StringBuilder();
sb.AppendLine("First Line");
sb.AppendLine("Second Line");
byte[] buffer = Encoding.UTF8.GetBytes(sb.ToString().Trim());
var stream = new MemoryStream(buffer);
// act
string line = await stream.ReadLineAsync();
// assert
Assert.AreEqual("First Line", line);
stream.Dispose();
}
[TestMethod]
public async Task ShouldReadUntilEndAsLineAsynchronous()
{
// arrange
byte[] buffer = Encoding.UTF8.GetBytes("Single Line");
var stream = new MemoryStream(buffer);
// act
string line = await stream.ReadLineAsync();
// assert
Assert.AreEqual("Single Line", line);
stream.Dispose();
}
[TestMethod]
public async Task ShouldReturnNullWhenNotReadableAsynchronous()
{
// arrange
var stream = new WriteOnlyStream();
// act
string line = await stream.ReadLineAsync();
// assert
Assert.IsNull(line);
stream.Dispose();
}
private class WriteOnlyStream : Stream
{
public override bool CanRead => false;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => 0;
public override long Position { get => 0; set => throw new NotImplementedException(); }
public override void Flush()
{ }
public override int Read(byte[] buffer, int offset, int count) => 0;
public override long Seek(long offset, SeekOrigin origin) => 0;
public override void SetLength(long value)
{ }
public override void Write(byte[] buffer, int offset, int count)
{ }
}
}
}

View File

@@ -0,0 +1,353 @@
using System;
using System.Linq;
using System.Net;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Common.Extensions
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class StringExtensionsTests
{
[TestMethod]
public void ShouldReturnEmptyList()
{
// arrange
string hex = "";
// act
var bytes = hex.HexToBytes();
// assert
Assert.IsNotNull(bytes);
Assert.IsFalse(bytes.Any());
}
[TestMethod]
public void ShouldReturnEmptyListWhenInvalid()
{
// arrange
string hex1 = "aff";
string hex2 = "de:ad:be:e";
string hex3 = "hell";
// act
var bytes1 = hex1.HexToBytes();
var bytes2 = hex2.HexToBytes(":");
var bytes3 = hex3.HexToBytes();
// assert
Assert.IsNotNull(bytes1);
Assert.IsFalse(bytes1.Any());
Assert.IsNotNull(bytes2);
Assert.IsFalse(bytes2.Any());
Assert.IsNotNull(bytes3);
Assert.IsFalse(bytes3.Any());
}
[TestMethod]
public void ShouldConvertHexToBytes()
{
// arrange
string hex = "deadbeef";
// act
var bytes = hex.HexToBytes();
// assert
Assert.IsNotNull(bytes);
Assert.AreEqual(4, bytes.Count());
Assert.AreEqual(0xde, bytes.ElementAt(0));
Assert.AreEqual(0xad, bytes.ElementAt(1));
Assert.AreEqual(0xbe, bytes.ElementAt(2));
Assert.AreEqual(0xef, bytes.ElementAt(3));
}
[TestMethod]
public void ShouldConvertHexToBytesWithDelimiter()
{
// arrange
string hex = "af:fe";
// act
var bytes = hex.HexToBytes(":");
// assert
Assert.IsNotNull(bytes);
Assert.AreEqual(2, bytes.Count());
Assert.AreEqual(0xaf, bytes.ElementAt(0));
Assert.AreEqual(0xfe, bytes.ElementAt(1));
}
[TestMethod]
public void ShouldReturnNullWhenInvalid()
{
// arrange
byte[] bytes1 = null;
byte[] bytes2 = Array.Empty<byte>();
// act
string hex1 = bytes1.BytesToHex();
string hex2 = bytes2.BytesToHex();
// assert
Assert.IsNull(hex1);
Assert.IsNull(hex2);
}
[TestMethod]
public void ShouldReturnHexString()
{
// arrange
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string hex = bytes.BytesToHex();
// assert
Assert.IsNotNull(hex);
Assert.AreEqual("affe", hex);
}
[TestMethod]
public void ShouldReturnHexStringWithDelimiter()
{
// arrange
byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
// act
string hex = bytes.BytesToHex("_");
// assert
Assert.IsNotNull(hex);
Assert.AreEqual("de_ad_be_ef", hex);
}
[TestMethod]
public void ShouldEncodeStringToHex()
{
// arrange
string plain = "Hello";
// act
string hex1 = plain.HexEncode();
string hex2 = plain.HexEncode(Encoding.Default);
// assert
Assert.AreEqual("48656c6c6f", hex1);
Assert.AreEqual("48656c6c6f", hex2);
}
[TestMethod]
public void ShouldDecodeStringFromHex()
{
// arrange
string hex = "48656c6c6f";
// act
string plain1 = hex.HexDecode();
string plain2 = hex.HexDecode(Encoding.Default);
// assert
Assert.AreEqual("Hello", plain1);
Assert.AreEqual("Hello", plain2);
}
[TestMethod]
public void ShouldEncodeStringToBase64()
{
// arrange
string plain = "Hello";
// act
string base641 = plain.Base64Encode();
string base642 = plain.Base64Encode(Encoding.Default);
// assert
Assert.AreEqual("SGVsbG8=", base641);
Assert.AreEqual("SGVsbG8=", base642);
}
[TestMethod]
public void ShouldDecodeStringFromBase64()
{
// arrange
string base64 = "SGVsbG8=";
// act
string plain1 = base64.Base64Decode();
string plain2 = base64.Base64Decode(Encoding.Default);
// assert
Assert.AreEqual("Hello", plain1);
Assert.AreEqual("Hello", plain2);
}
[TestMethod]
public void ShouldReplaceStart()
{
// arrange
string str = "Hello World!";
// act
string test1 = str.ReplaceStart("Hello", "Bye");
string test2 = str.ReplaceStart("World!", "Mars?");
// assert
Assert.AreNotEqual(str, test1);
Assert.AreEqual("Bye World!", test1);
Assert.AreEqual(str, test2);
Assert.AreNotEqual("Hello Mars?", test2);
}
[TestMethod]
public void ShouldReplaceEnd()
{
// arrange
string str = "Hello World!";
// act
string test1 = str.ReplaceEnd("Hello", "Bye");
string test2 = str.ReplaceEnd("World!", "Mars?");
// assert
Assert.AreEqual(str, test1);
Assert.AreNotEqual("Bye World!", test1);
Assert.AreNotEqual(str, test2);
Assert.AreEqual("Hello Mars?", test2);
}
[TestMethod]
public void ShouldParseDecimal()
{
// arrange
decimal number = 1234.56m;
string stringNumberEn = "1234.56";
string stringNumberDe = "1234,56";
// act
decimal numberEn = stringNumberEn.ParseDecimal();
decimal numberDe = stringNumberDe.ParseDecimal();
// assert
Assert.AreEqual(number, numberEn);
Assert.AreEqual(number, numberDe);
}
[TestMethod]
public void ShouldParseDecimalWithThousandsSeparator()
{
// arrange
decimal number = 1234.56m;
string stringNumberEn = "1,234.56";
string stringNumberDe = "1.234,56";
// act
decimal numberEn = stringNumberEn.ParseDecimal();
decimal numberDe = stringNumberDe.ParseDecimal();
// assert
Assert.AreEqual(number, numberEn);
Assert.AreEqual(number, numberDe);
}
[TestMethod]
public void ShouldValidateEmailAddressWithoutRecordCheck()
{
// arrange
string validEmailWithoutTag = "test@gmail.com";
string validEmailWithTag = "test+tag@not.exists";
string invalidEmailWithoutTag = "<Test Account> test@gmail.com";
string invalidEmailWithTag = "<Test Account> test+tag@not.exists";
string nullStr = null;
// act
bool validWithoutTag = validEmailWithoutTag.IsValidEmailAddress(checkRecordExists: false);
bool validWithTag = validEmailWithTag.IsValidEmailAddress(checkRecordExists: false);
bool invalidWithoutTag = !invalidEmailWithoutTag.IsValidEmailAddress(checkRecordExists: false);
bool invalidWithTag = !invalidEmailWithTag.IsValidEmailAddress(checkRecordExists: false);
bool nullTest = nullStr.IsValidEmailAddress(checkRecordExists: false);
// assert
Assert.IsTrue(validWithoutTag);
Assert.IsTrue(validWithTag);
Assert.IsTrue(invalidWithoutTag);
Assert.IsTrue(invalidWithTag);
Assert.IsFalse(nullTest);
}
[TestMethod]
public void ShouldValidateEmailAddressWithRecordCheck()
{
// arrange
string validEmail = "test@gmail.com";
string invalidEmail = "test@not.exists";
// act
bool valid = validEmail.IsValidEmailAddress(checkRecordExists: true);
bool invalid = !invalidEmail.IsValidEmailAddress(checkRecordExists: true);
// assert
Assert.IsTrue(valid);
Assert.IsTrue(invalid);
}
[TestMethod]
public void ShouldValidateEmailAddressWithRecordCheckDefinedNameservers()
{
// arrange
string validEmail = "test@gmail.com";
string invalidEmail = "test@not.exists";
var nameserver = new IPEndPoint(IPAddress.Parse("1.1.1.1"), 53);
// act
bool valid = validEmail.IsValidEmailAddress(new[] { nameserver });
bool invalid = !invalidEmail.IsValidEmailAddress(new[] { nameserver });
// assert
Assert.IsTrue(valid);
Assert.IsTrue(invalid);
}
[TestMethod]
public void ShouldWorkWithNullOrEmptyString()
{
// arrange
string nullStr = null;
string emptyStr = "";
// act
string hexEncodeNull = nullStr.HexEncode();
string hexEncodeEmpty = emptyStr.HexEncode();
string hexDecodeNull = nullStr.HexDecode();
string hexDecodeEmpty = emptyStr.HexDecode();
// assert
Assert.IsNull(hexEncodeNull);
Assert.AreEqual("", hexEncodeEmpty);
Assert.IsNull(hexDecodeNull);
Assert.AreEqual("", hexDecodeEmpty);
}
[TestMethod]
public void ShouldAddCustomLineTermination()
{
// arrange
string value = "abc";
var sb = new StringBuilder();
// act
sb.AppendLine(value, "\r");
sb.AppendLine(value, "\r");
// assert
Assert.AreEqual($"{value}\r{value}\r", sb.ToString());
Assert.IsFalse(sb.ToString().Contains('\n'));
}
}
}

View File

@@ -0,0 +1,635 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using UnitTests.Common.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Common.Utilities
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class CryptographyHelperTests
{
private string keyFile;
private CryptographyHelper cryptoHelper;
[TestInitialize]
public void Initialize()
{
keyFile = Path.GetTempFileName();
cryptoHelper = new CryptographyHelper(keyFile);
}
[TestCleanup]
public void Cleanup()
{
File.Delete(keyFile);
}
#region Static
#region Encryption
#region AES
[TestMethod]
public void ShouldEncryptAesWithoutSalt() // required to test the encryption itself
{
// arrange
using var _ = CryptographyHelperSaltMock.Create(0);
byte[] bytes = new byte[] { 0xaf, 0xfe };
string str = "ABC";
string password1 = "P@ssw0rd!";
string password2 = "P@ssw0rd";
byte[] expectedBytes = new byte[] { 0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc };
// act
byte[] cipherBytes1 = CryptographyHelper.AesEncrypt(bytes, password1);
string cipherStr1 = CryptographyHelper.AesEncrypt(str, password1);
byte[] cipherBytes2 = CryptographyHelper.AesEncrypt(bytes, password2);
string cipherStr2 = CryptographyHelper.AesEncrypt(str, password2);
// assert
CollectionAssert.AreEqual(expectedBytes, cipherBytes1);
Assert.AreEqual("ueLuhFNpCuYmx8v3hczHtg==", cipherStr1);
CollectionAssert.AreNotEqual(expectedBytes, cipherBytes2);
Assert.AreNotEqual("ueLuhFNpCuYmx8v3hczHtg==", cipherStr2);
}
[TestMethod]
public void ShouldDecryptAesWithoutSalt() // required to test the decryption itself
{
// arrange
using var _ = CryptographyHelperSaltMock.Create(0);
string cipherStr = "ueLuhFNpCuYmx8v3hczHtg==";
byte[] cipherBytes = new byte[] { 0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc };
string password1 = "P@ssw0rd!";
string password2 = "P@ssw0rd";
byte[] expectedBytes = new byte[] { 0xaf, 0xfe };
// act
byte[] plainBytes1 = CryptographyHelper.AesDecrypt(cipherBytes, password1);
string plainStr1 = CryptographyHelper.AesDecrypt(cipherStr, password1);
try
{
CryptographyHelper.AesDecrypt(cipherBytes, password2);
Assert.Fail();
}
catch (CryptographicException)
{ }
try
{
CryptographyHelper.AesDecrypt(cipherStr, password2);
Assert.Fail();
}
catch (CryptographicException)
{ }
// assert
CollectionAssert.AreEqual(expectedBytes, plainBytes1);
Assert.AreEqual("ABC", plainStr1);
}
[TestMethod]
public void ShouldEncryptDecryptAesBytes()
{
// arrange
byte[] plain = new byte[] { 0xaf, 0xfe };
string password = "P@ssw0rd!";
// act
byte[] cipher1 = CryptographyHelper.AesEncrypt(plain, password);
byte[] cipher2 = CryptographyHelper.AesEncrypt(plain, password);
byte[] plain1 = CryptographyHelper.AesDecrypt(cipher1, password);
byte[] plain2 = CryptographyHelper.AesDecrypt(cipher2, password);
// assert
CollectionAssert.AreNotEqual(cipher1, cipher2);
Assert.AreEqual(24, cipher1.Length);
Assert.AreEqual(24, cipher2.Length);
CollectionAssert.AreEqual(plain, plain1);
CollectionAssert.AreEqual(plain, plain2);
}
[TestMethod]
public void ShouldEncryptDecryptAesString()
{
// arrange
string plain = "ABC";
string password = "P@ssw0rd!";
// act
string cipher1 = CryptographyHelper.AesEncrypt(plain, password);
string cipher2 = CryptographyHelper.AesEncrypt(plain, password);
string plain1 = CryptographyHelper.AesDecrypt(cipher1, password);
string plain2 = CryptographyHelper.AesDecrypt(cipher2, password);
// assert
Assert.AreNotEqual(cipher1, cipher2);
Assert.AreEqual(32, cipher1.Length);
Assert.AreEqual(32, cipher2.Length);
Assert.AreEqual(plain, plain1);
Assert.AreEqual(plain, plain2);
}
#endregion AES
#region TripleDES
[TestMethod]
public void ShouldEncryptTdesWithoutSalt() // required to test the encryption itself
{
// arrange
using var _ = CryptographyHelperSaltMock.Create(0);
byte[] bytes = new byte[] { 0xaf, 0xfe };
string str = "ABC";
string password1 = "P@ssw0rd!";
string password2 = "P@ssw0rd";
byte[] expectedBytes = new byte[] { 0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7 };
// act
byte[] cipherBytes1 = CryptographyHelper.TripleDesEncrypt(bytes, password1);
string cipherStr1 = CryptographyHelper.TripleDesEncrypt(str, password1);
byte[] cipherBytes2 = CryptographyHelper.TripleDesEncrypt(bytes, password2);
string cipherStr2 = CryptographyHelper.TripleDesEncrypt(str, password2);
// assert
CollectionAssert.AreEqual(expectedBytes, cipherBytes1);
Assert.AreEqual("1l74soBuuEI=", cipherStr1);
CollectionAssert.AreNotEqual(expectedBytes, cipherBytes2);
Assert.AreNotEqual("1l74soBuuEI=", cipherStr2);
}
[TestMethod]
public void ShouldDecryptTdesWithoutSalt() // required to test the decryption itself
{
// arrange
using var _ = CryptographyHelperSaltMock.Create(0);
string cipherStr = "1l74soBuuEI=";
byte[] cipherBytes = new byte[] { 0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7 };
string password1 = "P@ssw0rd!";
string password2 = "P@ssw0rd";
byte[] expectedBytes = new byte[] { 0xaf, 0xfe };
// act
byte[] plainBytes1 = CryptographyHelper.TripleDesDecrypt(cipherBytes, password1);
string plainStr1 = CryptographyHelper.TripleDesDecrypt(cipherStr, password1);
try
{
CryptographyHelper.TripleDesDecrypt(cipherBytes, password2);
Assert.Fail();
}
catch (CryptographicException)
{ }
try
{
CryptographyHelper.TripleDesDecrypt(cipherStr, password2);
Assert.Fail();
}
catch (CryptographicException)
{ }
// assert
CollectionAssert.AreEqual(expectedBytes, plainBytes1);
Assert.AreEqual("ABC", plainStr1);
}
[TestMethod]
public void ShouldEncryptDecryptTdesBytes()
{
// arrange
byte[] plain = new byte[] { 0xaf, 0xfe };
string password = "P@ssw0rd!";
// act
byte[] cipher1 = CryptographyHelper.TripleDesEncrypt(plain, password);
byte[] cipher2 = CryptographyHelper.TripleDesEncrypt(plain, password);
byte[] plain1 = CryptographyHelper.TripleDesDecrypt(cipher1, password);
byte[] plain2 = CryptographyHelper.TripleDesDecrypt(cipher2, password);
// assert
CollectionAssert.AreNotEqual(cipher1, cipher2);
Assert.AreEqual(16, cipher1.Length);
Assert.AreEqual(16, cipher2.Length);
CollectionAssert.AreEqual(plain, plain1);
CollectionAssert.AreEqual(plain, plain2);
}
[TestMethod]
public void ShouldEncryptDecryptTdesString()
{
// arrange
string plain = "ABC";
string password = "P@ssw0rd!";
// act
string cipher1 = CryptographyHelper.TripleDesEncrypt(plain, password);
string cipher2 = CryptographyHelper.TripleDesEncrypt(plain, password);
string plain1 = CryptographyHelper.TripleDesDecrypt(cipher1, password);
string plain2 = CryptographyHelper.TripleDesDecrypt(cipher2, password);
// assert
Assert.AreNotEqual(cipher1, cipher2);
Assert.AreEqual(24, cipher1.Length);
Assert.AreEqual(24, cipher2.Length);
Assert.AreEqual(plain, plain1);
Assert.AreEqual(plain, plain2);
}
#endregion TripleDES
#endregion Encryption
#region Hash
[TestMethod]
public void ShouldReturnMd5Hash()
{
// arrange
string text = "Hello World!";
byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
string fileName = Path.GetTempFileName();
// act
string textHash = CryptographyHelper.Md5(text);
string bytesHash = CryptographyHelper.Md5(bytes);
File.WriteAllText(fileName, text);
string fileHash = CryptographyHelper.Md5File(fileName);
File.Delete(fileName);
// assert
Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", textHash);
Assert.AreEqual("2f249230a8e7c2bf6005ccd2679259ec", bytesHash);
Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", fileHash);
}
[TestMethod]
public void ShouldReturnSha1Hash()
{
// arrange
string text = "Hello World!";
byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
string fileName = Path.GetTempFileName();
// act
string textHash = CryptographyHelper.Sha1(text);
string bytesHash = CryptographyHelper.Sha1(bytes);
File.WriteAllText(fileName, text);
string fileHash = CryptographyHelper.Sha1File(fileName);
File.Delete(fileName);
// assert
Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", textHash);
Assert.AreEqual("d78f8bb992a56a597f6c7a1fb918bb78271367eb", bytesHash);
Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", fileHash);
}
[TestMethod]
public void ShouldReturnSha256Hash()
{
// arrange
string text = "Hello World!";
byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
string fileName = Path.GetTempFileName();
// act
string textHash = CryptographyHelper.Sha256(text);
string bytesHash = CryptographyHelper.Sha256(bytes);
File.WriteAllText(fileName, text);
string fileHash = CryptographyHelper.Sha256File(fileName);
File.Delete(fileName);
// assert
Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", textHash);
Assert.AreEqual("5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953", bytesHash);
Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", fileHash);
}
[TestMethod]
public void ShouldReturnSha512Hash()
{
// arrange
string text = "Hello World!";
byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
string fileName = Path.GetTempFileName();
// act
string textHash = CryptographyHelper.Sha512(text);
string bytesHash = CryptographyHelper.Sha512(bytes);
File.WriteAllText(fileName, text);
string fileHash = CryptographyHelper.Sha512File(fileName);
File.Delete(fileName);
// assert
Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", textHash);
Assert.AreEqual("1284b2d521535196f22175d5f558104220a6ad7680e78b49fa6f20e57ea7b185d71ec1edb137e70eba528dedb141f5d2f8bb53149d262932b27cf41fed96aa7f", bytesHash);
Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", fileHash);
}
#endregion Hash
#region Random
[TestMethod]
public void ShouldReturnRandomBytes()
{
// arrange
int length1 = 12;
int length2 = 12;
int length3 = 42;
// act
byte[] bytes1 = CryptographyHelper.GetRandomBytes(length1);
byte[] bytes2 = CryptographyHelper.GetRandomBytes(length2);
byte[] bytes3 = CryptographyHelper.GetRandomBytes(length3);
// assert
Assert.AreEqual(length1, bytes1.Length);
Assert.AreEqual(length2, bytes2.Length);
Assert.AreEqual(length3, bytes3.Length);
Assert.IsTrue(bytes1.Length == bytes2.Length);
CollectionAssert.AreNotEqual(bytes1, bytes2);
}
[TestMethod]
public void ShouldReturnRandomString()
{
// arrange
int length1 = 12;
int length2 = 12;
int length3 = 42;
// act
string str1 = CryptographyHelper.GetRandomString(length1);
string str2 = CryptographyHelper.GetRandomString(length2);
string str3 = CryptographyHelper.GetRandomString(length3);
// assert
Assert.AreEqual(length1, str1.Length);
Assert.AreEqual(length2, str2.Length);
Assert.AreEqual(length3, str3.Length);
Assert.IsTrue(str1.Length == str2.Length);
Assert.IsFalse(str1 == str2);
}
[TestMethod]
public void ShouldReturnRandomStringWithPool()
{
// arrange
int length = 12;
string pool = "0123456789abcdef";
// act
string str1 = CryptographyHelper.GetRandomString(length, pool);
string str2 = CryptographyHelper.GetRandomString(length, pool);
// assert
Assert.AreEqual(length, str1.Length);
Assert.AreEqual(length, str2.Length);
Assert.IsFalse(str1 == str2);
Assert.IsFalse(Regex.IsMatch(str1, "[^0-9a-f]"));
Assert.IsFalse(Regex.IsMatch(str2, "[^0-9a-f]"));
}
#endregion Random
#region Secure probing
[TestMethod]
public void ShouldTakeSameTimeToCompareString()
{
// arrange
string str1 = "Hello World!";
string str2 = "Hello World!";
string str3 = "Hallo World!";
string str4 = "Hello World?";
string nullStr = null;
string strLen = "Hello World";
var sw = new Stopwatch();
// act
bool nullCompare = CryptographyHelper.SecureEquals(nullStr, str1);
bool lenCompare = CryptographyHelper.SecureEquals(strLen, str1);
sw.Start();
bool compare1 = CryptographyHelper.SecureEquals(str1, str2);
long time1 = sw.ElapsedMilliseconds;
bool compare2 = CryptographyHelper.SecureEquals(str1, str3);
long time2 = sw.ElapsedMilliseconds;
bool compare3 = CryptographyHelper.SecureEquals(str1, str4);
long time3 = sw.ElapsedMilliseconds;
sw.Stop();
// assert
Assert.IsFalse(nullCompare);
Assert.IsFalse(lenCompare);
Assert.IsTrue(compare1);
Assert.IsFalse(compare2);
Assert.IsFalse(compare3);
Assert.AreEqual(time1, time2);
Assert.AreEqual(time1, time3);
Assert.AreEqual(time2, time3);
}
[TestMethod]
public void ShouldTakeSameTimeToCompareBytes()
{
// arrange
byte[] bytes1 = CryptographyHelper.GetRandomBytes(200);
byte[] bytes2 = new byte[bytes1.Length];
byte[] bytes3 = new byte[bytes1.Length];
byte[] bytes4 = new byte[bytes1.Length];
byte[] nullBytes = null;
byte[] lenBytes = new byte[bytes1.Length + 1];
Array.Copy(bytes1, bytes2, bytes1.Length);
Array.Copy(bytes1, bytes3, bytes1.Length);
Array.Copy(bytes1, bytes4, bytes1.Length);
bytes3[10] = (byte)(bytes1[10] + 1);
bytes4[190] = (byte)(bytes1[190] + 1);
var sw = new Stopwatch();
// act
bool nullCompare = CryptographyHelper.SecureEquals(nullBytes, bytes1);
bool lenCompare = CryptographyHelper.SecureEquals(lenBytes, bytes1);
sw.Start();
bool compare1 = CryptographyHelper.SecureEquals(bytes1, bytes2);
long time1 = sw.ElapsedMilliseconds;
bool compare2 = CryptographyHelper.SecureEquals(bytes1, bytes3);
long time2 = sw.ElapsedMilliseconds;
bool compare3 = CryptographyHelper.SecureEquals(bytes1, bytes4);
long time3 = sw.ElapsedMilliseconds;
sw.Stop();
// assert
Assert.IsFalse(nullCompare);
Assert.IsFalse(lenCompare);
Assert.IsTrue(compare1);
Assert.IsFalse(compare2);
Assert.IsFalse(compare3);
Assert.AreEqual(time1, time2);
Assert.AreEqual(time1, time3);
Assert.AreEqual(time2, time3);
}
#endregion Secure probing
#endregion Static
#region Instance
[TestMethod]
public void ShouldCreateDefaultFile()
{
// arrange
string executingAssemblyDir = AppContext.BaseDirectory;
string filePath = Path.Combine(executingAssemblyDir, "crypto.key");
// act
bool fileExistsBefore = File.Exists(filePath);
var helper = new CryptographyHelper();
bool fileExistsAfter = File.Exists(filePath);
string content = fileExistsAfter ? File.ReadAllText(filePath) : null;
File.Delete(filePath);
// assert
Assert.IsFalse(fileExistsBefore);
Assert.IsNotNull(helper);
Assert.IsTrue(fileExistsAfter);
Assert.IsTrue(!string.IsNullOrWhiteSpace(content));
}
[TestMethod]
public void ShouldEncryptAesUsingKeyFile()
{
// arrange
string str = "Hello World!";
byte[] bytes = CryptographyHelper.GetRandomBytes(32);
string password = File.ReadAllText(keyFile);
// act
string cipherStr = cryptoHelper.EncryptAes(str);
byte[] cipherBytes = cryptoHelper.EncryptAes(bytes);
string plainStr = CryptographyHelper.AesDecrypt(cipherStr, password);
byte[] plainBytes = CryptographyHelper.AesDecrypt(cipherBytes, password);
// assert
Assert.AreNotEqual(str, cipherStr);
Assert.AreEqual(str, plainStr);
CollectionAssert.AreNotEqual(bytes, cipherBytes);
CollectionAssert.AreEqual(bytes, plainBytes);
}
[TestMethod]
public void ShouldDecryptAesUsingKeyFile()
{
// arrange
string str = "Hello World!";
byte[] bytes = CryptographyHelper.GetRandomBytes(32);
string password = File.ReadAllText(keyFile);
// act
string cipherStr = CryptographyHelper.AesEncrypt(str, password);
byte[] cipherBytes = CryptographyHelper.AesEncrypt(bytes, password);
string plainStr = cryptoHelper.DecryptAes(cipherStr);
byte[] plainBytes = cryptoHelper.DecryptAes(cipherBytes);
// assert
Assert.AreNotEqual(str, cipherStr);
Assert.AreEqual(str, plainStr);
CollectionAssert.AreNotEqual(bytes, cipherBytes);
CollectionAssert.AreEqual(bytes, plainBytes);
}
[TestMethod]
public void ShouldEncryptTdesUsingKeyFile()
{
// arrange
string str = "Hello World!";
byte[] bytes = CryptographyHelper.GetRandomBytes(32);
string password = File.ReadAllText(keyFile);
// act
string cipherStr = cryptoHelper.EncryptTripleDes(str);
byte[] cipherBytes = cryptoHelper.EncryptTripleDes(bytes);
string plainStr = CryptographyHelper.TripleDesDecrypt(cipherStr, password);
byte[] plainBytes = CryptographyHelper.TripleDesDecrypt(cipherBytes, password);
// assert
Assert.AreNotEqual(str, cipherStr);
Assert.AreEqual(str, plainStr);
CollectionAssert.AreNotEqual(bytes, cipherBytes);
CollectionAssert.AreEqual(bytes, plainBytes);
}
[TestMethod]
public void ShouldDecryptTdesUsingKeyFile()
{
// arrange
string str = "Hello World!";
byte[] bytes = CryptographyHelper.GetRandomBytes(32);
string password = File.ReadAllText(keyFile);
// act
string cipherStr = CryptographyHelper.TripleDesEncrypt(str, password);
byte[] cipherBytes = CryptographyHelper.TripleDesEncrypt(bytes, password);
string plainStr = cryptoHelper.DecryptTripleDes(cipherStr);
byte[] plainBytes = cryptoHelper.DecryptTripleDes(cipherBytes);
// assert
Assert.AreNotEqual(str, cipherStr);
Assert.AreEqual(str, plainStr);
CollectionAssert.AreNotEqual(bytes, cipherBytes);
CollectionAssert.AreEqual(bytes, plainBytes);
}
#endregion Instance
}
}

View File

@@ -0,0 +1,288 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using AMWD.Common.Utilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Common.Utilities
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class DelayedTaskTests
{
[TestMethod]
public void ShouldCreateNewDelayedTaskNotStarting()
{
// arrange
int executionCount = 0;
var delay = TimeSpan.FromMilliseconds(100);
var action = () => { executionCount++; };
// act
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
var delayedTask = DelayedTask.Create(action, delay);
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
// assert
Assert.IsNotNull(delayedTask);
Assert.AreEqual(delay, delayedTask.Delay);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNull(delayedTask.Exception);
Assert.AreEqual(0, executionCount);
}
[TestMethod]
public void ShouldCreateNewDelayedTaskStarting()
{
// arrange
int executionCount = 0;
var delay = TimeSpan.FromMilliseconds(100);
var action = () => { executionCount++; };
// act
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
var delayedTask = DelayedTask.Run(action, delay);
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
// assert
Assert.IsNotNull(delayedTask);
Assert.AreEqual(delay, delayedTask.Delay);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNull(delayedTask.Exception);
Assert.AreEqual(1, executionCount);
}
[TestMethod]
public void ShouldRunOnceAfterReset()
{
// arrange
int executionCount = 0;
var delay = TimeSpan.FromMilliseconds(100);
var action = () => { executionCount++; };
// act
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
var delayedTask = DelayedTask.Create(action, delay);
delayedTask.Reset();
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
// assert
Assert.IsNotNull(delayedTask);
Assert.AreEqual(delay, delayedTask.Delay);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNull(delayedTask.Exception);
Assert.AreEqual(1, executionCount);
}
[TestMethod]
public void ShouldCancelWaitingTask()
{
// arrange
int executionCount = 0;
var delay = TimeSpan.FromMilliseconds(100);
var action = () => { executionCount++; };
// act
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
var delayedTask = DelayedTask.Run(action, delay);
delayedTask.Cancel();
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
// assert
Assert.IsNotNull(delayedTask);
Assert.AreEqual(delay, delayedTask.Delay);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNull(delayedTask.Exception);
Assert.AreEqual(0, executionCount);
}
[TestMethod]
public async Task ShouldResetRunningDelay()
{
// arrange
int executionCount = 0;
var sw = new Stopwatch();
var delay = TimeSpan.FromMilliseconds(200);
var action = () => { sw.Stop(); executionCount++; };
// act
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
sw.Start();
var delayedTask = DelayedTask.Run(action, delay);
await Task.Delay(50);
delayedTask.Reset();
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
// assert
Assert.IsNotNull(delayedTask);
Assert.AreEqual(delay, delayedTask.Delay);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNull(delayedTask.Exception);
Assert.AreEqual(1, executionCount);
// delta of 60ms as the precision below 50ms is really bad for System.Timer
Assert.AreEqual(250, sw.ElapsedMilliseconds, 60);
}
[TestMethod]
public async Task ShouldNotExecutedImmediateOnCreated()
{
// arrange
int executionCount = 0;
var sw = new Stopwatch();
var delay = TimeSpan.FromMilliseconds(200);
var action = () => { sw.Stop(); executionCount++; };
// act
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
sw.Start();
var delayedTask = DelayedTask.Create(action, delay);
await Task.Delay(50);
bool isSuccess = delayedTask.ExecutePending();
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
sw.Stop();
// assert
Assert.IsNotNull(delayedTask);
Assert.AreEqual(delay, delayedTask.Delay);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNull(delayedTask.Exception);
Assert.AreEqual(0, executionCount);
// delta of 60ms as the precision below 50ms is really bad for System.Timer
Assert.AreEqual(1250, sw.ElapsedMilliseconds, 60);
Assert.IsFalse(isSuccess);
}
[TestMethod]
public async Task ShouldExecuteImmediateOnExecutePendingWhenRunning()
{
// arrange
int executionCount = 0;
var sw = new Stopwatch();
var delay = TimeSpan.FromMilliseconds(200);
var action = () => { sw.Stop(); executionCount++; };
// act
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
sw.Start();
var delayedTask = DelayedTask.Run(action, delay);
await Task.Delay(50);
bool isSuccess = delayedTask.ExecutePending();
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
// assert
Assert.IsNotNull(delayedTask);
Assert.AreEqual(delay, delayedTask.Delay);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNull(delayedTask.Exception);
Assert.AreEqual(1, executionCount);
// delta of 60ms as the precision below 50ms is really bad for System.Timer
Assert.AreEqual(50, sw.ElapsedMilliseconds, 60);
Assert.IsTrue(isSuccess);
}
[TestMethod]
public void ShouldReturnAnAwaiter()
{
// arrange
int executionCount = 0;
var delay = TimeSpan.FromMilliseconds(100);
var action = () => { executionCount++; };
var delayedTask = DelayedTask.Create(action, delay);
// act
delayedTask.Reset();
var awaiter = delayedTask.GetAwaiter();
SpinWait.SpinUntil(() => awaiter.IsCompleted);
// assert
Assert.IsNotNull(delayedTask);
Assert.IsNotNull(awaiter);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNull(delayedTask.Exception);
Assert.AreEqual(1, executionCount);
}
[TestMethod]
public void ShouldHaveAnExceptionSet()
{
// arrange
var delay = TimeSpan.FromMilliseconds(100);
var action = () => { throw new Exception("TEST :D"); };
// act
var delayedTask = DelayedTask.Run(action, delay);
var awaiter = delayedTask.GetAwaiter();
SpinWait.SpinUntil(() => awaiter.IsCompleted);
// assert
Assert.IsNotNull(delayedTask);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNotNull(delayedTask.Exception);
}
[TestMethod]
public void ShouldUseExceptionHandler()
{
// arrange
string exceptionText = null;
var delay = TimeSpan.FromMilliseconds(100);
var action = () => { throw new Exception("TEST :D"); };
var exceptionHandler = (Exception ex) =>
{
exceptionText = ex.Message;
};
// act
var delayedTask = DelayedTask.Run(action, delay)
.WithExceptionHandler(exceptionHandler);
var awaiter = delayedTask.GetAwaiter();
SpinWait.SpinUntil(() => awaiter.IsCompleted);
// assert
Assert.IsNotNull(delayedTask);
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.IsNotNull(delayedTask.Exception);
Assert.AreEqual("TEST :D", exceptionText);
}
[TestMethod]
public async Task ShouldReturnNormalTask()
{
// arrange
int executionCount = 0;
var delay = TimeSpan.FromMilliseconds(100);
var action = () => { executionCount++; };
var delayedTask = DelayedTask.Create(action, delay);
// act
delayedTask.Reset();
var task = delayedTask.Task;
await task;
// assert
Assert.IsNotNull(delayedTask);
Assert.IsNotNull(task);
Assert.IsInstanceOfType(task, typeof(Task));
Assert.IsFalse(delayedTask.IsRunning);
Assert.IsFalse(delayedTask.IsWaitingToRun);
Assert.AreEqual(1, executionCount);
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
}
}
}

View File

@@ -0,0 +1,134 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using AMWD.Common.Utilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Common.Utilities
{
[TestClass]
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class DelayedTaskWithResultTests
{
[TestMethod]
public void ShouldCreateNewDelayedTaskNotStarting()
{
// arrange
int executionCount = 0;
var delay = TimeSpan.FromMilliseconds(100);
var function = () => { executionCount++; return new[] { 42, 21 }; };
// act
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
var delayedTaskWithResult = DelayedTask.Create(function, delay);
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
// assert
Assert.IsNotNull(delayedTaskWithResult);
Assert.AreEqual(delay, delayedTaskWithResult.Delay);
Assert.IsFalse(delayedTaskWithResult.IsRunning);
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
Assert.IsNull(delayedTaskWithResult.Exception);
Assert.AreEqual(0, executionCount);
}
[TestMethod]
public async Task ShouldCreateNewDelayedTaskStarting()
{
// arrange
int executionCount = 0;
var delay = TimeSpan.FromMilliseconds(100);
var function = () => { executionCount++; return new[] { 42, 21 }; };
// act
var delayedTaskWithResult = DelayedTask.Run(function, delay);
int[] result = await delayedTaskWithResult;
// assert
Assert.IsNotNull(delayedTaskWithResult);
Assert.AreEqual(delay, delayedTaskWithResult.Delay);
Assert.IsFalse(delayedTaskWithResult.IsRunning);
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
Assert.IsNull(delayedTaskWithResult.Exception);
Assert.AreEqual(1, executionCount);
CollectionAssert.AreEqual(new[] { 42, 21 }, result);
}
[TestMethod]
public void ShouldHaveAnExceptionSet()
{
// arrange
var delay = TimeSpan.FromMilliseconds(100);
#pragma warning disable CS0162 // Unreachable Code detected.
var function = () => { throw new Exception("TEST :D"); return new[] { 42, 21 }; };
#pragma warning restore CS0162 // Unreachable Code detected
// act
var delayedTaskWithResult = DelayedTask.Run(function, delay);
var awaiter = delayedTaskWithResult.GetAwaiter();
SpinWait.SpinUntil(() => awaiter.IsCompleted);
// assert
Assert.IsNotNull(delayedTaskWithResult);
Assert.IsFalse(delayedTaskWithResult.IsRunning);
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
Assert.IsNotNull(delayedTaskWithResult.Exception);
}
[TestMethod]
public void ShouldUseExceptionHandler()
{
// arrange
string exceptionText = null;
var delay = TimeSpan.FromMilliseconds(100);
#pragma warning disable CS0162 // Unreachable Code detected.
var function = () => { throw new Exception("TEST :D"); return new[] { 42, 21 }; };
#pragma warning restore CS0162 // Unreachable Code detected
var exceptionHandler = (Exception ex) =>
{
exceptionText = ex.Message;
};
// act
var delayedTaskWithResult = DelayedTask.Run(function, delay)
.WithExceptionHandler(exceptionHandler);
var awaiter = delayedTaskWithResult.GetAwaiter();
SpinWait.SpinUntil(() => awaiter.IsCompleted);
// assert
Assert.IsNotNull(delayedTaskWithResult);
Assert.IsFalse(delayedTaskWithResult.IsRunning);
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
Assert.IsNotNull(delayedTaskWithResult.Exception);
Assert.AreEqual("TEST :D", exceptionText);
}
[TestMethod]
public async Task ShouldReturnNormalTask()
{
// arrange
int executionCount = 0;
var delay = TimeSpan.FromMilliseconds(100);
var function = () => { executionCount++; return new[] { 42, 21 }; };
var delayedTaskWithResult = DelayedTask.Create(function, delay);
// act
delayedTaskWithResult.Reset();
var task = delayedTaskWithResult.Task;
int[] result = await task;
// assert
Assert.IsNotNull(delayedTaskWithResult);
Assert.IsNotNull(task);
Assert.IsInstanceOfType(task, typeof(Task));
Assert.IsFalse(delayedTaskWithResult.IsRunning);
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
Assert.AreEqual(1, executionCount);
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
CollectionAssert.AreEqual(new int[] { 42, 21 }, result);
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Security.Cryptography;
using ReflectionMagic;
namespace UnitTests.Common.Utils
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class CryptographyHelperSaltMock : IDisposable
{
private readonly int saltLength;
private CryptographyHelperSaltMock(int saltLength)
{
this.saltLength = typeof(CryptographyHelper).AsDynamicType().saltLength;
SetSaltLength(saltLength);
}
public static IDisposable Create(int saltLength)
=> new CryptographyHelperSaltMock(saltLength);
public void Dispose()
=> SetSaltLength(saltLength);
private static void SetSaltLength(int length)
=> typeof(CryptographyHelper).AsDynamicType().saltLength = length;
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace UnitTests.Common.Utils
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
internal class CustomMultipleAttribute : Attribute
{
public CustomMultipleAttribute(string name)
{
Name = name;
}
public string Name { get; set; }
}
}

View File

@@ -0,0 +1,50 @@
using System;
namespace UnitTests.Common.Utils
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class JsonTestClass
{
public string StringValue { get; set; } = "Hello World!";
public bool IsBoolTrue { get; set; } = true;
public float FloatValue { get; set; } = 12.34f;
public double DoubleValue { get; set; } = 21.42;
public decimal DecimalValue { get; set; } = 42.21m;
public DateTime LocalTimestamp { get; set; } = new(2021, 11, 16, 20, 15, 34, DateTimeKind.Local);
public DateTime UtcTimestamp { get; set; } = new(2021, 11, 16, 20, 15, 34, DateTimeKind.Utc);
public JsonTestSubClass Object { get; set; } = new();
}
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class JsonTestSubClass
{
public int IntegerValue { get; set; } = 42;
public string StringValue { get; set; } = "Foo-Bar";
}
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class JsonErrorClass
{
private int? number;
public int Number
{
get
{
if (number.HasValue)
return number.Value;
throw new Exception("Null value");
}
set { number = value; }
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using ReflectionMagic;
namespace UnitTests.Common.Utils
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class TimeZoneInfoLocalMock : IDisposable
{
private readonly TimeZoneInfo localTimeZoneInfo;
private TimeZoneInfoLocalMock(TimeZoneInfo timeZoneInfo)
{
localTimeZoneInfo = TimeZoneInfo.Local;
SetLocalTimeZone(timeZoneInfo);
}
public static IDisposable Create(TimeZoneInfo mockTimeZoneInfo)
=> new TimeZoneInfoLocalMock(mockTimeZoneInfo);
public void Dispose()
=> SetLocalTimeZone(localTimeZoneInfo);
private static void SetLocalTimeZone(TimeZoneInfo timeZoneInfo)
=> typeof(TimeZoneInfo).AsDynamicType().s_cachedData._localTimeZone = timeZoneInfo;
}
}

View File

@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<CollectCoverage>true</CollectCoverage>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<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.TestFramework" Version="2.2.10" />
<PackageReference Include="ReflectionMagic" Version="4.1.0" />
</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" />
</ItemGroup>
</Project>