From c91a4f477682ed3332e3d2e66f1c6efdbb05a21e Mon Sep 17 00:00:00 2001 From: Maksym Sadovnychyy Date: Sat, 6 Aug 2022 23:16:25 +0200 Subject: [PATCH] (feat): adding common extension methods and tests --- .../Converters/EnumerationSerializer.cs | 2 +- webapi/Extensions/Extensions.csproj | 14 + webapi/Extensions/HttpRequestExtensions.cs | 45 +++ webapi/Extensions/IEquatableExtensions.cs | 66 ++++ webapi/Extensions/ObjectExtensions.cs | 20 + webapi/Extensions/StringExtensions.cs | 345 ++++++++++++++++++ .../Core/Abstractions/ConfigurationBase.cs | 61 ++++ .../Attributes/DefaultPriorityAttribute.cs | 23 ++ .../Core/Attributes/PriorityAttribute.cs | 27 ++ webapi/Tests/Core/CoreTests.csproj | 26 ++ .../Core/FakeServices/MessageFakeService.cs | 19 + .../FakeServices/ObjectStorageFakeService.cs | 70 ++++ .../Core/FakeServices/SessionFakeService.cs | 49 +++ .../Core/FakeServices/VaultFakeService.cs | 48 +++ webapi/Tests/Core/PriorityOrderer.cs | 97 +++++ .../Extensions/Abstractions/ServiceBase.cs | 17 + webapi/Tests/Extensions/Configuration.cs | 3 + .../Tests/Extensions/ExtensionsTests.csproj | 37 ++ .../Extensions/IEquatableExtensionsTests.cs | 121 ++++++ .../Tests/Extensions/ObjectExtensionsTests.cs | 22 ++ .../Tests/Extensions/StringExtensionsTests.cs | 256 +++++++++++++ webapi/Tests/Extensions/appsettings.json | 5 + webapi/WeatherForecast.sln | 22 ++ 23 files changed, 1394 insertions(+), 1 deletion(-) create mode 100644 webapi/Extensions/Extensions.csproj create mode 100644 webapi/Extensions/HttpRequestExtensions.cs create mode 100644 webapi/Extensions/IEquatableExtensions.cs create mode 100644 webapi/Extensions/ObjectExtensions.cs create mode 100644 webapi/Extensions/StringExtensions.cs create mode 100644 webapi/Tests/Core/Abstractions/ConfigurationBase.cs create mode 100644 webapi/Tests/Core/Attributes/DefaultPriorityAttribute.cs create mode 100644 webapi/Tests/Core/Attributes/PriorityAttribute.cs create mode 100644 webapi/Tests/Core/CoreTests.csproj create mode 100644 webapi/Tests/Core/FakeServices/MessageFakeService.cs create mode 100644 webapi/Tests/Core/FakeServices/ObjectStorageFakeService.cs create mode 100644 webapi/Tests/Core/FakeServices/SessionFakeService.cs create mode 100644 webapi/Tests/Core/FakeServices/VaultFakeService.cs create mode 100644 webapi/Tests/Core/PriorityOrderer.cs create mode 100644 webapi/Tests/Extensions/Abstractions/ServiceBase.cs create mode 100644 webapi/Tests/Extensions/Configuration.cs create mode 100644 webapi/Tests/Extensions/ExtensionsTests.csproj create mode 100644 webapi/Tests/Extensions/IEquatableExtensionsTests.cs create mode 100644 webapi/Tests/Extensions/ObjectExtensionsTests.cs create mode 100644 webapi/Tests/Extensions/StringExtensionsTests.cs create mode 100644 webapi/Tests/Extensions/appsettings.json diff --git a/webapi/DataProviders/Converters/EnumerationSerializer.cs b/webapi/DataProviders/Converters/EnumerationSerializer.cs index 9726cba..dd37731 100644 --- a/webapi/DataProviders/Converters/EnumerationSerializer.cs +++ b/webapi/DataProviders/Converters/EnumerationSerializer.cs @@ -3,7 +3,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using Core.Abstractions; -namespace PecMgr.DataProviders.Converters { +namespace DataProviders.Converters { public class EnumerationSerializer : IBsonSerializer where T : Enumeration { private T Deserialize(IBsonReader reader) => Enumeration.FromValue(BsonSerializer.Deserialize(reader)); diff --git a/webapi/Extensions/Extensions.csproj b/webapi/Extensions/Extensions.csproj new file mode 100644 index 0000000..e6bfcab --- /dev/null +++ b/webapi/Extensions/Extensions.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + $(MSBuildProjectName.Replace(" ", "_")) + + + + + + + diff --git a/webapi/Extensions/HttpRequestExtensions.cs b/webapi/Extensions/HttpRequestExtensions.cs new file mode 100644 index 0000000..43d8d26 --- /dev/null +++ b/webapi/Extensions/HttpRequestExtensions.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Extensions { + public static class HttpRequestExtensions { + + + /// + /// + /// + /// + /// + /// + public static List GetHeader(this HttpRequest request, string name) { + var headers = request.Headers[name].ToList(); + return headers != null ? headers : new List(); + } + + + + /// + /// Return clean JWT Bearer token from Authorisation Header + /// + public static string? GeBearerToken(this HttpRequest request) { + var header = request.GetHeader("Authorization").FirstOrDefault(); + return header !=null + ? header.Replace("Bearer ", "") + : default; + } + + /// + /// Returns JWT Bearer token Vault path from custom AuthorizationPath Header + /// + /// + /// + public static string? GetBearerPath(this HttpRequest request) { + var header = request.GetHeader("AuthorizationPath").FirstOrDefault(); + return header; + } + } +} diff --git a/webapi/Extensions/IEquatableExtensions.cs b/webapi/Extensions/IEquatableExtensions.cs new file mode 100644 index 0000000..2a79c0d --- /dev/null +++ b/webapi/Extensions/IEquatableExtensions.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExtensionMethods { + public static class IEquatableExtensions { + /// + /// + /// + /// + /// + /// + /// + public static bool Eq(this IEquatable? item1, T? item2) { + if ((item1 == null && item2 != null) || (item1 != null && item2 == null)) + return false; + + if (item1 == null && item2 == null) + return true; + + if (item1 != null && item2 != null) { + var result = item1.Equals(item2); + + return result; + } + + return false; + } + + /// + /// + /// + /// + /// + /// + /// + public static bool EnumerableEq(this IEnumerable>? list1, IEnumerable>? list2) { + if ((list1 == null && list2 != null) || (list1 != null && list2 == null)) + return false; + + if (list1 == null && list2 == null) + return true; + + if (list1 != null && list2 != null && list1.Count() == list2.Count()) { + + var diffDic = list2.GroupBy(x => x.GetHashCode()).ToDictionary(g => g.Key, g => g.Count()); + + for (int i = 0; i < list1.Count(); i++) { + var obj = (T)list1.ElementAt(i); + var objHash = obj.GetHashCode(); + + if (diffDic.ContainsKey(objHash)) + diffDic[objHash] = diffDic[objHash] - 1; + else + diffDic.Add(objHash, -1); + } + + return !diffDic.Any(x => x.Value != 0); + } + + return false; + } + } +} diff --git a/webapi/Extensions/ObjectExtensions.cs b/webapi/Extensions/ObjectExtensions.cs new file mode 100644 index 0000000..0ef0c34 --- /dev/null +++ b/webapi/Extensions/ObjectExtensions.cs @@ -0,0 +1,20 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ExtensionMethods { + public static class ObjectExtensions { + + /// + /// Converts object to json string + /// + /// + /// + /// + public static string ToJson(this T obj) { + return JsonSerializer.Serialize(obj, new JsonSerializerOptions { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }); + } + } +} \ No newline at end of file diff --git a/webapi/Extensions/StringExtensions.cs b/webapi/Extensions/StringExtensions.cs new file mode 100644 index 0000000..133cd77 --- /dev/null +++ b/webapi/Extensions/StringExtensions.cs @@ -0,0 +1,345 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Globalization; +using System.Security.Cryptography; +using System.ComponentModel.DataAnnotations; +using System.Text.Json; + +namespace ExtensionMethods { + public static class StringExtensions { + + /// + /// SQL Like implementation + /// + /// + /// + /// + public static bool Like(this string text, string wildcardedText) { + text = text ?? ""; + wildcardedText = wildcardedText ?? ""; + + return Regex.IsMatch(text, wildcardedText.WildcardToRegular(), RegexOptions.IgnoreCase | RegexOptions.Multiline); + } + + /// + /// Converts wildcarded string tgo regex + /// + /// + /// + private static string WildcardToRegular(this string value) => + $"^{Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*")}$"; + + /// + /// VB.Net Left implementation + /// + /// + /// + /// + public static string Left(this string s, int count) { + return s.Substring(0, count); + } + + /// + /// VB.Net Right implementation + /// + /// + /// + /// + public static string Right(this string s, int count) { + return s.Substring(s.Length - count, count); + } + + /// + /// VB.Net Mid implementation + /// + /// + /// + /// + /// + public static string Mid(this string s, int index, int count) { + return s.Substring(index, count); + } + + /// + /// VB.Net ToInteger implementation + /// + /// + /// + public static int ToInteger(this string s) { + var integerValue = 0; + int.TryParse(s, out integerValue); + return integerValue; + } + + /// + /// VB.Net IsInteger implementations + /// + /// + /// + public static bool IsInteger(this string s) => new Regex("^-[0-9]+$|^[0-9]+$").Match(s).Success; + + public static StringBuilder Prepend(this StringBuilder sb, string content) => sb.Insert(0, content); + + public static T ToEnum(this string input) where T : struct { + var result = default(T); + + if (string.IsNullOrWhiteSpace(input)) + return result; + + bool parseSucceeded = Enum.TryParse(input, true, out result); + + if (parseSucceeded) + return result; + + // se fallisce il parse per valore proviamo ad usare il DisplayAttribute.Name + var enumType = typeof(T); + + foreach (T enumItem in Enum.GetValues(enumType)) { + var att = enumType.GetMember(enumItem.ToString())[0] + .GetCustomAttributes(typeof(DisplayAttribute), false).SingleOrDefault(); + + var displayName = ((DisplayAttribute)att).GetName(); + + if (input.Equals(displayName, StringComparison.InvariantCultureIgnoreCase)) + return enumItem; + } + throw new NotSupportedException($"Cannot parse the value '{input}' for {enumType}"); + } + + public static T? ToNullableEnum(this string input) where T : struct => !string.IsNullOrWhiteSpace(input) ? input.ToEnum() : default(T?); + + public static string ToNull(this string s) => !string.IsNullOrWhiteSpace(s) ? s : null; + + public static string NullIfEmptyString(this string s) => s.ToNull(); + + public static long ToLong(this string s) { + if (!long.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out long result)) + result = !string.IsNullOrWhiteSpace(s) ? s.ToUpper().GetHashCode() : default; + return result; + } + + public static long? ToNullableLong(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToLong() : default(long?); + + public static int ToInt(this string s) { + if (!int.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out int result)) + result = !string.IsNullOrWhiteSpace(s) ? s.ToUpper().GetHashCode() : default; + return result; + } + + public static int? ToNullableInt(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToInt() : default(int?); + + public static uint ToUint(this string s) { + if (!uint.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out uint result)) + result = !string.IsNullOrWhiteSpace(s) ? (uint)s.ToUpper().GetHashCode() : default; + return result; + } + + public static uint? ToNullableUint(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToUint() : default(uint?); + + public static decimal ToDecimal(this string s) { + if (!int.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out int result)) + result = !string.IsNullOrWhiteSpace(s) ? s.ToUpper().GetHashCode() : default; + return result; + } + + public static decimal? ToNullableDecimal(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToDecimal() : default(decimal?); + + public static double ToDouble(this string s) { + if (!double.TryParse(s, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out double result)) + result = !string.IsNullOrWhiteSpace(s) ? s.ToUpper().GetHashCode() : default; + return result; + } + + public static double? ToNullableDouble(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToDouble() : default(double?); + + #region DateTime + public static DateTime ToDate(this string s, string[] formats) { + return DateTime.TryParseExact(s, formats, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime datetime) + ? DateTime.SpecifyKind(datetime, DateTimeKind.Utc) + : throw new ApplicationException($"the date [{s}] is not in required format: [{formats[0]}]"); + } + + public static DateTime ToDate(this string s) => s.ToDate(new string[] { "dd/MM/yyyy" }); + + public static DateTime? ToNullableDate(this string s) => !string.IsNullOrEmpty(s) ? s.ToDate() : default(DateTime?); + + public static DateTime? ToNullableDate(this string s, string[] formats) => !string.IsNullOrEmpty(s) ? s.ToDate(formats) : default(DateTime?); + + public static DateTime ToDateTime(this string s, string[] formats) { + if (s == "Now") + return DateTime.Now; + else if (s == "Today") + return DateTime.Today; + + DateTime result; + + if (!DateTime.TryParseExact(s, formats, new DateTimeFormatInfo(), DateTimeStyles.None, out result)) + throw new ApplicationException(string.Format("unable to parse exact date from value [{0}] with formats [{1}]", s, string.Join(", ", formats))); + + return DateTime.SpecifyKind(result, DateTimeKind.Utc); + } + + public static DateTime ToDateTime(this string s) => s.ToDateTime(new string[] { "dd/MM/yyyy", "dd/MM/yyyy HH:mm:ss", "dd/MM/yyyy HH:mm", "yyyy-MM-dd'T'HH:mm:ss'Z'" }); + + public static DateTime? ToNullableDateTime(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToDateTime() : default(DateTime?); + + public static DateTime? ToNullableDateTime(this string s, string[] formats) => !string.IsNullOrWhiteSpace(s) ? s.ToDateTime(formats) : default(DateTime?); + #endregion + public static bool ToBool(this string s) => new string[] { "ok", "yes", "y", "true", "1" }.Any(x => string.Equals(x, s, StringComparison.InvariantCultureIgnoreCase)); + + public static bool? ToNullableBool(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToBool() : default(bool?); + + public static Guid ToGuid(this string text) { + if (Guid.TryParse(text, out var value)) + return value; + + using var md5 = MD5.Create(); + byte[] hash = md5.ComputeHash(Encoding.Default.GetBytes(text.ToUpper())); + return new Guid(hash); + } + + public static Guid? ToNullableGuid(this string s) => !string.IsNullOrWhiteSpace(s) ? s.ToGuid() : default(Guid?); + + public static string[] StringSplit(this string s, char c) { + return s.Split(c).Select(x => x.Trim()).ToArray(); + } + + /// + /// + /// + /// + /// + public static string ToTitle(this string s) { + if (!string.IsNullOrWhiteSpace(s)) { + var tmp = s.ToCharArray(); + tmp[0] = char.ToUpper(tmp[0]); + s = new string(tmp); + } + + return s; + } + + public static IEnumerable ExtractUrls(this string s) { + var urls = new List(); + + foreach (Match match in Regex.Matches(s, @"(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])")) { + if (Uri.TryCreate(match.Value, UriKind.Absolute, out Uri uri)) { + urls.Add(uri); + } + } + + return urls.Distinct(); + } + + public static string Format(this string s, params object[] args) => string.Format(s, args); + + /// + /// Adds elipsis + /// + /// + /// + /// + public static string Excerpt(this string s, int length = 60) => + string.IsNullOrEmpty(s) || s.Length < length ? s : s.Substring(0, length - 3) + "..."; + + /// + /// Converts JSON string to object + /// + /// + /// + /// + public static T? ToObject(this string s) => JsonSerializer.Deserialize(s, new JsonSerializerOptions { + PropertyNameCaseInsensitive = true + }); + + /// + /// Adapted from .Net [EmailAddres] model validation attribute + /// https://referencesource.microsoft.com/#System.ComponentModel.DataAnnotations/DataAnnotations/EmailAddressAttribute.cs,17f9a2457ddc8d93 + /// + /// + /// + public static bool IsValidEmail(this string? s) { + Regex CreateRegEx() { + const string pattern = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$"; + const RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture; + + // Set explicit regex match timeout, sufficient enough for email parsing + // Unless the global REGEX_DEFAULT_MATCH_TIMEOUT is already set + TimeSpan matchTimeout = TimeSpan.FromSeconds(2); + + try { + if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null) { + return new Regex(pattern, options, matchTimeout); + } + } + catch { + // Fallback on error + } + + // Legacy fallback (without explicit match timeout) + return new Regex(pattern, options); + } + + // This attribute provides server-side email validation equivalent to jquery validate, + // and therefore shares the same regular expression. See unit tests for examples. + var _regex = CreateRegEx(); + + if (s == null) + return false; + + return s != null && _regex.Match(s).Length > 0; + } + + /// + /// Convert HTML string to Plain Text without external libraries + /// https://docs.microsoft.com/en-us/answers/questions/594274/convert-html-text-to-plain-text-in-c.html + /// + /// + /// + public static string HTMLToPlainText(this string htmlCode) { + + // Remove new lines since they are not visible in HTML + htmlCode = htmlCode.Replace("\n", " "); + + // Remove tab spaces + htmlCode = htmlCode.Replace("\t", " "); + + // Remove multiple white spaces from HTML + htmlCode = Regex.Replace(htmlCode, "\\s+", " "); + + // Remove HEAD tag + htmlCode = Regex.Replace(htmlCode, "", "", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + // Remove any JavaScript + htmlCode = Regex.Replace(htmlCode, "", "", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + // Replace special characters like &, <, >, " etc. + StringBuilder sbHTML = new StringBuilder(htmlCode); + + // Note: There are many more special characters, these are just + // most common. You can add new characters in this arrays if needed + string[] OldWords = {" ", "&", """, "<", ">", "®", "©", "•", "™","'"}; + string[] NewWords = { " ", "&", "\"", "<", ">", "®", "©", "•", "â„¢", "\'" }; + + for (int i = 0; i < OldWords.Length; i++) { + sbHTML.Replace(OldWords[i], NewWords[i]); + } + + // Check if there are line breaks (
) or paragraph (

) + sbHTML.Replace("
", "\n
"); + sbHTML.Replace("
]*>", ""); + + // Remove external new lines + plainText = plainText.Trim(); + + return plainText; + } + } +} \ No newline at end of file diff --git a/webapi/Tests/Core/Abstractions/ConfigurationBase.cs b/webapi/Tests/Core/Abstractions/ConfigurationBase.cs new file mode 100644 index 0000000..e57c724 --- /dev/null +++ b/webapi/Tests/Core/Abstractions/ConfigurationBase.cs @@ -0,0 +1,61 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +using Xunit; + +using CoreTests.Core; + +namespace Tests.CoreTests.Abstractions { + [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] + public abstract class ConfigurationBase { + + protected IConfiguration _configuration; + + protected ServiceCollection ServiceCollection = new ServiceCollection(); + + protected ServiceProvider ServiceProvider { get => ServiceCollection.BuildServiceProvider(); } + + public ConfigurationBase() { + _configuration = InitConfig(); + ConfigureServices(ServiceCollection); + } + + protected abstract void ConfigureServices(IServiceCollection services); + + private IConfiguration InitConfig() { + var aspNetCoreEnvironment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + var currentDirectory = Directory.GetCurrentDirectory(); + + var configurationBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddEnvironmentVariables(); + + if (!string.IsNullOrWhiteSpace(aspNetCoreEnvironment) && new FileInfo(Path.Combine(currentDirectory, $"appsettings.{aspNetCoreEnvironment}.json")).Exists) + configurationBuilder.AddJsonFile($"appsettings.{aspNetCoreEnvironment}.json", true); + else if (new FileInfo(Path.Combine(currentDirectory, "appsettings.json")).Exists) + configurationBuilder.AddJsonFile("appsettings.json", true, true); + else + throw new FileNotFoundException($"Unable to find appsetting.json in {currentDirectory}"); + + //var builtConfig = configurationBuilder.Build(); + //var vaultOptions = builtConfig.GetSection("Vault"); + + //configurationBuilder.AddVault(options => { + // options.Address = vaultOptions["Address"]; + + // options.UnsealKeys = vaultOptions.GetSection("UnsealKeys").Get>(); + + // options.AuthMethod = EnumerationStringId.FromValue(vaultOptions["AuthMethod"]); + // options.AppRoleAuthMethod = vaultOptions.GetSection("AppRoleAuthMethod").Get(); + // options.TokenAuthMethod = vaultOptions.GetSection("TokenAuthMethod").Get(); + + // options.MountPath = vaultOptions["MountPath"]; + // options.SecretType = vaultOptions["SecretType"]; + + // options.ConfigurationMappings = vaultOptions.GetSection("ConfigurationMappings").Get>(); + //}); + + return configurationBuilder.Build(); + } + } +} diff --git a/webapi/Tests/Core/Attributes/DefaultPriorityAttribute.cs b/webapi/Tests/Core/Attributes/DefaultPriorityAttribute.cs new file mode 100644 index 0000000..597f14c --- /dev/null +++ b/webapi/Tests/Core/Attributes/DefaultPriorityAttribute.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tests.CoreTests.Attributes +{ + ///

+ /// Indicates the priority value which will be assigned + /// to tests in this class which don't have a . + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class DefaultPriorityAttribute : Attribute + { + public DefaultPriorityAttribute(int priority) + { + Priority = priority; + } + + public int Priority { get; private set; } + } +} \ No newline at end of file diff --git a/webapi/Tests/Core/Attributes/PriorityAttribute.cs b/webapi/Tests/Core/Attributes/PriorityAttribute.cs new file mode 100644 index 0000000..2e58f19 --- /dev/null +++ b/webapi/Tests/Core/Attributes/PriorityAttribute.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tests.CoreTests.Attributes +{ + /// + /// Indicates relative priority of tests for execution. Tests with the same + /// priority are run in alphabetical order. + /// + /// Tests with no priority attribute + /// are assigned a priority of , + /// unless the class has a . + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class PriorityAttribute : Attribute + { + public PriorityAttribute(int priority) + { + Priority = priority; + } + + public int Priority { get; private set; } + } +} \ No newline at end of file diff --git a/webapi/Tests/Core/CoreTests.csproj b/webapi/Tests/Core/CoreTests.csproj new file mode 100644 index 0000000..35847cf --- /dev/null +++ b/webapi/Tests/Core/CoreTests.csproj @@ -0,0 +1,26 @@ + + + + net6.0 + enable + enable + $(MSBuildProjectName.Replace(" ", "_")) + + + + + + + + + + + + + + + + + + + diff --git a/webapi/Tests/Core/FakeServices/MessageFakeService.cs b/webapi/Tests/Core/FakeServices/MessageFakeService.cs new file mode 100644 index 0000000..a5f9685 --- /dev/null +++ b/webapi/Tests/Core/FakeServices/MessageFakeService.cs @@ -0,0 +1,19 @@ +//using DomainResults.Common; + +//using Core.Domain; +//using MsgProvider; + +//namespace CoreTests.FakeServices { +// public class MessageFakeService : IMessageService, IDisposable { +// public IDomainResult Connect(MailServerConnection conn, string userName, string password) { +// return IDomainResult.Success(); +// } + +// public IDomainResult Send(byte[] bytes) { +// return IDomainResult.Success(); +// } + + +// public void Dispose() { } +// } +////} diff --git a/webapi/Tests/Core/FakeServices/ObjectStorageFakeService.cs b/webapi/Tests/Core/FakeServices/ObjectStorageFakeService.cs new file mode 100644 index 0000000..a4b804a --- /dev/null +++ b/webapi/Tests/Core/FakeServices/ObjectStorageFakeService.cs @@ -0,0 +1,70 @@ +//using DomainResults.Common; + +//using Microsoft.Extensions.Logging; +//using Core.Enumerations; +//using ObjectStorageProvider; + + +//namespace CoreTests.FakeServices { +// public class ObjectStorageFakeService : IObjectStorageFileService { + +// private readonly ILogger _logger; +// private Dictionary> _collection; + +// /// +// /// Dictionary +// /// +// /// +// /// Dictionary +// public ObjectStorageFakeService( +// ILogger logger, +// Dictionary> collection +// ) { +// _logger = logger; +// _collection = collection; +// } + +// public (byte[]?, IDomainResult) FileDownload(string bucketName, string objectName) => +// FileDownloadAsync(bucketName, objectName).Result; + +// public Task<(byte[]?, IDomainResult)> FileDownloadAsync(string bucketName, string objectName) => +// Task.Run(() => { +// Dictionary? bucket; +// _collection.TryGetValue(bucketName, out bucket); + +// if (bucket == null) +// return IDomainResult.Failed(); + +// byte[]? content; +// bucket.TryGetValue(objectName, out content); + +// if (content == null) +// return IDomainResult.Failed(); + +// return IDomainResult.Success(content); +// }); + +// public (string?, IDomainResult) FileUpload(string bucketName, byte[] bytes, string contentType, string location = "us-east-1", bool force = false) { +// throw new NotImplementedException(); +// } + +// public (string?, IDomainResult) FileUpload(string bucketName, byte[] bytes, FileExtension fileExtension, string location = "us-east-1", bool force = false) => +// FileUploadAsync(bucketName, bytes, fileExtension, location, force).Result; + +// public Task<(string?, IDomainResult)> FileUploadAsync(string bucketName, byte[] bytes, string contentType, string location = "us-east-1", bool force = false) { +// throw new NotImplementedException(); +// } + +// public Task<(string?, IDomainResult)> FileUploadAsync(string bucketName, byte[] bytes, FileExtension fileExtension, string location = "us-east-1", bool force = false) => +// Task.Run(() => { +// var objName = $"{Guid.NewGuid()}{fileExtension.Id}"; + +// if (!_collection.ContainsKey(bucketName)) +// _collection.Add(bucketName, new Dictionary()); + +// _collection[bucketName].Add(objName, bytes); + +// return IDomainResult.Success(objName); +// }); +// } +//} diff --git a/webapi/Tests/Core/FakeServices/SessionFakeService.cs b/webapi/Tests/Core/FakeServices/SessionFakeService.cs new file mode 100644 index 0000000..cda8df0 --- /dev/null +++ b/webapi/Tests/Core/FakeServices/SessionFakeService.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Logging; + +using MongoDB.Driver; + +using DataProviders; + +namespace CoreTests.FakeServices { + public class SessionFakeService : ISessionService { + + private readonly ILogger _logger; + private List _sessions; + + public SessionFakeService( + ILogger logger + ) { + _logger = logger; + _sessions = new List(); + } + + + public void CommitTransaction(Guid id) { + _sessions.Remove(id); + } + + public void DisposeSession(Guid id) { + _sessions.Remove(id); + } + + public IClientSessionHandle? GetSession(Guid id) { + return null; + } + + public void RollbackTransaction(Guid id) { + _sessions.Remove(id); + } + + public Task StartSession() { + return Task.Run(() => { + var sessionId = Guid.NewGuid(); + _sessions.Add(sessionId); + return sessionId; + }); + } + + public void StartTransaction(Guid id) { + // do nothing + } + } +} diff --git a/webapi/Tests/Core/FakeServices/VaultFakeService.cs b/webapi/Tests/Core/FakeServices/VaultFakeService.cs new file mode 100644 index 0000000..314bee3 --- /dev/null +++ b/webapi/Tests/Core/FakeServices/VaultFakeService.cs @@ -0,0 +1,48 @@ +//using Microsoft.Extensions.Logging; + +//using DomainResults.Common; + +//using VaultProvider; + +//namespace CoreTests.FakeServices { +// public class VaultFakeService : IVaultService { + +// private readonly ILogger _logger; +// private Dictionary> _collection; + +// public VaultFakeService( +// ILogger logger, +// Dictionary> collection +// ) { +// _logger = logger; +// _collection = collection; +// } + +// public (Guid?, IDomainResult) IsTokenWhitelisted(string path, string token) => +// IsTokenWhitelistedAsync(path, token).Result; + +// public Task<(Guid?, IDomainResult)> IsTokenWhitelistedAsync(string path, string token) => +// Task.Run(() => IDomainResult.Success(Guid.NewGuid())); + +// public (string?, IDomainResult) ReadMailboxPassword(string path) => +// ReadMailboxPasswordAsync(path).Result; + +// public Task<(string?, IDomainResult)> ReadMailboxPasswordAsync(string path) => +// Task.Run(() => { +// KeyValuePair kv; +// _collection.TryGetValue(path, out kv); + +// if (kv.Value == null) +// return IDomainResult.Failed(); + +// return IDomainResult.Success((string)kv.Value); +// }); + +// public (Guid?, IDomainResult) WriteMailboxPassword(string path, string email, string password) => +// WriteMailboxPasswordAsync(path, email, password).Result; + +// public Task<(Guid?, IDomainResult)> WriteMailboxPasswordAsync(string path, string email, string password) { +// throw new NotImplementedException(); +// } +// } +//} diff --git a/webapi/Tests/Core/PriorityOrderer.cs b/webapi/Tests/Core/PriorityOrderer.cs new file mode 100644 index 0000000..eec2424 --- /dev/null +++ b/webapi/Tests/Core/PriorityOrderer.cs @@ -0,0 +1,97 @@ +/* + Usage +Add the following attribute to classes for which you want tests run in order: +[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] +Then decorate your test methods with the Priority attribute. +[Fact, Priority(-10)] +public void FirstTestToRun() { } +[Fact, Priority(0)] +public void SecondTestToRun() { } +[Fact, Priority(10)] +public void ThirdTestToRunA() { } +[Fact, Priority(10)] +public void ThirdTestToRunB() { } +[Fact] +public void TestsWithNoPriorityRunLast() { } +Priorities are evaluated in numeric order (including 0 and negative numbers). If there are multiple tests with the same priority, those tests will be run in alphabetical order. +By default, tests with no explicit Priority attribute are assigned priority int.MaxValue and will be run last. You can change this by setting a DefaultPriority attribute on your test class. +[DefaultPriority(0)] +public class MyTests +{ + [Fact] + public void SomeTest() { } + + [Fact] + public void SomeOtherTest() { } + + [Fact, Priority(10)] + public void RunMeLast() { } +} + + */ + +using Xunit.Abstractions; +using Xunit.Sdk; + +using Tests.CoreTests.Attributes; +using System.Collections.Concurrent; + +namespace CoreTests.Core +{ + public class PriorityOrderer : ITestCaseOrderer + { + + public const string Name = "Tests.CoreTests.PriorityOrderer"; + public const string Assembly = "Tests.CoreTests"; + + private static string _priorityAttributeName = typeof(PriorityAttribute).AssemblyQualifiedName; + private static string _defaultPriorityAttributeName = typeof(DefaultPriorityAttribute).AssemblyQualifiedName; + private static string _priorityArgumentName = nameof(PriorityAttribute.Priority); + + private static ConcurrentDictionary _defaultPriorities = new ConcurrentDictionary(); + + public IEnumerable OrderTestCases(IEnumerable testCases) where TTestCase : ITestCase + { + var groupedTestCases = new Dictionary>(); + var defaultPriorities = new Dictionary(); + + foreach (var testCase in testCases) + { + var defaultPriority = DefaultPriorityForClass(testCase); + var priority = PriorityForTest(testCase, defaultPriority); + + if (!groupedTestCases.ContainsKey(priority)) + groupedTestCases[priority] = new List(); + + groupedTestCases[priority].Add(testCase); + } + + var orderedKeys = groupedTestCases.Keys.OrderBy(k => k); + foreach (var list in orderedKeys.Select(priority => groupedTestCases[priority])) + { + list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name)); + foreach (TTestCase testCase in list) + yield return testCase; + } + } + + private int PriorityForTest(ITestCase testCase, int defaultPriority) + { + var priorityAttribute = testCase.TestMethod.Method.GetCustomAttributes(_priorityAttributeName).SingleOrDefault(); + return priorityAttribute?.GetNamedArgument(_priorityArgumentName) ?? defaultPriority; + } + + private int DefaultPriorityForClass(ITestCase testCase) + { + var testClass = testCase.TestMethod.TestClass.Class; + if (!_defaultPriorities.TryGetValue(testClass.Name, out var result)) + { + var defaultAttribute = testClass.GetCustomAttributes(_defaultPriorityAttributeName).SingleOrDefault(); + result = defaultAttribute?.GetNamedArgument(_priorityArgumentName) ?? int.MaxValue; + _defaultPriorities[testClass.Name] = result; + } + + return result; + } + } +} \ No newline at end of file diff --git a/webapi/Tests/Extensions/Abstractions/ServiceBase.cs b/webapi/Tests/Extensions/Abstractions/ServiceBase.cs new file mode 100644 index 0000000..5fef108 --- /dev/null +++ b/webapi/Tests/Extensions/Abstractions/ServiceBase.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; + +using Tests.CoreTests.Abstractions; + +namespace ExtensionsTests.Abstractions { + public abstract class ServicesBase : ConfigurationBase { + + public ServicesBase() : base() { } + + protected override void ConfigureServices(IServiceCollection services) { + // configure strongly typed settings objects + var appSettingsSection = _configuration.GetSection("Configuration"); + services.Configure(appSettingsSection); + // var appSettings = appSettingsSection.Get(); + } + } +} \ No newline at end of file diff --git a/webapi/Tests/Extensions/Configuration.cs b/webapi/Tests/Extensions/Configuration.cs new file mode 100644 index 0000000..938aedb --- /dev/null +++ b/webapi/Tests/Extensions/Configuration.cs @@ -0,0 +1,3 @@ +namespace ExtensionsTests { + public class Configuration {} +} diff --git a/webapi/Tests/Extensions/ExtensionsTests.csproj b/webapi/Tests/Extensions/ExtensionsTests.csproj new file mode 100644 index 0000000..4112c56 --- /dev/null +++ b/webapi/Tests/Extensions/ExtensionsTests.csproj @@ -0,0 +1,37 @@ + + + + net6.0 + enable + + false + $(MSBuildProjectName.Replace(" ", "_")) + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + PreserveNewest + + + + diff --git a/webapi/Tests/Extensions/IEquatableExtensionsTests.cs b/webapi/Tests/Extensions/IEquatableExtensionsTests.cs new file mode 100644 index 0000000..1433754 --- /dev/null +++ b/webapi/Tests/Extensions/IEquatableExtensionsTests.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; + +using ExtensionMethods; + +using Xunit; + +namespace ExtensionsTests { + public class IEquatableExtensionsTests { + + private class DummyClass : IEquatable { + public string? Test { get; set; } + + public bool Equals(DummyClass? other) { + if (other == null) + return false; + + return other.Test == Test; + } + + public override int GetHashCode() { + return Test.GetHashCode(); + } + } + + + [Fact] + public void Eq() { + Assert.True( + !new DummyClass { Test = "test" }.Eq(new DummyClass { Test = "Test" }) + && new DummyClass { Test = "Test" }.Eq(new DummyClass { Test = "Test" }) + && !new DummyClass().Eq(new DummyClass { Test = "Test" }) + ); + } + + [Fact] + public void EnumerableEq() { + + var emptyList = new List(); + + var list1 = new List { + new DummyClass { + Test = "12345" + }, + new DummyClass { + Test = "67890" + } + }; + + var list2 = new List { + new DummyClass { + Test = "67890" + }, + new DummyClass { + Test = "12345" + } + }; + + var list3 = new List { + new DummyClass { + Test = "12345" + }, + new DummyClass { + Test = "ABCDE" + } + }; + + var list4 = new List { + new DummyClass { + Test = "12345" + }, + new DummyClass { + Test = "67890" + }, + new DummyClass { + Test = "67890" + } + }; + + var list5 = new List { + new DummyClass { + Test = "12345" + }, + new DummyClass { + Test = "12345" + }, + new DummyClass { + Test = "67890" + } + }; + + + // compare between two empty lists (true) + var result1 = emptyList.EnumerableEq(emptyList); + + // compare between empty and full list (false) + var result2 = emptyList.EnumerableEq(list1); + + // compare between same but unordered lists (true) + var result3 = list1.EnumerableEq(list2); + + // compare beween two different lists of the same lenght (false) + var result4 = list1.EnumerableEq(list3); + + // compare between two different lenght lists containing same elements (false) + var result5 = list1.EnumerableEq(list4); + + // compare between different lists with same lenght and same elements but different proportions (false) + var result6 = list4.EnumerableEq(list5); + + Assert.True( + result1 + && !result2 + && result3 + && !result4 + && !result5 + && !result6 + ); + } + } +} diff --git a/webapi/Tests/Extensions/ObjectExtensionsTests.cs b/webapi/Tests/Extensions/ObjectExtensionsTests.cs new file mode 100644 index 0000000..42128d4 --- /dev/null +++ b/webapi/Tests/Extensions/ObjectExtensionsTests.cs @@ -0,0 +1,22 @@ +using ExtensionMethods; + +using Xunit; + +namespace ExtensionsTests { + + public class ObjectExtensions { + + private class DummyClass { + public string Test { get; set; } + } + + [Fact] + public void ToJson() { + var json = new DummyClass { + Test = "Test" + }.ToJson(); + + Assert.Equal("{\"test\":\"Test\"}", json); + } + } +} diff --git a/webapi/Tests/Extensions/StringExtensionsTests.cs b/webapi/Tests/Extensions/StringExtensionsTests.cs new file mode 100644 index 0000000..a47b482 --- /dev/null +++ b/webapi/Tests/Extensions/StringExtensionsTests.cs @@ -0,0 +1,256 @@ +using System; +using System.Text; +using System.Linq; +using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; + +using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyInjection; + +using Xunit; + +using ExtensionMethods; +using ExtensionsTests.Abstractions; +using System.IO; +using System.Threading.Tasks; + +namespace ExtensionsTests { + public class StringExtensions : ServicesBase { + + private readonly Configuration _configuration; + + public StringExtensions() { + _configuration = ServiceProvider.GetService>()?.Value + ?? throw new NullReferenceException("Configuration not found"); + } + + + [Fact] + public void Like() => "http://maks-it.com".Like("https:?//maks*.com"); + + [Fact] + public void Left() => string.Equals("https://maks-it.com".Left(4), "http"); + + [Fact] + public void Right() => string.Equals("https://maks-it.com".Right(4), ".com"); + + [Fact] + public void Mid() => string.Equals("https://maks-it.com".Mid(8, 7), "maks-it"); + + [Fact] + public void ToInteger() { + var value = "0".ToInteger(); + Assert.True(value.GetType() == typeof(int) && value == 0); + } + + [Fact] + public void IsInteger() { + Assert.True("123".IsInteger() && !"abc123".IsInteger()); + } + + [Fact] + public void Prepend() { + var sb = new StringBuilder("123").Prepend("abc"); + Assert.Equal("abc123", sb.ToString()); + } + + private enum DummyEnum { + [Display(Name = "First member")] + Member1, + [Display(Name = "Second member")] + Member2 + } + + [Fact] + public void ToEnum() { + Assert.True(DummyEnum.Member1 == "First member".ToEnum() && DummyEnum.Member2 == "Member2".ToEnum()); + } + + [Fact] + public void ToNullableEnum() => Assert.True(null == "".ToNullableEnum() || DummyEnum.Member1 == "Member1".ToNullableEnum()); + + [Fact] + public void ToNull() => Assert.True(null == "".ToNull() && string.Equals("test", "test".ToNull())); + + [Fact] + public void ToLong() { + var value = "0".ToLong(); + Assert.True(value.GetType() == typeof(long) && value == 0); + } + + [Fact] + public void ToNullableLong() { + var value = "0".ToNullableLong(); + Assert.True(value == 0 && value.GetType() == typeof(long) && "".ToNullableLong() == null); + } + + [Fact] + public void ToInt() { + var value = "0".ToInt(); + Assert.True(value.GetType() == typeof(int) && value == 0); + } + + [Fact] + public void ToNullableInt() { + var value = "0".ToNullableInt(); + Assert.True(value.GetType() == typeof(int) && value == 0 && "".ToNullableInt() == null); + } + + [Fact] + public void ToUint() { + var value = "0".ToUint(); + Assert.True(value.GetType() == typeof(uint) && value == 0); + } + + [Fact] + public void ToNullableUint() { + var value = "0".ToNullableUint(); + Assert.True(value.GetType() == typeof(uint) && value == 0 && "".ToNullableUint() == null); + } + + [Fact] + public void ToDecimal() { + var value = "0".ToDecimal(); + Assert.True(value.GetType() == typeof(decimal) && value == 0); + } + + [Fact] + public void ToNullableDecimal() { + var value = "0".ToNullableDecimal(); + Assert.True(value.GetType() == typeof(decimal) && "".ToNullableDecimal() == null); + } + + [Fact] + public void ToDouble() { + var value = "0".ToDouble(); + Assert.True(value.GetType() == typeof(double) && value == 0); + } + + [Fact] + public void ToNullableDouble() { + var value = "0".ToNullableDouble(); + Assert.True(value.GetType() == typeof(double) && value == 0 && "".ToNullableDouble() == null); + } + + [Fact] + public void ToDate() { + var value = "05/08/1988".ToDate(); + Assert.True(value.GetType() == typeof(DateTime) && value == new DateTime(1988, 08, 05)); + } + + [Fact] + public void ToNullableDate() { + var value = "05/08/1988".ToDate(); + Assert.True(value.GetType() == typeof(DateTime) && value == new DateTime(1988, 08, 05) && "".ToNullableDate() == null); + } + + [Fact] + public void ToDateTime() { + var value = "05/08/1988 00:30:00".ToDateTime(); + Assert.True(value.GetType() == typeof(DateTime) && value == new DateTime(1988, 08, 05, 00, 30, 00)); + } + + [Fact] + public void ToNullableDateTime() { + var value = "05/08/1988 00:30:00".ToDateTime(); + Assert.True(value.GetType() == typeof(DateTime) && value == new DateTime(1988, 08, 05, 00, 30, 00) && "".ToNullableDateTime() == null); + } + + [Fact] + public void ToBool() { + Assert.True("true".ToBool() && !"false".ToBool()); + } + + [Fact] + public void ToNullalbeBool() { + Assert.True("true".ToNullableBool().Value && !"false".ToNullableBool().Value && "".ToNullableBool() == null); + } + + [Fact] + public void ToGuid() { + var value = "7a11f6ed-d1b6-4d38-9f66-738762c4fde6".ToGuid(); + Assert.True(value.GetType() == typeof(Guid)); + } + + [Fact] + public void ToNullableGuid() => Assert.Null("".ToNullableGuid()); + + [Fact] + public void StringSplit() => Assert.True(new string[] { "hello", "world" }.SequenceEqual("hello, world".StringSplit(','))); + + [Fact] + public void ToTitle() => Assert.Equal("Maks-IT", "maks-IT".ToTitle()); + + [Fact] + public void ExtractUrls() => Assert.True(new string[] { "https://maks-it.com/123", "https://git.maks-it.com/123" } + .SequenceEqual( + "Here are some urls that we have to extract! This one: https://maks-it.com/123 and this one too https://git.maks-it.com/123" + .ExtractUrls().Select(x => x.AbsoluteUri.ToString() + ))); + + [Fact] + public void Format() => Assert.Equal("MAKS-IT is a consulting company", "{0} is a consulting company".Format("MAKS-IT")); + + [Fact] + public void Excerpt() => Assert.Equal("M...", "MAKS-IT".Excerpt(4)); + + private class DummyClass { + public string Test { get; set; } + } + + [Fact] + public void ToObject() { + var obj = "{ \"Test\": \"Test\" }".ToObject(); + Assert.Equal("Test", obj.Test); + } + + public static IEnumerable GetEmailValidationData() { + + var allData = new List { + new object [] { "BAR.SPORT.SAS.@LEGALMAIL.IT", false }, + new object [] { "DANICONFCOM@PEC..IT", false }, + new object [] { "FRANCESCOF..90@PEC.IT", false }, + new object [] { "grecoornellas.a.s.@pec.it", false }, + new object [] { "lavillamar.@pec.libero.it", false }, + new object [] { "podda.pietro.@tiscali.it", false }, + new object [] { "scads.r.l.@legalmail.it", false }, + + new object [] { "ALESSANDRO.DOTTI@ARCHIWORLDPEC.IT", true }, + new object [] { "alessandro.durante@mastertrucksrl.it", true }, + new object [] { "alessandro.elia@pec.lottomatica.it", true }, + new object [] { "ALESSANDRO.EMILIANI@PEC.IT", true }, + new object [] { "alessandro.falco@pec.lottomatica.it", true }, + new object [] { "ALESSANDRO.FANNI@PEC.IT", true }, + new object [] { "ALESSANDRO.FASULO@PEC.IT", true } + }; + + return allData; + } + + [Theory] + [MemberData(nameof(GetEmailValidationData))] + public void IsValidEmail(string email, bool expected) { + var result = email.IsValidEmail(); + Assert.Equal(expected, result); + } + + [Fact] + public void HTMLToPlainText() { + var html = @" + + +

Hello

+

World!

+
+
+
"; + + var result = html.HTMLToPlainText(); + var expected = "Hello \nWorld!"; + + Assert.True(string.Compare(expected, result) == 0); + } + + + } +} \ No newline at end of file diff --git a/webapi/Tests/Extensions/appsettings.json b/webapi/Tests/Extensions/appsettings.json new file mode 100644 index 0000000..644974e --- /dev/null +++ b/webapi/Tests/Extensions/appsettings.json @@ -0,0 +1,5 @@ +{ + "Configuration": { + + } +} \ No newline at end of file diff --git a/webapi/WeatherForecast.sln b/webapi/WeatherForecast.sln index c9da0dc..d349497 100644 --- a/webapi/WeatherForecast.sln +++ b/webapi/WeatherForecast.sln @@ -19,6 +19,14 @@ Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-co EndProject Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "ClientApp", "ClientApp\ClientApp.njsproj", "{8531F7E2-07D6-4EC5-B3E6-316BBF4499E4}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extensions", "Extensions\Extensions.csproj", "{94A44D75-4AE2-4F1A-A7E9-821710B19F93}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{216302C2-64DE-4AE7-BC14-BDAC5B732472}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExtensionsTests", "Tests\Extensions\ExtensionsTests.csproj", "{43315A1D-9E09-4398-84B9-A9D9D623AE5A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreTests", "Tests\Core\CoreTests.csproj", "{04CB9827-AA6D-4708-A26D-8420C842506D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -53,6 +61,18 @@ Global {8531F7E2-07D6-4EC5-B3E6-316BBF4499E4}.Debug|Any CPU.Build.0 = Debug|Any CPU {8531F7E2-07D6-4EC5-B3E6-316BBF4499E4}.Release|Any CPU.ActiveCfg = Release|Any CPU {8531F7E2-07D6-4EC5-B3E6-316BBF4499E4}.Release|Any CPU.Build.0 = Release|Any CPU + {94A44D75-4AE2-4F1A-A7E9-821710B19F93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94A44D75-4AE2-4F1A-A7E9-821710B19F93}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94A44D75-4AE2-4F1A-A7E9-821710B19F93}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94A44D75-4AE2-4F1A-A7E9-821710B19F93}.Release|Any CPU.Build.0 = Release|Any CPU + {43315A1D-9E09-4398-84B9-A9D9D623AE5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43315A1D-9E09-4398-84B9-A9D9D623AE5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43315A1D-9E09-4398-84B9-A9D9D623AE5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43315A1D-9E09-4398-84B9-A9D9D623AE5A}.Release|Any CPU.Build.0 = Release|Any CPU + {04CB9827-AA6D-4708-A26D-8420C842506D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04CB9827-AA6D-4708-A26D-8420C842506D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04CB9827-AA6D-4708-A26D-8420C842506D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04CB9827-AA6D-4708-A26D-8420C842506D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -60,6 +80,8 @@ Global GlobalSection(NestedProjects) = preSolution {B8F84A37-B54B-4606-9BC3-6FEB96A5A34B} = {113EE574-E047-4727-AA36-841F845504D5} {B717D8BD-BCCA-4515-9A62-CA3BE802D0F7} = {113EE574-E047-4727-AA36-841F845504D5} + {43315A1D-9E09-4398-84B9-A9D9D623AE5A} = {216302C2-64DE-4AE7-BC14-BDAC5B732472} + {04CB9827-AA6D-4708-A26D-8420C842506D} = {216302C2-64DE-4AE7-BC14-BDAC5B732472} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E2805D02-2425-424C-921D-D97341B76F73}