From 806b1cf99547f7398d755090e4973aac4ebb6c79 Mon Sep 17 00:00:00 2001 From: alex held Date: Fri, 10 Jan 2020 19:42:26 +0100 Subject: [PATCH 01/64] refactor(mock-server): add LogItem models --- .../Extensions/JsonExtensions.cs | 66 +++++++ .../Models/Logging/ILogItem.cs | 20 +++ .../Models/Logging/LogItem.cs | 87 +++++++++ .../Models/Logging/LogKind.cs | 30 ++++ .../Models/Logging/RequestMatchedLog.cs | 24 +++ .../Models/Logging/RequestUnmatchedLog.cs | 24 +++ .../Models/Logging/SetupLog.cs | 13 ++ .../Models/RequestMatcher.cs | 48 ++++- .../Models/ViewModels/HttpRequestViewModel.cs | 79 ++++++++ .../Models/ViewModels/MatchedRequest.cs | 24 +++ .../Types/IInitializer.cs | 7 + .../Extensions/EnumExtensions.cs | 74 ++++++++ .../Models/Logging/LogItemTests.cs | 170 ++++++++++++++++++ 13 files changed, 660 insertions(+), 6 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Logging/ILogItem.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Logging/LogKind.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Logging/RequestMatchedLog.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Logging/RequestUnmatchedLog.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Logging/SetupLog.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/ViewModels/MatchedRequest.cs create mode 100644 test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs create mode 100644 test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs new file mode 100644 index 00000000..aca50a32 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs @@ -0,0 +1,66 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using Newtonsoft.Json.Linq; + +namespace NinjaTools.FluentMockServer.API.Extensions +{ + public static class JsonExtensions + { + public static bool IsNullOrEmpty(this JToken token) + { + return (token == null) || + (token.Type == JTokenType.Array && !token.HasValues) || + (token.Type == JTokenType.Object && !token.HasValues) || + (token.Type == JTokenType.String && token.ToString() == String.Empty) || + (token.Type == JTokenType.Null); + } + + public static bool IsValidJson(this string text) + { + text = text.Trim(); + if ((text.StartsWith("{") && text.EndsWith("}")) || //For object + (text.StartsWith("[") && text.EndsWith("]"))) //For array + { + try + { + var obj = JToken.Parse(text); + return true; + } + catch(Exception) { + return false; + } + } + else + { + return false; + } + } + + + public static string GetDescription(this T e) where T : Enum, IConvertible + { + + var type = typeof(T); + var values = Enum.GetValues(type); + + foreach (int val in values) + { + if (val != e.ToInt32(CultureInfo.InvariantCulture)) + { + continue; + } + + var memInfo = type.GetMember(type.GetEnumName(val)); + var descriptionAttributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); + + if (descriptionAttributes.Length > 0) + { + return ((DescriptionAttribute)descriptionAttributes[0]).Description; + } + } + + throw new InvalidOperationException($"Could not find any {nameof(DescriptionAttribute)} on members of enum {type.Name}."); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/ILogItem.cs b/src/NinjaTools.FluentMockServer.API/Models/Logging/ILogItem.cs new file mode 100644 index 00000000..0278c885 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Logging/ILogItem.cs @@ -0,0 +1,20 @@ +using NinjaTools.FluentMockServer.API.Types; + +namespace NinjaTools.FluentMockServer.API.Models.Logging +{ + public interface ILogItem : IIdentifable + { + public string ToFormattedString(); + + LogKind Kind { get; } + + LogType Type => Kind switch + { + LogKind.RequestMatched => LogType.Request, + LogKind.RequestUnmatched => LogType.Request, + LogKind.SetupCreated => LogType.Setup, + LogKind.SetupDeleted => LogType.Setup + }; + } +} + diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs b/src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs new file mode 100644 index 00000000..fe5c204d --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs @@ -0,0 +1,87 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using NinjaTools.FluentMockServer.API.Extensions; + +namespace NinjaTools.FluentMockServer.API.Models.Logging +{ + [DebuggerDisplay("{DebuggerDisplay(),nq}")] + public abstract class LogItem : ILogItem + { + /// + [JsonProperty(Order = 0)] + public string Id { get; } + + [JsonProperty(Order = 1)] + public abstract LogKind Kind { get; } + + [JsonProperty(Order = 2)] + public T Content { get; } + + + public LogItem(string id, T content) + { + Content = content; + Id = id; + } + + + protected virtual string FormatContent() + { + var json = Serialize(Content); + return json; + } + + protected string Serialize(object obj) + { + return JsonConvert.SerializeObject(obj, new JsonSerializerSettings + { + Formatting = Formatting.Indented, + Converters = {new StringEnumConverter()}, + NullValueHandling = NullValueHandling.Ignore + }); + } + + /// + public virtual string ToFormattedString() + { + var header = SafeFormatHeader(); + var content = FormatContent(); + + var sb = new StringBuilder(); + sb.AppendLine(header); + sb.AppendLine(content); + var message = sb.ToString(); + return message; + } + + private string SafeFormatHeader() + { + try + { + var header = FormatHeader(); + return header; + } + catch (Exception e) + { + return GetType().Name; + } + } + protected virtual string FormatHeader() + { + var description = Kind.GetDescription(); + var header = description + $" Id={Id};"; + return header; + } + + [ExcludeFromCodeCoverage] + [DebuggerStepThrough] + public virtual string DebuggerDisplay() + { + return $"Id={Id}; Kind={Kind.ToString()};\n{ToFormattedString()}"; + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/LogKind.cs b/src/NinjaTools.FluentMockServer.API/Models/Logging/LogKind.cs new file mode 100644 index 00000000..ecee2f30 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Logging/LogKind.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace NinjaTools.FluentMockServer.API.Models.Logging +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum LogType + { + Request, + Setup + } + + + [JsonConverter(typeof(StringEnumConverter))] + public enum LogKind + { + [Description("Matched {0}")] + RequestMatched, + + [Description("Unmatched {0}")] + RequestUnmatched, + + [Description("Setup created!")] + SetupCreated, + + [Description("Setup deleted!")] + SetupDeleted + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/RequestMatchedLog.cs b/src/NinjaTools.FluentMockServer.API/Models/Logging/RequestMatchedLog.cs new file mode 100644 index 00000000..8b950dec --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Logging/RequestMatchedLog.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Models.ViewModels; + +namespace NinjaTools.FluentMockServer.API.Models.Logging +{ + public class RequestMatchedLog : LogItem + { + /// + public override LogKind Kind => LogKind.RequestMatched; + + /// + public RequestMatchedLog(string id, (HttpContext, Setup) content) : base(id, content) + { + } + + /// + protected override string FormatHeader() + { + var request = Content.Context.Request; + var requestLiteral = $"{request.Method} {request.Path}"; + return string.Format(base.FormatHeader(), requestLiteral); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/RequestUnmatchedLog.cs b/src/NinjaTools.FluentMockServer.API/Models/Logging/RequestUnmatchedLog.cs new file mode 100644 index 00000000..15cd19ac --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Logging/RequestUnmatchedLog.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Models.ViewModels; + +namespace NinjaTools.FluentMockServer.API.Models.Logging +{ + public class RequestUnmatchedLog : LogItem + { + /// + public override LogKind Kind => LogKind.RequestUnmatched; + + /// + public RequestUnmatchedLog(string id, HttpContext content) : base(id,new HttpRequestViewModel(content.Request)) + { + } + + /// + protected override string FormatHeader() + { + var request = Content; + var requestLiteral = $"{request.Method} {request.Path}"; + return string.Format(base.FormatHeader(), requestLiteral); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/SetupLog.cs b/src/NinjaTools.FluentMockServer.API/Models/Logging/SetupLog.cs new file mode 100644 index 00000000..1e809194 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Logging/SetupLog.cs @@ -0,0 +1,13 @@ +namespace NinjaTools.FluentMockServer.API.Models.Logging +{ + public sealed class SetupLog : LogItem + { + /// + public SetupLog(string id, Setup content, LogKind kind) : base(id, content) + { + Kind = kind; + } + + public override LogKind Kind { get; } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs index d9e02795..a8c3fd46 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs @@ -2,14 +2,47 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Text; using JetBrains.Annotations; using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace NinjaTools.FluentMockServer.API.Models { - [DebuggerDisplay("{" + nameof(Path) + "} ({" + nameof(Method) + "})" )] - public class RequestMatcher + [DebuggerDisplay("{DebuggerDisplay()}")] + public class RequestMatcher { + public string DebuggerDisplay() + { + var path = Path + QueryString ?? ""; + var sb = new StringBuilder(); + + if (!string.IsNullOrEmpty(Method)) + { + sb.Append($"Method={Method}; "); + } + + if (!string.IsNullOrEmpty(Path)) + { + sb.Append($"Path={Path}; "); + } + + if (!string.IsNullOrEmpty(QueryString)) + { + sb.Append($"Query={QueryString}; "); + } + + sb.Append($"Headers={Headers?.Count ?? 0};\n"); + + if (BodyMatcher != null) + { + sb.AppendLine(BodyMatcher.DebuggerDisplay()); + } + + return sb.ToString(); + } + private string _path; public RequestBodyMatcher BodyMatcher { get; set; } @@ -24,7 +57,6 @@ public string Path public string Method { get; set; } public Dictionary Headers { get; set; } - public string? QueryString { get; set; } public bool IsMatch(HttpContext context) @@ -107,11 +139,15 @@ private bool HeadersMatching(IHeaderDictionary requestHeaders) } } + [DebuggerDisplay("{DebuggerDisplay()}")] public class RequestBodyMatcher { - [CanBeNull] - public string Content { get; set; } + public string DebuggerDisplay() + { + return $"Type={Type.ToString()}; MatchExact={MatchExact.ToString()}; Content={Content ?? ""}"; + } + public string? Content { get; set; } public RequestBodyType Type { get; set; } public bool MatchExact { get; set; } @@ -133,10 +169,10 @@ public bool IsMatch([NotNull] HttpRequest request) } } + [JsonConverter(typeof(StringEnumConverter))] public enum RequestBodyType { Text, Base64 } - } diff --git a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs new file mode 100644 index 00000000..8f2ff2ab --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; + +namespace NinjaTools.FluentMockServer.API.Models.ViewModels +{ + [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore, ItemRequired = Required.AllowNull)] + public class HttpRequestViewModel + { + [JsonIgnore] public HttpRequest Request { get; } + + public HttpRequestViewModel(HttpRequest request) + { + Request = request; + Body = ReadBody(); + + Headers = Request.Headers.Any() + ? Request.Headers.ToDictionary( + k => k.Key, + v => v.Value.ToList()) + : null; + + Cookies = request.Cookies.Any() + ? Request.Cookies.ToDictionary( + k => k.Key, + v => v.Value) + : null; + + Method = Request.Method; + Path = Request.Path.Value; + QueryString = Request.QueryString.HasValue ? Request.QueryString.Value : null; + IsHttps = Request.IsHttps; + } + + public string? Method { get; } + public string? Path { get; } + public string? Body { get; } + public string? QueryString { get; } + public Dictionary> Headers { get; } + public Dictionary Cookies { get; } + public bool IsHttps { get; } + + private string ReadBody() + { + var stream = Request.Body; + + try + { + if (stream.CanRead && stream.CanSeek && stream.Length > 0) + { + Request.EnableBuffering(); + stream.Seek(0, SeekOrigin.Begin); + using var reader = new StreamReader(stream); + var body = reader.ReadToEndAsync().GetAwaiter().GetResult(); + + if (body == string.Empty) + { + return null; + } + + return body; + } + + return null; + } + catch (NotSupportedException e) when (e.Message == "Specified method is not supported.") + { + return null; + } + catch (Exception e) + { + return e.Message; + } + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/MatchedRequest.cs b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/MatchedRequest.cs new file mode 100644 index 00000000..8aedce53 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/MatchedRequest.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; + +namespace NinjaTools.FluentMockServer.API.Models.ViewModels +{ + public class MatchedRequest + { + [JsonIgnore] public HttpContext Context { get; } + + [JsonProperty(Order = 0)] public HttpRequestViewModel HttpRequest { get; } + + [JsonProperty(Order = 1)] public Setup Setup { get; } + + public MatchedRequest(HttpContext context, Setup setup) + { + Context = context; + Setup = setup; + HttpRequest = new HttpRequestViewModel(context.Request); + } + + public static implicit operator (HttpContext, Setup)(MatchedRequest matchedRequest) => (matchedRequest.Context, matchedRequest.Setup); + public static implicit operator MatchedRequest((HttpContext, Setup) matchedRequest) => new MatchedRequest(matchedRequest.Item1, matchedRequest.Item2); + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Types/IInitializer.cs b/src/NinjaTools.FluentMockServer.API/Types/IInitializer.cs index 09d4fc26..cd449a75 100644 --- a/src/NinjaTools.FluentMockServer.API/Types/IInitializer.cs +++ b/src/NinjaTools.FluentMockServer.API/Types/IInitializer.cs @@ -4,6 +4,13 @@ [assembly: InternalsVisibleTo("NinjaTools.FluentMockServer.API.Tests")] namespace NinjaTools.FluentMockServer.API.Types { + + public interface IIdentifable + { + T Id { get; } + } + + /// /// Initializes this instance. /// diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs b/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs new file mode 100644 index 00000000..5554ca8c --- /dev/null +++ b/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs @@ -0,0 +1,74 @@ +using System; +using System.ComponentModel; +using FluentAssertions; +using NinjaTools.FluentMockServer.API.Extensions; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.API.Tests.Extensions +{ + public class EnumExtensions : XUnitTestBase + { + /// + public EnumExtensions(ITestOutputHelper output) : base(output) + { + } + + public enum Animals + { + Dog, + Cat, + Fish + } + + + private const string DogDescription = "The best friend of the human."; + + public enum AnimalsWithDescription + { + [Description(DogDescription)] + Dog, + [Description()] + Cat, + Fish + } + + [Theory] + [InlineData(Animals.Cat)] + [InlineData(Animals.Dog)] + [InlineData(Animals.Fish)] + [InlineData(AnimalsWithDescription.Fish)] + public void GetDescription_Should_Throw_When_Enum_Does_Not_Contain_Description_Attribute(T value) where T : Enum, IConvertible + { + // Act + Func invocation = () => value.GetDescription(); + + // Assert + invocation.Should().ThrowExactly(); + } + + + + [Fact] + public void GetDescription_Should_Return_DescriptionValue_When_EnumValue_Contains_Description_Attribute() + { + // Arrange + var value = AnimalsWithDescription.Dog; + + // Act & Assert + value.GetDescription().Should().Be(DogDescription); + } + + [Fact] + public void GetDescription_Should_Return_DescriptionValue_When_Enum_Contains_Empty_Description_Attribute() + { + // Arrange + var value = AnimalsWithDescription.Cat; + + // Act & Assert + value.GetDescription().Should().Be(string.Empty); + } + + } +} diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs new file mode 100644 index 00000000..47df5232 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs @@ -0,0 +1,170 @@ +using FluentAssertions; +using JsonPrettyPrinterPlus; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Helper; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using Xunit; +using Xunit.Abstractions; +using HttpResponse = NinjaTools.FluentMockServer.API.Models.HttpResponse; + +namespace NinjaTools.FluentMockServer.API.Tests.Models.Logging +{ + public class LogItemTestData : TheoryData + { + public static HttpContext Context = new DefaultHttpContext() + { + Request = { Path = "/request/path",Method = "PUT" } + }; + + public static readonly Setup DefaultSetup = new Setup + { + Action = new ResponseAction + { + Response = new HttpResponse + { + Body = "body", + StatusCode = 200 + } + }, + Matcher = new RequestMatcher + { + Method = "POST", + Path = "/test/path" + } + }; + + internal static readonly SetupLog SetupCreated; + internal static readonly SetupLog SetupDeleted; + internal static readonly RequestUnmatchedLog RequestUnmatched; + internal static readonly RequestMatchedLog RequestMatched; + + static LogItemTestData() + { + var factory = new LogFactory(); + SetupCreated = factory.SetupCreated(DefaultSetup); + SetupDeleted = factory.SetupDeleted(DefaultSetup); + RequestMatched = factory.RequestMached(Context, DefaultSetup); + RequestUnmatched = factory.RequestUnmatched(Context); + } + + private void AddLogItem(ILogItem logItem, string expected) + { + Add(logItem, expected.Replace("{id}", logItem.Id)); + } + + public LogItemTestData() + { + AddLogItem(SetupCreated, @"Setup created! Id={id}; +{ + ""Matcher"": { + ""Path"": ""/test/path"", + ""Method"": ""POST"" + }, + ""Action"": { + ""Response"": { + ""StatusCode"": 200, + ""Body"": ""body"" + } + } +} +"); + AddLogItem(SetupDeleted, @"Setup deleted! Id={id}; +{ + ""Matcher"": { + ""Path"": ""/test/path"", + ""Method"": ""POST"" + }, + ""Action"": { + ""Response"": { + ""StatusCode"": 200, + ""Body"": ""body"" + } + } +} +"); + AddLogItem(RequestMatched, @"Matched PUT /request/path Id={id}; +{ + ""HttpRequest"": { + ""Method"": ""PUT"", + ""Path"": ""/request/path"", + ""IsHttps"": false + }, + ""Setup"": { + ""Matcher"": { + ""Path"": ""/test/path"", + ""Method"": ""POST"" + }, + ""Action"": { + ""Response"": { + ""StatusCode"": 200, + ""Body"": ""body"" + } + } + } +} +"); + AddLogItem(RequestUnmatched, @"Unmatched PUT /request/path Id={id}; +{ + ""Method"": ""PUT"", + ""Path"": ""/request/path"", + ""IsHttps"": false +} +"); + } + } + + public class LogItemTests : XUnitTestBase + { + + /// + public LogItemTests(ITestOutputHelper output) : base(output) + { } + + [Fact] + public void SetupLog_LogType_Should_Return_Setup() + { + // Arrange + ILogItem log = LogItemTestData.SetupCreated; + + // Act & Assert + log.Type.Should().Be(LogType.Setup); + } + + + [Fact] + public void RequestMatchedLog_LogType_Should_Return_Request() + { + // Arrange + ILogItem log = LogItemTestData.RequestMatched; + + // Act & Assert + log.Type.Should().Be(LogType.Request); + } + + + [Fact] + public void RequestUnmatchedLog_LogType_Should_Return_Request() + { + // Arrange + ILogItem log = LogItemTestData.RequestUnmatched; + + // Act & Assert + log.Type.Should().Be(LogType.Request); + } + + [Theory] + [ClassData(typeof(LogItemTestData))] + public void ToFormattedString_Should_Return_PrettyFormatted_String(ILogItem log, string expected) + { + // Act + var formatted = log.ToFormattedString(); + formatted.PrettyPrintJson(); + Dump(expected, formatted); + + // Assert + formatted.Should().Be(expected); + } + } +} From 2e2316c72e1572b737d3bf65b5d744c68295fb1c Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 05:53:58 +0100 Subject: [PATCH 02/64] feat(mock-server): add support for manging logs * logs HttpRequest (matched, unmatched) * logs creation / deletion of Setup * list and query for logs * prune all logs --- .../Controllers/LogController.cs | 56 ++++++++ .../DependencyInjection/MockServerBuilder.cs | 18 ++- .../ServiceCollectionExtensions.cs | 4 - .../Helper/LogFactory.cs | 39 ++++++ .../Infrastructure/SetupRepository.cs | 37 ++---- .../Proxy/ProxyMiddleware.cs | 17 ++- .../Services/ILogService.cs | 22 ++++ .../Services/LogService.cs | 58 +++++++++ .../Infrastructure/SetupRepositoryTests.cs | 46 +++++++ .../Models/Logging/LogItemTests.cs | 3 +- .../Services/LogServiceTests.cs | 122 ++++++++++++++++++ 11 files changed, 383 insertions(+), 39 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Helper/LogFactory.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Services/ILogService.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Services/LogService.cs create mode 100644 test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs create mode 100644 test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs diff --git a/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs b/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs new file mode 100644 index 00000000..31ab36aa --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.API.Services; + +namespace NinjaTools.FluentMockServer.API.Controllers +{ + [Route("log")] + public class LogController : ControllerBase + { + private readonly ILogService _logService; + + public LogController(ILogService logService) + { + _logService = logService; + } + + [HttpGet("list")] + public Task> List([FromQuery] string? type) + { + if (Enum.TryParse(type, out var logType)) + { + var logs = _logService.OfType(logType); + return Task.FromResult(logs); + } + else + { + var logs = _logService.Get(); + return Task.FromResult(logs); + } + } + + [HttpGet("matched")] + public Task> GetMatchedRequests() + { + var logs = _logService.OfType(); + return Task.FromResult>(logs); + } + + [HttpGet("unmatched")] + public Task> GetUnmatchedRequests() + { + var logs = _logService.OfType(); + return Task.FromResult>(logs); + } + + [HttpPost("prune")] + public Task> Prune() + { + var logs = _logService.Prune(); + return Task.FromResult(logs); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs index 390e75c4..a9aeb34c 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs @@ -4,6 +4,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using NinjaTools.FluentMockServer.API.Configuration; using NinjaTools.FluentMockServer.API.Infrastructure; using NinjaTools.FluentMockServer.API.Services; @@ -19,16 +21,22 @@ public MockServerBuilder([NotNull] IServiceCollection services, [NotNull] IConfi Services.TryAddSingleton(); Services.TryAddSingleton(); - Services.TryAddSingleton(); - Services.TryAddSingleton(); Services.TryAddScoped(); Services.TryAddSingleton(); - + Services.TryAddSingleton(); + Services.TryAddSingleton(); + Services.TryAddSingleton(); + MvcCoreBuilder = Services .AddMvcCore(options => { options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true; }) .AddControllersAsServices() .AddAuthorization() - .AddNewtonsoftJson(); + .AddNewtonsoftJson(opt => + { + opt.SerializerSettings.Formatting = Formatting.Indented; + opt.SerializerSettings.Converters.Add(new StringEnumConverter()); + opt.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + }); Services.AddMiddlewareAnalysis(); Services.AddLogging(); @@ -36,7 +44,7 @@ public MockServerBuilder([NotNull] IServiceCollection services, [NotNull] IConfi Services.Configure(opt => { - + opt.ConfigFilePath = "/etc/mock-server/config/"; }); } diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs index e416d6b3..12392697 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs @@ -28,8 +28,6 @@ public static IMockServerBuilder AddMockServer([NotNull] this IServiceCollection return AddMockServer(services, config); } - - internal static IServiceCollection AddInitializers(this IServiceCollection services) { services.TryAddSingleton(sp => @@ -43,8 +41,6 @@ internal static IServiceCollection AddInitializers(this IServiceCollection servi return services; } - - public static IServiceCollection AddSwagger(this IServiceCollection services) { return services.AddSwaggerGen(o => diff --git a/src/NinjaTools.FluentMockServer.API/Helper/LogFactory.cs b/src/NinjaTools.FluentMockServer.API/Helper/LogFactory.cs new file mode 100644 index 00000000..f48f07dd --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Helper/LogFactory.cs @@ -0,0 +1,39 @@ +using System; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Models.Logging; + +namespace NinjaTools.FluentMockServer.API.Helper +{ + public delegate string GenerateId(); + + public interface ILogFactory + { + + string GenerateId(); + SetupLog SetupCreated(Setup setup); + SetupLog SetupDeleted(Setup setup); + RequestMatchedLog RequestMached(HttpContext context, Setup setup); + RequestUnmatchedLog RequestUnmatched(HttpContext context); + } + + + public class LogFactory + { + private readonly GenerateId _idGenerator; + + public LogFactory() : this(() => Guid.NewGuid().ToString()) + { + } + + public LogFactory(GenerateId idGenerator) + { + _idGenerator = idGenerator; + } + + public SetupLog SetupCreated(Setup setup) => new SetupLog(_idGenerator(), setup, LogKind.SetupCreated); + public SetupLog SetupDeleted(Setup setup) => new SetupLog(_idGenerator(), setup, LogKind.SetupDeleted); + public RequestMatchedLog RequestMached(HttpContext context, Setup setup) => new RequestMatchedLog(_idGenerator(), (context, setup)); + public RequestUnmatchedLog RequestUnmatched(HttpContext context) => new RequestUnmatchedLog(_idGenerator(), context); + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs index 6789eb2f..9508a61e 100644 --- a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs +++ b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using System.Linq; -using System.Net; using JetBrains.Annotations; using Microsoft.AspNetCore.Http; using NinjaTools.FluentMockServer.API.Models; -using HttpResponse = NinjaTools.FluentMockServer.API.Models.HttpResponse; +using NinjaTools.FluentMockServer.API.Services; namespace NinjaTools.FluentMockServer.API.Infrastructure { @@ -12,41 +11,27 @@ internal sealed class SetupRepository : ISetupRepository { private readonly List _setups; - public SetupRepository() + private readonly ILogService _logService; + + public SetupRepository(ILogService logService) { -#if DEBUG - _setups = new List() - { - new Setup - { - Matcher = new RequestMatcher - { - Path = "/some/test" - }, - Action = new ResponseAction - { - Response = new HttpResponse() - { - StatusCode = (int) HttpStatusCode.Accepted, - Body = "hello world!" - } - } - } - }; -#else + _logService = logService; _setups = new List(); -#endif } public IEnumerable GetAll() => _setups; - public void Add(Setup setup) => _setups.Add(setup); + public void Add(Setup setup) + { + _setups.Add(setup); + _logService.Log(log => log.SetupCreated(setup)); + } /// [CanBeNull] public Setup? TryGetMatchingSetup([NotNull] HttpContext context) { - return GetAll().FirstOrDefault(s => s.Matcher.IsMatch(context)) is {} setup + return GetAll().FirstOrDefault(s => s.Matcher?.IsMatch(context) ?? false) is {} setup ? setup : null; } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs b/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs index 21c68674..1cd175ab 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs @@ -12,16 +12,20 @@ public class ProxyMiddleware private readonly IOptions _adminOptions; private readonly RequestDelegate _next; private readonly ISetupService _setupService; + private readonly ILogService _logService; - public ProxyMiddleware(RequestDelegate next, ISetupService setupService, IOptions adminOptions) + public ProxyMiddleware(RequestDelegate next, ISetupService setupService, IOptions adminOptions, ILogService logService) { _next = next; _setupService = setupService; _adminOptions = adminOptions; + _logService = logService; } public async Task InvokeAsync([NotNull] HttpContext context) { + var path = context.Request.Path; + var method = context.Request.Method; if (context.Connection.LocalPort == _adminOptions.Value.Port) { await _next.Invoke(context); @@ -30,11 +34,20 @@ public async Task InvokeAsync([NotNull] HttpContext context) if (_setupService.TryGetMatchingSetup(context, out var setup)) { + _logService.Log(fac => fac.RequestMached(context, setup)); context.Response.StatusCode = setup.Action.Response.StatusCode; - await context.Response.WriteAsync(setup.Action.Response.Body); + + if (setup.Action.Response.Body is {} body) + { + await context.Response.WriteAsync(body); + return; + } + + await context.Response.CompleteAsync(); } else { + _logService.Log(fac => fac.RequestUnmatched(context)); context.Response.Clear(); context.Response.StatusCode = 404; await context.Response.WriteAsync($"No expectation for this request."); diff --git a/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs b/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs new file mode 100644 index 00000000..8c513e0c --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using NinjaTools.FluentMockServer.API.Helper; +using NinjaTools.FluentMockServer.API.Models.Logging; + +namespace NinjaTools.FluentMockServer.API.Services +{ + public interface ILogService + { + void Log([NotNull] Func> logFactory); + + IEnumerable Get([NotNull] Func predicate) where T : class, ILogItem; + + IEnumerable OfType(LogType type) => Get().Where(l => l.Type == type); + IEnumerable OfType() where T : class, ILogItem => Get().OfType(); + + IEnumerable Prune(); + IEnumerable Get(); + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Services/LogService.cs b/src/NinjaTools.FluentMockServer.API/Services/LogService.cs new file mode 100644 index 00000000..263e5c24 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Services/LogService.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using Microsoft.Extensions.Logging; +using NinjaTools.FluentMockServer.API.Helper; +using NinjaTools.FluentMockServer.API.Models.Logging; + +namespace NinjaTools.FluentMockServer.API.Services +{ + internal sealed class LogService : ILogService + { + private readonly List _logs; + private readonly ILogger _logger; + private readonly LogFactory _factory; + + public LogService(ILogger logger) : this(logger, new LogFactory()) + { + _logger = logger; + _logs = new List(); + _factory = new LogFactory(); + } + + public LogService(ILogger logger, LogFactory logFactory) + { + _logger = logger; + _logs = new List(); + _factory = logFactory; + } + + /// + public void Log([NotNull] Func> logFactory) + { + var logItem = logFactory(_factory); + _logger.LogInformation(logItem.ToFormattedString()); + _logs.Add(logItem); + } + + public IEnumerable Get(Func predicate) where T : class, ILogItem + { + return _logs + .OfType() + .Where(log => predicate(log)); + } + + public IEnumerable Prune() + { + var logs = _logs.ToList(); + + _logs.Clear(); + + return logs; + } + + /// + public IEnumerable Get() => _logs.ToList(); + } +} diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs new file mode 100644 index 00000000..cd479fc9 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs @@ -0,0 +1,46 @@ +using System; +using Moq; +using NinjaTools.FluentMockServer.API.Helper; +using NinjaTools.FluentMockServer.API.Infrastructure; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.API.Services; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.API.Tests.Infrastructure +{ + public class SetupRepositoryTests : XUnitTestBase + { + /// + public SetupRepositoryTests(ITestOutputHelper output) : base(output) + { } + + private ISetupRepository CreateSubject(out Mock logService) + { + logService = new Mock(); + return new SetupRepository(logService.Object); + } + + [Fact] + public void Add_Should_Create_A_SetupLogCreated_Log_Entry() + { + // Arrange + var sut = CreateSubject(out var logService); + var setup = new Setup + { + Matcher = new RequestMatcher + { + Path = "/some/path" + } + }; + + // Act + sut.Add(setup); + + // Assert + logService.Verify(m => m.Log(It.IsAny>>())); + } + } +} diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs index 47df5232..35c36618 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs @@ -1,5 +1,4 @@ using FluentAssertions; -using JsonPrettyPrinterPlus; using Microsoft.AspNetCore.Http; using NinjaTools.FluentMockServer.API.Helper; using NinjaTools.FluentMockServer.API.Models; @@ -160,7 +159,7 @@ public void ToFormattedString_Should_Return_PrettyFormatted_String(ILogItem log, { // Act var formatted = log.ToFormattedString(); - formatted.PrettyPrintJson(); + Dump(expected, formatted); // Assert diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs new file mode 100644 index 00000000..2f61e946 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs @@ -0,0 +1,122 @@ +using System.Linq; +using FluentAssertions; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Helper; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.API.Services; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.API.Tests.Services +{ + public class LogServiceTests : XUnitTestBase + { + /// + public LogServiceTests(ITestOutputHelper output) : base(output) + { + } + + private ILogService CreateSubject(GenerateId idGenerator = null) + { + if (idGenerator != null) + { + return new LogService(CreateLogger(), new LogFactory(idGenerator)); + } + + return new LogService(CreateLogger()); + } + + public const string Id = "1234"; + + [Fact] + public void Log_Should_Create_LogItem() + { + // Arrange + var sut = CreateSubject(() => Id); + var setup = new Setup(); + + // Act + sut.Log(l => l.SetupCreated(setup)); + + // Assert + var log = (SetupLog) sut.Get().Single(); + log.Id.Should().Be(Id); + log.Content.Should().Be(setup); + } + + private ILogService CreateSeededLogService() + { + var context = new DefaultHttpContext() + { + Request = { Method = "POST", Path = "/another/path"} + }; + var sut = CreateSubject(); + sut.Log(l => l.SetupCreated(new Setup + { + Matcher = new RequestMatcher {Path = "/path"} + })); + sut.Log(l => l.SetupDeleted(new Setup + { + Matcher = new RequestMatcher {Method = "POST"} + })); + sut.Log(l => l.RequestUnmatched(context)); + sut.Log(l => l.RequestMached(context, new Setup + { + Matcher = new RequestMatcher {Method = "POST"} + })); + + return sut; + } + + [Fact] + public void OfType_Returns_Only_LogItems_Of_Given_Type() + { + // Arrange + var sut = CreateSeededLogService(); + + // Act & Assert + sut.OfType().Should().HaveCount(2); + sut.OfType(LogType.Setup).Should().HaveCount(2); + sut.OfType(LogType.Request).Should().HaveCount(2); + } + + + [Fact] + public void Get_Returns_All_Log_Items() + { + // Arrange + var sut = CreateSeededLogService(); + + // Act & Assert + sut.Get().Should().HaveCount(4); + } + + + [Fact] + public void Prune_Clears_All_Logs() + { + // Arrange + var sut = CreateSeededLogService(); + + // Act & Assert + sut.Prune(); + sut.Get().Should().BeEmpty(); + } + + + [Fact] + public void Get_Returns_Only_LogItems_Matching_The_Predicate() + { + // Arrange + var sut = CreateSeededLogService(); + + // Act & Assert + sut.Get(log => log.Kind == LogKind.SetupCreated) + .Should().ContainSingle(); + sut.Get(log => log.Kind == LogKind.SetupDeleted) + .Should().ContainSingle(); + } + } +} From fb1b617d4809a4017c2df20fe6b26d687d6ad29b Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 05:54:36 +0100 Subject: [PATCH 03/64] refactor(mock-server): add [DebuggerDisplay] --- .../Models/ResponseAction.cs | 16 +++++++++++++++- .../Models/Setup.cs | 8 +++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs b/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs index c810d970..d4be939d 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs @@ -1,12 +1,26 @@ +using System.Diagnostics; + namespace NinjaTools.FluentMockServer.API.Models { - public class ResponseAction + [DebuggerDisplay("{DebuggerDisplay()}")] + public class ResponseAction { + public string DebuggerDisplay() + { + return $"{Response?.DebuggerDisplay()}"; + } + public HttpResponse Response { get; set; } } + [DebuggerDisplay("{DebuggerDisplay()}")] public class HttpResponse { + public string DebuggerDisplay() + { + return $"Status={StatusCode}; Body={Body}"; + } + public int StatusCode { get; set; } public string Body { get; set; } } diff --git a/src/NinjaTools.FluentMockServer.API/Models/Setup.cs b/src/NinjaTools.FluentMockServer.API/Models/Setup.cs index 94ab31ea..5f14b824 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/Setup.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/Setup.cs @@ -1,10 +1,16 @@ +using System.Diagnostics; using JetBrains.Annotations; namespace NinjaTools.FluentMockServer.API.Models { - // [DebuggerDisplay("{" + nameof(RequestMatcher) + "})")] + [DebuggerDisplay("{DebuggerDisplay()}")] public class Setup { + public string DebuggerDisplay() + { + return $"Matcher={Matcher?.DebuggerDisplay() ?? "*"}; Action={Action?.DebuggerDisplay() ?? ""}"; + } + [CanBeNull] public RequestMatcher? Matcher { get; set; } From bec90a001613cd6136c443d8f2851e9f4af7dc76 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 05:55:18 +0100 Subject: [PATCH 04/64] docs(api): add logging http request examples --- NinjaTools.FluentMockServer.sln | 2 ++ docs/http-requests/list-logs.http | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 docs/http-requests/list-logs.http diff --git a/NinjaTools.FluentMockServer.sln b/NinjaTools.FluentMockServer.sln index b67b88f0..4fec198d 100644 --- a/NinjaTools.FluentMockServer.sln +++ b/NinjaTools.FluentMockServer.sln @@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NinjaTools.FluentMockServer EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NinjaTools.FluentMockServer.API.Tests", "test\NinjaTools.FluentMockServer.API.Tests\NinjaTools.FluentMockServer.API.Tests.csproj", "{175B41A0-DF12-499C-AD27-9E4EC09A82DA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{760EDB36-07F6-4BB1-B026-FB517F921820}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/docs/http-requests/list-logs.http b/docs/http-requests/list-logs.http new file mode 100644 index 00000000..40e37d85 --- /dev/null +++ b/docs/http-requests/list-logs.http @@ -0,0 +1,17 @@ +### List all Logs +GET http://localhost:1080/log/list + +> {% +client.test("List all Logs", () => { + client.assert(response.status === 200, ""); +}); +%} + + +### List Request Logs +GET http://localhost:1080/log/list?type=Request + +### List Setup Logs +GET http://localhost:1080/log/list?type=Request + +### From 195b33955eba0289123ec3d591ff6847d6155292 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 06:24:34 +0100 Subject: [PATCH 05/64] chore(git): set text=auto --- .gitattributes | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index aa88753d..7cc4aec1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ -# Auto detect text files that will always have CRLF line endings on checkout. -* text eol=crlf +# Auto detect text files that will always have LF line endings on checkout. +* text=auto + # Denote all files that are truly binary and should not be modified. *.png binary From 61fe0157e03337ebf45048705ca1f8d1381224b2 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 06:43:23 +0100 Subject: [PATCH 06/64] style(mock-server): apply code styles --- .../Extensions/JsonExtensions.cs | 2 +- src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs | 4 +--- src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs | 1 - src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs | 2 -- .../Extensions/EnumExtensions.cs | 2 +- .../Models/Logging/LogItemTests.cs | 4 ++-- .../Services/LogServiceTests.cs | 2 +- 7 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs index aca50a32..d2aa59ad 100644 --- a/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs @@ -24,7 +24,7 @@ public static bool IsValidJson(this string text) { try { - var obj = JToken.Parse(text); + JToken.Parse(text); return true; } catch(Exception) { diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs b/src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs index fe5c204d..b1150d1b 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs @@ -21,14 +21,12 @@ public abstract class LogItem : ILogItem [JsonProperty(Order = 2)] public T Content { get; } - - public LogItem(string id, T content) + protected LogItem(string id, T content) { Content = content; Id = id; } - protected virtual string FormatContent() { var json = Serialize(Content); diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs index a8c3fd46..781fd103 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs @@ -15,7 +15,6 @@ public class RequestMatcher { public string DebuggerDisplay() { - var path = Path + QueryString ?? ""; var sb = new StringBuilder(); if (!string.IsNullOrEmpty(Method)) diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs b/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs index 1cd175ab..0fac1f9a 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs @@ -24,8 +24,6 @@ public ProxyMiddleware(RequestDelegate next, ISetupService setupService, IOption public async Task InvokeAsync([NotNull] HttpContext context) { - var path = context.Request.Path; - var method = context.Request.Method; if (context.Connection.LocalPort == _adminOptions.Value.Port) { await _next.Invoke(context); diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs b/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs index 5554ca8c..0a9a9149 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs @@ -29,7 +29,7 @@ public enum AnimalsWithDescription { [Description(DogDescription)] Dog, - [Description()] + [Description] Cat, Fish } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs index 35c36618..860112a0 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs @@ -12,12 +12,12 @@ namespace NinjaTools.FluentMockServer.API.Tests.Models.Logging { public class LogItemTestData : TheoryData { - public static HttpContext Context = new DefaultHttpContext() + private static readonly HttpContext Context = new DefaultHttpContext { Request = { Path = "/request/path",Method = "PUT" } }; - public static readonly Setup DefaultSetup = new Setup + private static readonly Setup DefaultSetup = new Setup { Action = new ResponseAction { diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs index 2f61e946..fffc0812 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs @@ -48,7 +48,7 @@ public void Log_Should_Create_LogItem() private ILogService CreateSeededLogService() { - var context = new DefaultHttpContext() + var context = new DefaultHttpContext { Request = { Method = "POST", Path = "/another/path"} }; From b8815de459860cdb7cf00ed8bad6f9daf6f80557 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 08:15:56 +0100 Subject: [PATCH 07/64] refactor(mock-server): remove obsolete AdministrationService --- .../DependencyInjection/MockServerBuilder.cs | 1 - .../Extensions/HttpContextExtensions.cs | 18 ----- .../Proxy/ProxyMiddleware.cs | 14 +--- .../Services/AdministrationService.cs | 72 ------------------- .../Services/IAdministrationService.cs | 10 --- 5 files changed, 1 insertion(+), 114 deletions(-) delete mode 100644 src/NinjaTools.FluentMockServer.API/Extensions/HttpContextExtensions.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Services/AdministrationService.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Services/IAdministrationService.cs diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs index a9aeb34c..56124832 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs @@ -21,7 +21,6 @@ public MockServerBuilder([NotNull] IServiceCollection services, [NotNull] IConfi Services.TryAddSingleton(); Services.TryAddSingleton(); - Services.TryAddScoped(); Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/HttpContextExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/HttpContextExtensions.cs deleted file mode 100644 index f00cbfc5..00000000 --- a/src/NinjaTools.FluentMockServer.API/Extensions/HttpContextExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.IO; -using System.Threading.Tasks; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; -using Newtonsoft.Json.Linq; - -namespace NinjaTools.FluentMockServer.API.Extensions -{ - public static class HttpContextExtensions - { - public static async Task ReadAsync([NotNull] this HttpRequest httpRequest) - { - var reader = new StreamReader(httpRequest.Body); - var json = await reader.ReadToEndAsync(); - return JObject.Parse(json).ToObject(); - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs b/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs index 0fac1f9a..e9c50775 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs @@ -1,35 +1,23 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Options; -using NinjaTools.FluentMockServer.API.Administration; using NinjaTools.FluentMockServer.API.Services; namespace NinjaTools.FluentMockServer.API.Proxy { public class ProxyMiddleware { - private readonly IOptions _adminOptions; - private readonly RequestDelegate _next; private readonly ISetupService _setupService; private readonly ILogService _logService; - public ProxyMiddleware(RequestDelegate next, ISetupService setupService, IOptions adminOptions, ILogService logService) + public ProxyMiddleware(ISetupService setupService, ILogService logService) { - _next = next; _setupService = setupService; - _adminOptions = adminOptions; _logService = logService; } public async Task InvokeAsync([NotNull] HttpContext context) { - if (context.Connection.LocalPort == _adminOptions.Value.Port) - { - await _next.Invoke(context); - return; - } - if (_setupService.TryGetMatchingSetup(context, out var setup)) { _logService.Log(fac => fac.RequestMached(context, setup)); diff --git a/src/NinjaTools.FluentMockServer.API/Services/AdministrationService.cs b/src/NinjaTools.FluentMockServer.API/Services/AdministrationService.cs deleted file mode 100644 index 7504cf2e..00000000 --- a/src/NinjaTools.FluentMockServer.API/Services/AdministrationService.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NinjaTools.FluentMockServer.API.Extensions; -using NinjaTools.FluentMockServer.API.Models; - -namespace NinjaTools.FluentMockServer.API.Services -{ - public class AdministrationService : IAdministrationService - { - private readonly ISetupService _setupService; - - public AdministrationService(ISetupService setupService) - { - _setupService = setupService; - } - - - /// - [ItemNotNull] - public async Task HandleAsync([NotNull] HttpContext context, PathString path) - { - var response = context.Response; - - if (path.StartsWithSegments("/setup", out var setupPath)) - { - return await HandleSetupsAsync(context, setupPath); - } - - - response.StatusCode = 200; - - await response.WriteAsync($"Admin area.."); - - return context; - } - - - [ItemNotNull] - public async Task HandleSetupsAsync([NotNull] HttpContext context, PathString path) - { - - if (path.StartsWithSegments("/create")) - { - var setup = await context.Request.ReadAsync(); - _setupService.Add(setup); - var json = JObject.FromObject(setup).ToString(Formatting.Indented); - context.Response.StatusCode = (int) HttpStatusCode.Created; - await context.Response.WriteAsync($"Setup created successfully!\n\n{json}"); - return context; - } - else if (path.StartsWithSegments("/list")) - { - var json = JArray.FromObject(_setupService.GetAll().ToList()).ToString(Formatting.Indented); - context.Response.StatusCode = (int) HttpStatusCode.OK; - await context.Response.WriteAsync(json); - return context; - } - else - { - await context.Response.WriteAsync($"No api endpoint available for /setup/{path.Value}"); - context.Response.StatusCode = (int) HttpStatusCode.BadRequest; - return context; - } - - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Services/IAdministrationService.cs b/src/NinjaTools.FluentMockServer.API/Services/IAdministrationService.cs deleted file mode 100644 index 196050df..00000000 --- a/src/NinjaTools.FluentMockServer.API/Services/IAdministrationService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; - -namespace NinjaTools.FluentMockServer.API.Services -{ - public interface IAdministrationService - { - Task HandleAsync(HttpContext context, PathString path); - } -} From 70c95bfa637c9a7285efabe0d09b2e93e33e0b2f Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 08:18:59 +0100 Subject: [PATCH 08/64] fix(mock-server): use correct DebuggerDisplay format --- .../Configuration/ConfigFile.cs | 2 +- .../Configuration/ConfigurationCollection.cs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFile.cs b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFile.cs index 51ff4ae8..e5bc6e48 100644 --- a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFile.cs +++ b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFile.cs @@ -9,7 +9,7 @@ namespace NinjaTools.FluentMockServer.API.Configuration { - [DebuggerDisplay("{DebuggerDisplay(), nq}")] + [DebuggerDisplay("{DebuggerDisplay()}")] public class ConfigFile : IConfigFile { [JsonConstructor] diff --git a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationCollection.cs b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationCollection.cs index 2dfad334..2c0bc674 100644 --- a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationCollection.cs +++ b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationCollection.cs @@ -8,13 +8,11 @@ namespace NinjaTools.FluentMockServer.API.Configuration { [JsonArray(AllowNullItems = false)] - [DebuggerDisplay("{DebuggerDisplay(), nq}")] + [DebuggerDisplay("{DebuggerDisplay()}")] public class ConfigurationCollection : List { public ConfigurationCollection() - { - - } + { } public ConfigurationCollection(params Setup[]? setups) : this() { From a9899277446130b42f83131e844d9dadfe311c1a Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 08:20:16 +0100 Subject: [PATCH 09/64] refactor(mock-server): reduce complexity --- .../ApplicationBuilderExtensions.cs | 8 +---- .../ServiceCollectionExtensions.cs | 11 +++---- .../Extensions/JsonExtensions.cs | 32 ------------------- .../Models/ViewModels/HttpRequestViewModel.cs | 30 ++++++----------- .../Startup.cs | 14 +------- 5 files changed, 15 insertions(+), 80 deletions(-) diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ApplicationBuilderExtensions.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ApplicationBuilderExtensions.cs index 114fc418..b3dd0ec9 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ApplicationBuilderExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ApplicationBuilderExtensions.cs @@ -15,21 +15,15 @@ public static void UseMockServer(this IApplicationBuilder app) app.UseMockServerAdminBranch(); app.UseMockServerMiddleware(); - app.UseMockServerRouting(); var initializer = app.ApplicationServices.GetRequiredService(); initializer.InitializeAsync().GetAwaiter().GetResult(); } private static void UseMockServerMiddleware(this IApplicationBuilder app) - { - app.UseMockServerProxyMiddleware(); - } - - - private static void UseMockServerProxyMiddleware(this IApplicationBuilder app) { app.UseMiddleware(); + app.UseMockServerRouting(); } diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs index 12392697..565380be 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs @@ -15,19 +15,14 @@ namespace NinjaTools.FluentMockServer.API.DependencyInjection { public static class ServiceCollectionExtensions { - [NotNull] - public static IMockServerBuilder AddMockServer([NotNull] this IServiceCollection services, [NotNull] IConfiguration configuration) - { - return new MockServerBuilder(services, configuration); - } - [NotNull] public static IMockServerBuilder AddMockServer([NotNull] this IServiceCollection services) { var config = services.BuildServiceProvider().GetRequiredService(); - return AddMockServer(services, config); + return new MockServerBuilder(services, config); } + [NotNull] internal static IServiceCollection AddInitializers(this IServiceCollection services) { services.TryAddSingleton(sp => @@ -41,6 +36,8 @@ internal static IServiceCollection AddInitializers(this IServiceCollection servi return services; } + + [NotNull] public static IServiceCollection AddSwagger(this IServiceCollection services) { return services.AddSwaggerGen(o => diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs index d2aa59ad..554bf00f 100644 --- a/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs @@ -1,43 +1,11 @@ using System; using System.ComponentModel; using System.Globalization; -using Newtonsoft.Json.Linq; namespace NinjaTools.FluentMockServer.API.Extensions { public static class JsonExtensions { - public static bool IsNullOrEmpty(this JToken token) - { - return (token == null) || - (token.Type == JTokenType.Array && !token.HasValues) || - (token.Type == JTokenType.Object && !token.HasValues) || - (token.Type == JTokenType.String && token.ToString() == String.Empty) || - (token.Type == JTokenType.Null); - } - - public static bool IsValidJson(this string text) - { - text = text.Trim(); - if ((text.StartsWith("{") && text.EndsWith("}")) || //For object - (text.StartsWith("[") && text.EndsWith("]"))) //For array - { - try - { - JToken.Parse(text); - return true; - } - catch(Exception) { - return false; - } - } - else - { - return false; - } - } - - public static string GetDescription(this T e) where T : Enum, IConvertible { diff --git a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs index 8f2ff2ab..7db9af4a 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs @@ -43,36 +43,24 @@ public HttpRequestViewModel(HttpRequest request) public Dictionary Cookies { get; } public bool IsHttps { get; } - private string ReadBody() + private string? ReadBody() { var stream = Request.Body; try { - if (stream.CanRead && stream.CanSeek && stream.Length > 0) - { - Request.EnableBuffering(); - stream.Seek(0, SeekOrigin.Begin); - using var reader = new StreamReader(stream); - var body = reader.ReadToEndAsync().GetAwaiter().GetResult(); + if (!stream.CanRead || !stream.CanSeek || stream.Length <= 0) + return null; - if (body == string.Empty) - { - return null; - } - - return body; - } - - return null; - } - catch (NotSupportedException e) when (e.Message == "Specified method is not supported.") - { - return null; + Request.EnableBuffering(); + stream.Seek(0, SeekOrigin.Begin); + using var reader = new StreamReader(stream); + var body = reader.ReadToEndAsync().GetAwaiter().GetResult(); + return body != string.Empty ? body : null; } catch (Exception e) { - return e.Message; + return null; } } } diff --git a/src/NinjaTools.FluentMockServer.API/Startup.cs b/src/NinjaTools.FluentMockServer.API/Startup.cs index fbe1f856..7ac88a4d 100644 --- a/src/NinjaTools.FluentMockServer.API/Startup.cs +++ b/src/NinjaTools.FluentMockServer.API/Startup.cs @@ -1,7 +1,6 @@ using JetBrains.Annotations; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using NinjaTools.FluentMockServer.API.DependencyInjection; @@ -10,22 +9,11 @@ namespace NinjaTools.FluentMockServer.API { public class Startup { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices([NotNull] IServiceCollection services) { - services - .AddMockServer() - .AddAdminPath(); - + services.AddMockServer().AddAdminPath(); services.AddSwagger(); - services.AddControllers(); } From 8616f1094feec9d6b2871e101fb08607f16d4a3d Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 08:20:47 +0100 Subject: [PATCH 10/64] refactor(mock-server): add test for MockServerBuilder --- .../MockServerBuilderTests.cs | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs diff --git a/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs new file mode 100644 index 00000000..0d127db9 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using FluentAssertions; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using NinjaTools.FluentMockServer.API.Configuration; +using NinjaTools.FluentMockServer.API.DependencyInjection; +using NinjaTools.FluentMockServer.API.Infrastructure; +using NinjaTools.FluentMockServer.API.Services; +using NinjaTools.FluentMockServer.API.Types; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.API.Tests.DependencyInjection +{ + public class MockServerBuilderTests : XUnitTestBase + { + /// + public MockServerBuilderTests(ITestOutputHelper output) : base(output) + { + } + + private IServiceCollection GetServiceCollection(out IConfiguration config) + { + var configBuilder = new ConfigurationBuilder(); + + configBuilder.AddInMemoryCollection(new List> + { + new KeyValuePair("MOCKSERVER_ADMIN_PORT", "1080") + }); + + config = configBuilder.Build(); + var services = new ServiceCollection(); + services.AddSingleton(config); + services.AddSingleton(config); + return services; + } + + private IMockServerBuilder CreateSubject() + { + var services = GetServiceCollection(out var config); + var builder = new MockServerBuilder(services, config); + return builder; + } + + [Fact] + public void Should_Return_When_State() + { + // Arrange + var sut = CreateSubject().Services.BuildServiceProvider(); + + // Assert + sut.EnsureRegistered(); + sut.EnsureRegistered(); + sut.EnsureRegistered(); + sut.EnsureRegistered(); + sut.EnsureRegistered(); + sut.EnsureRegistered(); + + sut.EnsureRegistered(); + sut.EnsureRegistered>().Value.Should().NotBeNull(); + sut.EnsureRegistered>(); + } + + [Fact] + public void Startup_Configure_Should_Register_All_Required_Services() + { + var host = Program.CreateHostBuilder(null).Build(); + + // Arrange + host.Services.EnsureServicesRegistered(); + } + + } + + public static class MockServerBuilderTestExtensions + { + public static T EnsureRegistered(this IServiceProvider sp) + { + return sp.GetRequiredService(); + } + + public static void EnsureServicesRegistered(this IServiceProvider sp) + { + sp.EnsureRegistered(); + sp.EnsureRegistered(); + sp.EnsureRegistered(); + sp.EnsureRegistered(); + sp.EnsureRegistered(); + sp.EnsureRegistered(); + + sp.EnsureRegistered(); + sp.EnsureRegistered>().Value.Should().NotBeNull(); + sp.EnsureRegistered>(); + } + + } +} From 5c35db1618d1a3f5b82184956016c8dc31aae1e8 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 08:21:20 +0100 Subject: [PATCH 11/64] !chore(nuget): set target framework to netstandard2.1 --- .../NinjaTools.FluentMockServer.TestContainers.csproj | 2 +- .../NinjaTools.FluentMockServer.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NinjaTools.FluentMockServer.TestContainers/NinjaTools.FluentMockServer.TestContainers.csproj b/src/NinjaTools.FluentMockServer.TestContainers/NinjaTools.FluentMockServer.TestContainers.csproj index 9524b039..229f4e93 100644 --- a/src/NinjaTools.FluentMockServer.TestContainers/NinjaTools.FluentMockServer.TestContainers.csproj +++ b/src/NinjaTools.FluentMockServer.TestContainers/NinjaTools.FluentMockServer.TestContainers.csproj @@ -3,7 +3,7 @@ BBF0E4BA-E43E-4628-B0C6-2DF1AAA5A705 1.0.8 - netstandard2.1;netcoreapp3.0;netcoreapp3.1 + netstandard2.1 8.0 Enabled diff --git a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj index bce45cfc..28e21707 100644 --- a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj +++ b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj @@ -3,7 +3,7 @@ 36A37D14-D7FC-4E32-8F48-5FB606818757 1.0.8 - netstandard2.1;netcoreapp3.0;netcoreapp3.1 + netstandard2.1 8.0 Enabled From ecaea24a0001708a21ad619c658430f268129a62 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 08:36:09 +0100 Subject: [PATCH 12/64] fix(mock-server): add missing RequestDelegate to ctor --- src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs b/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs index e9c50775..74ff2960 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/ProxyMiddleware.cs @@ -10,7 +10,7 @@ public class ProxyMiddleware private readonly ISetupService _setupService; private readonly ILogService _logService; - public ProxyMiddleware(ISetupService setupService, ILogService logService) + public ProxyMiddleware(RequestDelegate next, ISetupService setupService, ILogService logService) { _setupService = setupService; _logService = logService; From 9b420adef1717dad6c05f4625eca80aa6ee9f578 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 08:36:48 +0100 Subject: [PATCH 13/64] docs(api): add xml-doc to controllers to improve swagger --- .../Controllers/LogController.cs | 22 +++++++++++++++++++ .../Controllers/SetupController.cs | 13 +++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs b/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs index 31ab36aa..239e61cf 100644 --- a/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs +++ b/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs @@ -8,6 +8,7 @@ namespace NinjaTools.FluentMockServer.API.Controllers { [Route("log")] + [Produces("application/json")] public class LogController : ControllerBase { private readonly ILogService _logService; @@ -17,7 +18,13 @@ public LogController(ILogService logService) _logService = logService; } + /// + /// Gets all logs available. + /// + /// Request, Setup + /// A list of matching on your query. [HttpGet("list")] + [ProducesResponseType(typeof(IEnumerable), 200)] public Task> List([FromQuery] string? type) { if (Enum.TryParse(type, out var logType)) @@ -32,21 +39,36 @@ public Task> List([FromQuery] string? type) } } + /// + /// Gets all . + /// + /// A list of . [HttpGet("matched")] + [ProducesResponseType(typeof(IEnumerable), 200)] public Task> GetMatchedRequests() { var logs = _logService.OfType(); return Task.FromResult>(logs); } + /// + /// Gets all . + /// + /// A list of . [HttpGet("unmatched")] + [ProducesResponseType(typeof(IEnumerable), 200)] public Task> GetUnmatchedRequests() { var logs = _logService.OfType(); return Task.FromResult>(logs); } + /// + /// Resets all logs. + /// + /// A list of which have been deleted. [HttpPost("prune")] + [ProducesResponseType(typeof(IEnumerable), 200)] public Task> Prune() { var logs = _logService.Prune(); diff --git a/src/NinjaTools.FluentMockServer.API/Controllers/SetupController.cs b/src/NinjaTools.FluentMockServer.API/Controllers/SetupController.cs index 611f80e9..1f130327 100644 --- a/src/NinjaTools.FluentMockServer.API/Controllers/SetupController.cs +++ b/src/NinjaTools.FluentMockServer.API/Controllers/SetupController.cs @@ -7,17 +7,25 @@ namespace NinjaTools.FluentMockServer.API.Controllers { + /// + /// The API to administer . + /// [ApiController] [Route("setup")] public class SetupController : ControllerBase { private readonly ISetupService _setupService; + /// public SetupController(ISetupService setupService) { _setupService = setupService; } + /// + /// Gets all active on the Mock-Server. + /// + /// A list of . [HttpGet("list")] public IEnumerable List() { @@ -25,6 +33,11 @@ public IEnumerable List() return setups; } + /// + /// Configures a new on the Mock-Server. + /// + /// + /// The configured [HttpPost("create")] public Task Create([NotNull] Setup setup) { From 53748c67f4970c8ef48f5b1a6366e2878fdb972f Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 08:47:11 +0100 Subject: [PATCH 14/64] style: apply code styles --- .../Models/ViewModels/HttpRequestViewModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs index 7db9af4a..139e8380 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs @@ -50,7 +50,9 @@ public HttpRequestViewModel(HttpRequest request) try { if (!stream.CanRead || !stream.CanSeek || stream.Length <= 0) + { return null; + } Request.EnableBuffering(); stream.Seek(0, SeekOrigin.Begin); From 545c3428175cddd205da5ad065987e09f8a87fb3 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 13:12:43 +0100 Subject: [PATCH 15/64] feat(mock-server): write logs to file system --- .../DependencyInjection/MockServerBuilder.cs | 6 +- .../ServiceCollectionExtensions.cs | 7 +- .../Dockerfile | 7 +- .../Infrastructure/ILogRepository.cs | 15 ++++ .../Logging/FileSystemLogItem.cs | 19 ++++ .../Logging/LogRepository.cs | 86 +++++++++++++++++++ .../{Services => Logging}/LogService.cs | 32 +++---- .../Logging/LoggingInitializer.cs | 51 +++++++++++ .../Services/ILogService.cs | 1 + .../MockServerBuilderTests.cs | 5 +- .../Services/LogServiceTests.cs | 18 ++-- 11 files changed, 209 insertions(+), 38 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer.API/Infrastructure/ILogRepository.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Logging/FileSystemLogItem.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs rename src/NinjaTools.FluentMockServer.API/{Services => Logging}/LogService.cs (60%) create mode 100644 src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs index 56124832..d5ec6115 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs @@ -25,6 +25,7 @@ public MockServerBuilder([NotNull] IServiceCollection services, [NotNull] IConfi Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); + Services.TryAddSingleton(); MvcCoreBuilder = Services .AddMvcCore(options => { options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true; }) @@ -40,11 +41,6 @@ public MockServerBuilder([NotNull] IServiceCollection services, [NotNull] IConfi Services.AddMiddlewareAnalysis(); Services.AddLogging(); Services.AddInitializers(); - - Services.Configure(opt => - { - opt.ConfigFilePath = "/etc/mock-server/config/"; - }); } public IServiceProvider ServiceProvider => Services.BuildServiceProvider(); diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs index 565380be..43c0db69 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.IO.Abstractions; using System.Reflection; using JetBrains.Annotations; using Microsoft.Extensions.Configuration; @@ -8,6 +9,7 @@ using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using NinjaTools.FluentMockServer.API.Configuration; +using NinjaTools.FluentMockServer.API.Logging; using NinjaTools.FluentMockServer.API.Services; using NinjaTools.FluentMockServer.API.Types; @@ -27,10 +29,11 @@ internal static IServiceCollection AddInitializers(this IServiceCollection servi { services.TryAddSingleton(sp => { - var logger = sp.GetRequiredService>(); - var startupInitializer = new StartupInitializer(logger); + var logger = sp.GetRequiredService(); + var startupInitializer = new StartupInitializer(logger.CreateLogger()); startupInitializer.AddInitializer(new ConfigurationInitializer(sp.GetRequiredService())); + startupInitializer.AddInitializer(new LoggingInitializer(new FileSystem())); return startupInitializer; }); diff --git a/src/NinjaTools.FluentMockServer.API/Dockerfile b/src/NinjaTools.FluentMockServer.API/Dockerfile index 617b1bc4..031d1a6c 100644 --- a/src/NinjaTools.FluentMockServer.API/Dockerfile +++ b/src/NinjaTools.FluentMockServer.API/Dockerfile @@ -1,12 +1,15 @@ FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base WORKDIR /app + EXPOSE 80 EXPOSE 443 EXPOSE 1080 + RUN mkdir /etc/mock-server RUN mkdir /etc/mock-server/config -RUN chmod -R a+x /etc/mock-server - +RUN mkdir /var/log/mock-server +RUN chmod -R 777 /etc/mock-server +RUN chmod -R 777 /var/log/mock-server ENV ASPNETCORE_URLS="http://+:80;http://+:1080" diff --git a/src/NinjaTools.FluentMockServer.API/Infrastructure/ILogRepository.cs b/src/NinjaTools.FluentMockServer.API/Infrastructure/ILogRepository.cs new file mode 100644 index 00000000..25dc5db2 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Infrastructure/ILogRepository.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using NinjaTools.FluentMockServer.API.Models.Logging; + +namespace NinjaTools.FluentMockServer.API.Services +{ + public interface ILogRepository + { + IEnumerable Get(); + + public void AddOrUpdate(ILogItem log); + + public IEnumerable Prune(LogType type); + public IEnumerable Prune(); + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Logging/FileSystemLogItem.cs b/src/NinjaTools.FluentMockServer.API/Logging/FileSystemLogItem.cs new file mode 100644 index 00000000..227d9bb0 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Logging/FileSystemLogItem.cs @@ -0,0 +1,19 @@ +using System.Diagnostics; +using NinjaTools.FluentMockServer.API.Models.Logging; + +namespace NinjaTools.FluentMockServer.API.Services +{ + [DebuggerDisplay("Type={LogType}; Path={Path}; Log={Log};")] + public class FileSystemLogItem + { + public FileSystemLogItem(ILogItem log, string path) + { + Log = log; + Path = path; + } + + public ILogItem Log { get; } + public string Path { get; } + public LogType LogType => Log.Type; + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs b/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs new file mode 100644 index 00000000..6084f5fa --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions; +using System.Linq; +using NinjaTools.FluentMockServer.API.Logging; +using NinjaTools.FluentMockServer.API.Models.Logging; + +namespace NinjaTools.FluentMockServer.API.Services +{ + /// + public class LogRepository : ILogRepository + { + private readonly IFileSystem _fileSystem; + private readonly List _logItems; + + private static readonly string RequestLogDirectory = MockServerPaths.Logs.Requests; + private static readonly string SetupLogDirectory = MockServerPaths.Logs.Setups; + + public LogRepository(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + _logItems = new List(); + } + + public LogRepository() : this(new FileSystem()) + { } + + /// + public IEnumerable Get() + { + return _logItems.Select(log => log.Log); + } + + private string GetNextPath(ILogItem log) + { + var path = Path.Combine(log.Type switch + { + LogType.Request => RequestLogDirectory, + LogType.Setup => SetupLogDirectory + }, $"{log.Id}.log"); + + return path; + } + + /// + public void AddOrUpdate(ILogItem log) + { + var logPath = GetNextPath(log); + var content = log.ToFormattedString(); + + _fileSystem.File.WriteAllText(logPath, content); + var fsLogItem = new FileSystemLogItem(log, logPath); + _logItems.Add(fsLogItem); + } + + /// + public IEnumerable Prune(LogType type) + { + var pruned = new List(); + + foreach (var logItem in _logItems.Where(log => log.LogType == type)) + { + _fileSystem.File.Delete(logItem.Path); + pruned.Add(logItem.Log); + } + + _logItems.RemoveAll(log => log.LogType == type); + return pruned; + } + + /// + public IEnumerable Prune() + { + var pruned = new List(); + + foreach (var logItem in _logItems) + { + _fileSystem.File.Delete(logItem.Path); + pruned.Add(logItem.Log); + } + + _logItems.Clear(); + return pruned; + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Services/LogService.cs b/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs similarity index 60% rename from src/NinjaTools.FluentMockServer.API/Services/LogService.cs rename to src/NinjaTools.FluentMockServer.API/Logging/LogService.cs index 263e5c24..af86c959 100644 --- a/src/NinjaTools.FluentMockServer.API/Services/LogService.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs @@ -10,21 +10,17 @@ namespace NinjaTools.FluentMockServer.API.Services { internal sealed class LogService : ILogService { - private readonly List _logs; private readonly ILogger _logger; + private readonly ILogRepository _logRepository; private readonly LogFactory _factory; - public LogService(ILogger logger) : this(logger, new LogFactory()) - { - _logger = logger; - _logs = new List(); - _factory = new LogFactory(); - } + public LogService(ILogger logger, ILogRepository logRepository) : this(logger,logRepository ,new LogFactory()) + { } - public LogService(ILogger logger, LogFactory logFactory) + public LogService(ILogger logger, ILogRepository logRepository, LogFactory logFactory) { _logger = logger; - _logs = new List(); + _logRepository = logRepository; _factory = logFactory; } @@ -33,26 +29,30 @@ public void Log([NotNull] Func> logFactory) { var logItem = logFactory(_factory); _logger.LogInformation(logItem.ToFormattedString()); - _logs.Add(logItem); + _logRepository.AddOrUpdate(logItem); } public IEnumerable Get(Func predicate) where T : class, ILogItem { - return _logs + return _logRepository.Get() .OfType() .Where(log => predicate(log)); } public IEnumerable Prune() { - var logs = _logs.ToList(); - - _logs.Clear(); - + var logs = _logRepository.Get(); + _logRepository.Prune(); return logs; } + public IEnumerable Prune(LogType type) + { + var pruned = _logRepository.Prune(type); + return pruned; + } + /// - public IEnumerable Get() => _logs.ToList(); + public IEnumerable Get() => _logRepository.Get(); } } diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs b/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs new file mode 100644 index 00000000..c5e9df1e --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs @@ -0,0 +1,51 @@ +using System.IO; +using System.IO.Abstractions; +using System.Threading.Tasks; +using NinjaTools.FluentMockServer.API.Types; + +namespace NinjaTools.FluentMockServer.API.Logging +{ + public static class MockServerPaths + { + public const string LogRoot = "/var/log/mock-server"; + + public static class Logs + { + public static readonly string Setups = Path.Combine(LogRoot, "setups"); + public static readonly string Requests = Path.Combine(LogRoot, "requests"); + } + + public static string Configs = "/etc/mock-server/config/"; + } + + + public class LoggingInitializer : IInitializer + { + private readonly IFileSystem _fileSystem; + + public LoggingInitializer(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + /// + public async Task InitializeAsync() + { + var setupLogsPath = MockServerPaths.Logs.Setups; + var requestLogsPath = MockServerPaths.Logs.Requests; + + if (_fileSystem.Directory.Exists(setupLogsPath)) + { + _fileSystem.Directory.Delete(setupLogsPath, true); + } + + _fileSystem.Directory.CreateDirectory(setupLogsPath); + + if (_fileSystem.Directory.Exists(requestLogsPath)) + { + _fileSystem.Directory.Delete(requestLogsPath, true); + } + _fileSystem.Directory.CreateDirectory(requestLogsPath); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs b/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs index 8c513e0c..65df3227 100644 --- a/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs +++ b/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs @@ -17,6 +17,7 @@ public interface ILogService IEnumerable OfType() where T : class, ILogItem => Get().OfType(); IEnumerable Prune(); + IEnumerable Prune(LogType type); IEnumerable Get(); } } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs index 0d127db9..4950bc3f 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; -using FluentAssertions; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using NinjaTools.FluentMockServer.API.Configuration; using NinjaTools.FluentMockServer.API.DependencyInjection; using NinjaTools.FluentMockServer.API.Infrastructure; @@ -54,6 +52,7 @@ public void Should_Return_When_State() var sut = CreateSubject().Services.BuildServiceProvider(); // Assert + sut.EnsureRegistered(); sut.EnsureRegistered(); sut.EnsureRegistered(); sut.EnsureRegistered(); @@ -62,7 +61,6 @@ public void Should_Return_When_State() sut.EnsureRegistered(); sut.EnsureRegistered(); - sut.EnsureRegistered>().Value.Should().NotBeNull(); sut.EnsureRegistered>(); } @@ -94,7 +92,6 @@ public static void EnsureServicesRegistered(this IServiceProvider sp) sp.EnsureRegistered(); sp.EnsureRegistered(); - sp.EnsureRegistered>().Value.Should().NotBeNull(); sp.EnsureRegistered>(); } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs index fffc0812..bbb9fe6c 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs @@ -1,6 +1,6 @@ -using System.Linq; using FluentAssertions; using Microsoft.AspNetCore.Http; +using Moq; using NinjaTools.FluentMockServer.API.Helper; using NinjaTools.FluentMockServer.API.Models; using NinjaTools.FluentMockServer.API.Models.Logging; @@ -18,14 +18,16 @@ public LogServiceTests(ITestOutputHelper output) : base(output) { } - private ILogService CreateSubject(GenerateId idGenerator = null) + private ILogService CreateSubject(out Mock repo, GenerateId idGenerator = null) { + repo = new Mock(); + if (idGenerator != null) { - return new LogService(CreateLogger(), new LogFactory(idGenerator)); + return new LogService(CreateLogger(),repo.Object ,new LogFactory(idGenerator)); } - return new LogService(CreateLogger()); + return new LogService(CreateLogger(), repo.Object); } public const string Id = "1234"; @@ -34,16 +36,14 @@ private ILogService CreateSubject(GenerateId idGenerator = null) public void Log_Should_Create_LogItem() { // Arrange - var sut = CreateSubject(() => Id); + var sut = CreateSubject(out var repo,() => Id); var setup = new Setup(); // Act sut.Log(l => l.SetupCreated(setup)); // Assert - var log = (SetupLog) sut.Get().Single(); - log.Id.Should().Be(Id); - log.Content.Should().Be(setup); + repo.Verify(m => m.AddOrUpdate(It.Is(l => l.Content == setup))); } private ILogService CreateSeededLogService() @@ -52,7 +52,7 @@ private ILogService CreateSeededLogService() { Request = { Method = "POST", Path = "/another/path"} }; - var sut = CreateSubject(); + var sut = CreateSubject(out var repo); sut.Log(l => l.SetupCreated(new Setup { Matcher = new RequestMatcher {Path = "/path"} From 90bf34e56fe0aaab4a5e34ea8fa008567402e3bd Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 13:14:02 +0100 Subject: [PATCH 16/64] refactor(mock-server): organize files in feature folders --- .../Configuration/ConfigFileProvider.cs | 18 ++------- .../ConfigurationService.cs | 0 .../Controllers/LogController.cs | 40 ++++++++++++------- .../{Helper => Logging}/LogFactory.cs | 0 .../Logging => Logging/Models}/ILogItem.cs | 0 .../Logging => Logging/Models}/LogItem.cs | 0 .../Logging => Logging/Models}/LogKind.cs | 0 .../Models}/RequestMatchedLog.cs | 0 .../Models}/RequestUnmatchedLog.cs | 0 .../Logging => Logging/Models}/SetupLog.cs | 0 .../Configuration/ConfigFileProviderTests.cs | 10 +---- 11 files changed, 31 insertions(+), 37 deletions(-) rename src/NinjaTools.FluentMockServer.API/{Services => Configuration}/ConfigurationService.cs (100%) rename src/NinjaTools.FluentMockServer.API/{Helper => Logging}/LogFactory.cs (100%) rename src/NinjaTools.FluentMockServer.API/{Models/Logging => Logging/Models}/ILogItem.cs (100%) rename src/NinjaTools.FluentMockServer.API/{Models/Logging => Logging/Models}/LogItem.cs (100%) rename src/NinjaTools.FluentMockServer.API/{Models/Logging => Logging/Models}/LogKind.cs (100%) rename src/NinjaTools.FluentMockServer.API/{Models/Logging => Logging/Models}/RequestMatchedLog.cs (100%) rename src/NinjaTools.FluentMockServer.API/{Models/Logging => Logging/Models}/RequestUnmatchedLog.cs (100%) rename src/NinjaTools.FluentMockServer.API/{Models/Logging => Logging/Models}/SetupLog.cs (100%) diff --git a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFileProvider.cs b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFileProvider.cs index 41ae68fb..2ee3cd6f 100644 --- a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFileProvider.cs +++ b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFileProvider.cs @@ -3,20 +3,13 @@ using System.IO; using System.IO.Abstractions; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Newtonsoft.Json.Linq; +using NinjaTools.FluentMockServer.API.Logging; using NinjaTools.FluentMockServer.API.Models; using YamlDotNet.Serialization; namespace NinjaTools.FluentMockServer.API.Configuration { - /// - /// Runtime options for the MockServer - /// - public class MockServerOptions - { - public string ConfigFilePath { get; set; } = "/etc/mock-server/config/"; - } public interface IConfigFileProvider { @@ -27,24 +20,21 @@ public class ConfigFileProvider : IConfigFileProvider { private readonly IFileSystem _fs; private readonly ILogger _logger; - private readonly MockServerOptions _options; - public ConfigFileProvider(IFileSystem fs, ILogger logger, IOptions options) + public ConfigFileProvider(IFileSystem fs, ILogger logger) { _fs = fs; _logger = logger; - _options = options.Value; } - public ConfigFileProvider( ILogger logger, IOptions options) - : this(new FileSystem(), logger, options) + public ConfigFileProvider(ILogger logger) : this(new FileSystem(), logger) { } public IEnumerable GetConfigFiles() { - var rootDirectory = _options.ConfigFilePath; + var rootDirectory = MockServerPaths.Configs; var files = _fs.Directory.GetFiles(rootDirectory, "*", SearchOption.AllDirectories); foreach (var file in files) diff --git a/src/NinjaTools.FluentMockServer.API/Services/ConfigurationService.cs b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationService.cs similarity index 100% rename from src/NinjaTools.FluentMockServer.API/Services/ConfigurationService.cs rename to src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationService.cs diff --git a/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs b/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs index 239e61cf..b51c477c 100644 --- a/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs +++ b/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; @@ -13,6 +12,7 @@ public class LogController : ControllerBase { private readonly ILogService _logService; + /// public LogController(ILogService logService) { _logService = logService; @@ -25,11 +25,11 @@ public LogController(ILogService logService) /// A list of matching on your query. [HttpGet("list")] [ProducesResponseType(typeof(IEnumerable), 200)] - public Task> List([FromQuery] string? type) + public Task> List([FromQuery] LogType? type) { - if (Enum.TryParse(type, out var logType)) + if (type.HasValue) { - var logs = _logService.OfType(logType); + var logs = _logService.OfType(type.Value); return Task.FromResult(logs); } else @@ -39,6 +39,26 @@ public Task> List([FromQuery] string? type) } } + /// + /// Resets all logs. + /// + /// A list of which have been deleted. + [HttpGet("prune")] + [ProducesResponseType(typeof(IEnumerable), 200)] + public Task> Prune([FromQuery] LogType? type) + { + if (type.HasValue) + { + var logs = _logService.Prune(type.Value); + return Task.FromResult(logs); + } + else + { + var logs = _logService.Prune(); + return Task.FromResult(logs); + } + } + /// /// Gets all . /// @@ -63,16 +83,6 @@ public Task> GetUnmatchedRequests() return Task.FromResult>(logs); } - /// - /// Resets all logs. - /// - /// A list of which have been deleted. - [HttpPost("prune")] - [ProducesResponseType(typeof(IEnumerable), 200)] - public Task> Prune() - { - var logs = _logService.Prune(); - return Task.FromResult(logs); - } + } } diff --git a/src/NinjaTools.FluentMockServer.API/Helper/LogFactory.cs b/src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs similarity index 100% rename from src/NinjaTools.FluentMockServer.API/Helper/LogFactory.cs rename to src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/ILogItem.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs similarity index 100% rename from src/NinjaTools.FluentMockServer.API/Models/Logging/ILogItem.cs rename to src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs similarity index 100% rename from src/NinjaTools.FluentMockServer.API/Models/Logging/LogItem.cs rename to src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/LogKind.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs similarity index 100% rename from src/NinjaTools.FluentMockServer.API/Models/Logging/LogKind.cs rename to src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/RequestMatchedLog.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs similarity index 100% rename from src/NinjaTools.FluentMockServer.API/Models/Logging/RequestMatchedLog.cs rename to src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/RequestUnmatchedLog.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs similarity index 100% rename from src/NinjaTools.FluentMockServer.API/Models/Logging/RequestUnmatchedLog.cs rename to src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs diff --git a/src/NinjaTools.FluentMockServer.API/Models/Logging/SetupLog.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs similarity index 100% rename from src/NinjaTools.FluentMockServer.API/Models/Logging/SetupLog.cs rename to src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Configuration/ConfigFileProviderTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Configuration/ConfigFileProviderTests.cs index 506b2d18..8360fc71 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Configuration/ConfigFileProviderTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Configuration/ConfigFileProviderTests.cs @@ -2,7 +2,6 @@ using System.IO.Abstractions.TestingHelpers; using System.Linq; using FluentAssertions; -using Microsoft.Extensions.Options; using NinjaTools.FluentMockServer.API.Configuration; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; @@ -12,11 +11,6 @@ namespace NinjaTools.FluentMockServer.API.Tests.Configuration { public class ConfigurationProviderTests : XUnitTestBase { - private static IOptions GetOptions(string configFilePath = "/etc/mock-server/config") => Options.Create(new MockServerOptions - { - ConfigFilePath = configFilePath - }); - /// public ConfigurationProviderTests(ITestOutputHelper output) : base(output) { @@ -52,7 +46,7 @@ public void Returns_ConfigurationFile_When_Only_One_Yaml_File() Content-Length: - 105 ")}}); - var sut = new ConfigFileProvider(fs, CreateLogger(), GetOptions()); + var sut = new ConfigFileProvider(fs, CreateLogger()); // Act var configurations = sut.GetConfigFiles().ToArray(); @@ -132,7 +126,7 @@ public void Returns_ConfigurationFile_When_Only_Only_JSON_Files() } ] ")}}); - var sut = new ConfigFileProvider(fs, CreateLogger(), GetOptions()); + var sut = new ConfigFileProvider(fs, CreateLogger()); // Act var configurations = sut.GetConfigFiles().ToArray(); From 1e05d5a84f2bbc5057bb5a7da39f0d4caf8098dd Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 13:27:08 +0100 Subject: [PATCH 17/64] fix(mock-server): setup repo mock to return values --- .../Services/LogServiceTests.cs | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs index bbb9fe6c..457178e4 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using FluentAssertions; using Microsoft.AspNetCore.Http; using Moq; @@ -52,20 +53,25 @@ private ILogService CreateSeededLogService() { Request = { Method = "POST", Path = "/another/path"} }; + var factory = new LogFactory(); var sut = CreateSubject(out var repo); - sut.Log(l => l.SetupCreated(new Setup - { - Matcher = new RequestMatcher {Path = "/path"} - })); - sut.Log(l => l.SetupDeleted(new Setup - { - Matcher = new RequestMatcher {Method = "POST"} - })); - sut.Log(l => l.RequestUnmatched(context)); - sut.Log(l => l.RequestMached(context, new Setup - { - Matcher = new RequestMatcher {Method = "POST"} - })); + repo.Setup(m => m.Get()) + .Returns(new List + { + factory.SetupCreated(new Setup + { + Matcher = new RequestMatcher {Path = "/path"} + }), + factory.SetupDeleted(new Setup + { + Matcher = new RequestMatcher {Method = "POST"} + }), + factory.RequestUnmatched(context), + factory.RequestMached(context, new Setup + { + Matcher = new RequestMatcher {Method = "POST"} + }) + }); return sut; } @@ -98,11 +104,12 @@ public void Get_Returns_All_Log_Items() public void Prune_Clears_All_Logs() { // Arrange - var sut = CreateSeededLogService(); + var repo = new Mock(); + var sut = new LogService(CreateLogger(), repo.Object); // Act & Assert sut.Prune(); - sut.Get().Should().BeEmpty(); + repo.Verify(m => m.Prune(), Times.Once); } From 664e58ccc9246ba8a7596475bd2ec516c70c0afd Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 13:34:07 +0100 Subject: [PATCH 18/64] style: apply code styles --- .../Logging/LoggingInitializer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs b/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs index c5e9df1e..3b30cef8 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs @@ -7,7 +7,7 @@ namespace NinjaTools.FluentMockServer.API.Logging { public static class MockServerPaths { - public const string LogRoot = "/var/log/mock-server"; + private const string LogRoot = "/var/log/mock-server"; public static class Logs { @@ -15,10 +15,11 @@ public static class Logs public static readonly string Requests = Path.Combine(LogRoot, "requests"); } - public static string Configs = "/etc/mock-server/config/"; + public static string Configs { get; } = "/etc/mock-server/config/"; } + /// public class LoggingInitializer : IInitializer { private readonly IFileSystem _fileSystem; From a251f28cd09f44749f9a15b8142d4eef4d7dde0d Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 11 Jan 2020 22:36:22 +0100 Subject: [PATCH 19/64] chore: add idea configuration --- .gitignore | 2 + .../.idea/.gitignore | 6 + .../.idea/GitScope.xml | 7 + .../.idea/bashsupport_project.xml | 6 + .../.idea/codeStyles/Project.xml | 28 + .../.idea/codeStyles/codeStyleConfig.xml | 5 + .../.idea/codestream.xml | 7 + .../.idea/compiler.xml | 7 + .../.idea/contentModel.xml | 350 ++++++ .../.idea/dbnavigator.xml | 461 +++++++ .../.idea/debugger.xml | 6 + .../.idea/deployment.xml | 4 + .../.idea/dictionaries/dev.xml | 3 + .../.idea/discord.xml | 9 + .../.idea/indexLayout.xml | 8 + .../inspectionProfiles/Project_Default.xml | 15 + .../.idea/jsonCatalog.xml | 6 + .../.idea/jsonSchemas.xml | 18 + .../.idea/kubernetes-settings.xml | 13 + .../.idea/markdown-navigator-enh.xml | 22 + .../.idea/markdown-navigator.xml | 96 ++ .../.idea/misc.xml | 18 + .../.idea/modules.xml | 8 + .../.idea/mongoSettings.xml | 6 + .../.idea/options/debugger.xml | 32 + .../.idea/projectSettingsUpdater.xml | 6 + .../.idea/rSettings.xml | 4 + .../.idea/restRequest.json | 8 + .../.idea/runConfigurations/MockServer.xml | 29 + .../.idea/sonarIssues.xml | 1119 +++++++++++++++++ .../.idea/sonarSettings.xml | 19 + .../.idea/vagrant.xml | 7 + .../.idea/vcs.xml | 18 + .../riderModule.iml | 15 + NinjaTools.FluentMockServer.sln | 5 +- docs/1.puml | 42 + docs/http-requests/create-setup.http | 21 + .../NinjaTools.FluentMockServer.API.csproj | 4 + 38 files changed, 2439 insertions(+), 1 deletion(-) create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/.gitignore create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/GitScope.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/bashsupport_project.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/codeStyles/Project.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/codestream.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/compiler.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/dbnavigator.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/debugger.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/deployment.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/dictionaries/dev.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/discord.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/indexLayout.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/jsonCatalog.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/jsonSchemas.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/kubernetes-settings.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/markdown-navigator-enh.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/markdown-navigator.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/misc.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/modules.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/mongoSettings.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/options/debugger.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/projectSettingsUpdater.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/rSettings.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/restRequest.json create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/runConfigurations/MockServer.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/sonarSettings.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/vagrant.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/.idea/vcs.xml create mode 100644 .idea/.idea.NinjaTools.FluentMockServer/riderModule.iml create mode 100644 docs/1.puml create mode 100644 docs/http-requests/create-setup.http diff --git a/.gitignore b/.gitignore index fefa81f1..a1f967b3 100644 --- a/.gitignore +++ b/.gitignore @@ -284,3 +284,5 @@ TestResults.xml stryker-config.json **/Stryker*/*.* **/.DS_Store + +!.idea/ \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/.gitignore b/.idea/.idea.NinjaTools.FluentMockServer/.idea/.gitignore new file mode 100644 index 00000000..21069a00 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/.gitignore @@ -0,0 +1,6 @@ +# Default ignored files +/shelf/ +/workspace.xml + +# Editor-based HTTP Client requests +/httpRequests/ \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/GitScope.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/GitScope.xml new file mode 100644 index 00000000..aeaf904e --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/GitScope.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/bashsupport_project.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/bashsupport_project.xml new file mode 100644 index 00000000..ae0dddf3 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/bashsupport_project.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/codeStyles/Project.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..3cdc6aed --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/codeStyles/Project.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/codeStyles/codeStyleConfig.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..a55e7a17 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/codestream.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/codestream.xml new file mode 100644 index 00000000..111fcfa8 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/codestream.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/compiler.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/compiler.xml new file mode 100644 index 00000000..ef795199 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/compiler.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml new file mode 100644 index 00000000..1d4a7009 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -0,0 +1,350 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/dbnavigator.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/dbnavigator.xml new file mode 100644 index 00000000..94174229 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/dbnavigator.xml @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/debugger.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/debugger.xml new file mode 100644 index 00000000..4e66d610 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/debugger.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/deployment.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/deployment.xml new file mode 100644 index 00000000..820375f1 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/deployment.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/dictionaries/dev.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/dictionaries/dev.xml new file mode 100644 index 00000000..fcc4ff1a --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/dictionaries/dev.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/discord.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/discord.xml new file mode 100644 index 00000000..59b11d1d --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/discord.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/indexLayout.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/indexLayout.xml new file mode 100644 index 00000000..27ba142e --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/inspectionProfiles/Project_Default.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..e6ed9f62 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/jsonCatalog.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/jsonCatalog.xml new file mode 100644 index 00000000..d1748451 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/jsonCatalog.xml @@ -0,0 +1,6 @@ + + + + true + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/jsonSchemas.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/jsonSchemas.xml new file mode 100644 index 00000000..65aa5f61 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/jsonSchemas.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/kubernetes-settings.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/kubernetes-settings.xml new file mode 100644 index 00000000..21bf405e --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/kubernetes-settings.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/markdown-navigator-enh.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/markdown-navigator-enh.xml new file mode 100644 index 00000000..50a04a6e --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/markdown-navigator-enh.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/markdown-navigator.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/markdown-navigator.xml new file mode 100644 index 00000000..2b999b9a --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/markdown-navigator.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/misc.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/misc.xml new file mode 100644 index 00000000..75a3af77 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/misc.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/modules.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/modules.xml new file mode 100644 index 00000000..23117b80 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/mongoSettings.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/mongoSettings.xml new file mode 100644 index 00000000..d69c1f7f --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/mongoSettings.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/options/debugger.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/options/debugger.xml new file mode 100644 index 00000000..3d7e1bee --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/options/debugger.xml @@ -0,0 +1,32 @@ + + + + + + true + + + + + + + + + + + + + + + + + + diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/projectSettingsUpdater.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/projectSettingsUpdater.xml new file mode 100644 index 00000000..7515e760 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/projectSettingsUpdater.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/rSettings.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/rSettings.xml new file mode 100644 index 00000000..f48b70e1 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/rSettings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/restRequest.json b/.idea/.idea.NinjaTools.FluentMockServer/.idea/restRequest.json new file mode 100644 index 00000000..e8b1939a --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/restRequest.json @@ -0,0 +1,8 @@ +{ + "uri": "http://localhost:1080/log/list", + "method": "GET", + "body": "", + "headers": { + "Content-Type": "*/*" + } +} \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/runConfigurations/MockServer.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/runConfigurations/MockServer.xml new file mode 100644 index 00000000..5218a73c --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/runConfigurations/MockServer.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml new file mode 100644 index 00000000..11208541 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml @@ -0,0 +1,1119 @@ + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarSettings.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarSettings.xml new file mode 100644 index 00000000..0f10c883 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarSettings.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/vagrant.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/vagrant.xml new file mode 100644 index 00000000..a5aa7868 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/vagrant.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/vcs.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/vcs.xml new file mode 100644 index 00000000..a84cc4ef --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/vcs.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/riderModule.iml b/.idea/.idea.NinjaTools.FluentMockServer/riderModule.iml new file mode 100644 index 00000000..0dafa754 --- /dev/null +++ b/.idea/.idea.NinjaTools.FluentMockServer/riderModule.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/NinjaTools.FluentMockServer.sln b/NinjaTools.FluentMockServer.sln index 4fec198d..0b5cba15 100644 --- a/NinjaTools.FluentMockServer.sln +++ b/NinjaTools.FluentMockServer.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NinjaTools.FluentMockServer", "src\NinjaTools.FluentMockServer\NinjaTools.FluentMockServer.csproj", "{E9883692-C8F8-4CA9-8EDB-7859809D8DB8}" @@ -20,6 +20,9 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NinjaTools.FluentMockServer.API.Tests", "test\NinjaTools.FluentMockServer.API.Tests\NinjaTools.FluentMockServer.API.Tests.csproj", "{175B41A0-DF12-499C-AD27-9E4EC09A82DA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{760EDB36-07F6-4BB1-B026-FB517F921820}" +ProjectSection(SolutionItems) = preProject + docs\1.puml = docs\1.puml +EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/docs/1.puml b/docs/1.puml new file mode 100644 index 00000000..36835be3 --- /dev/null +++ b/docs/1.puml @@ -0,0 +1,42 @@ +@startuml +(*) --> " +start comparing pick optimized simulation results S1 and S2 +sum(P) - amount of picks +avg(G) - average amount of aisle per SIAA +avg(GG) - average amount of aisle groups per SIAA +" +--> S1 sum(P) = S2 sum(P) ? +if "" then + --> [yes] "S1 avg(G) = S2 avg(G) ?" + if "" then + --> [yes] "S1 avg(GG) = S2 avg(GG) ?" + if "" then + --> [yes] "S1 = S2" + --> (*) + else + --> [no] "S1 avg(GG) < S2 avg(GG) ?" + if "" then + --> [yes] "S1 better than S2" + else + --> [no] "S2 better than S2" + endif + endif + else + --> [no] "S1 avg(G) < S2 avg(G) ?" + if "" then + --> [yes] "S1 better than S2" + else + --> [no] "S2 better than S2" + endif + endif +else + --> [no] "S1 sum(P) < S2 sum(P) ?" + if "" then + --> [yes] "S1 better than S2" + --> (*) + else + --> [no] "S2 better than S2" + --> (*) + endif +endif +@enduml diff --git a/docs/http-requests/create-setup.http b/docs/http-requests/create-setup.http new file mode 100644 index 00000000..ef0f8b89 --- /dev/null +++ b/docs/http-requests/create-setup.http @@ -0,0 +1,21 @@ +POST http://localhost:1080/setup/create +Content-Type: application/json + +{ + "matcher":{ + "path":"/some/path" + }, + "action":{ + "response":{ + "statusCode":200, + "body":"aaa!" + } + } +} + +> {% client.test("Create Setup", () => { +client.assert(response.status === 200, "Response code is not 200."); +}); +%} + +### diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 89b2526a..117ae254 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -3,6 +3,9 @@ netcoreapp3.1 enable + preview + 1.0.0 + 1.0.0 @@ -11,6 +14,7 @@ bin\Debug\NinjaTools.FluentMockServer.API.xml + NU1605;CS8600 From bdc833d1731d1b42933799a32f80ae86b8d4ef43 Mon Sep 17 00:00:00 2001 From: alex held Date: Sun, 12 Jan 2020 14:14:29 +0100 Subject: [PATCH 20/64] SQUASH! feat(mock-server): add evaluation for best matching RequestMatcher --- .../.idea/contentModel.xml | 35 ++- .../.idea/sonarIssues.xml | 80 ++++++ .../Configuration/ConfigurationService.cs | 4 +- .../Controllers/LogController.cs | 2 +- .../DependencyInjection/MockServerBuilder.cs | 1 + ...{JsonExtensions.cs => StringExtensions.cs} | 19 +- .../Infrastructure/ILogRepository.cs | 4 +- .../Infrastructure/SetupRepository.cs | 1 + .../Logging/FileSystemLogItem.cs | 4 +- .../Logging/LogFactory.cs | 4 +- .../Logging/LogRepository.cs | 6 +- .../Logging/LogService.cs | 7 +- .../Logging/Models/ILogItem.cs | 2 +- .../Logging/Models/LogItem.cs | 2 +- .../Logging/Models/LogKind.cs | 2 +- .../Logging/Models/RequestMatchedLog.cs | 3 +- .../Logging/Models/RequestUnmatchedLog.cs | 2 +- .../Logging/Models/SetupLog.cs | 4 +- .../Models/RequestMatcher.cs | 11 +- .../NinjaTools.FluentMockServer.API.csproj | 2 + .../Proxy/Evaluation/EvaluationContext.cs | 45 +++ .../Proxy/Evaluation/EvaluationPipeline.cs | 79 ++++++ .../Evaluation/Evaluators/EvaluationWeight.cs | 10 + .../Evaluation/Evaluators/EvaluatorBase.cs | 54 ++++ .../Evaluators/HttpBodyEvaluator.cs | 46 ++++ .../Evaluators/HttpCookieEvaluator.cs | 35 +++ .../Evaluators/HttpHeaderEvaluator.cs | 36 +++ .../Evaluators/HttpMethodEvaluator.cs | 29 ++ .../Evaluators/HttpPathEvaluator.cs | 33 +++ .../Evaluators/QueryStringEvaluator.cs | 28 ++ .../Proxy/Evaluation/HttpRequestEvaluator.cs | 22 ++ .../Proxy/Evaluation/IEvaluator.cs | 11 + .../Evaluation/Models/EvaluationMessages.cs | 21 ++ .../Evaluation/Models/EvaluationResultBase.cs | 30 ++ .../Evaluation/Models/EvaluationScore.cs | 22 ++ .../Models/EvaluationSuccessfulResult.cs | 17 ++ .../Models/EvaluationUnsuccessfulResult.cs | 13 + .../Evaluation/Models/IEvaluationResult.cs | 14 + .../Evaluation/Models/NormalizedMatcher.cs | 23 ++ .../Proxy/Evaluation/Models/Rating.cs | 21 ++ .../Services/ILogService.cs | 4 +- .../Types/ChainElement.cs | 134 +++++++++ .../Infrastructure/SetupRepositoryTests.cs | 4 +- .../Models/Logging/LogItemTests.cs | 4 +- .../Models/RequestBodyMatcherTests.cs | 10 +- .../Models/RequestMatcherTests.cs | 110 ++++---- .../Proxy/Matchers/EvaluatorTests.cs | 256 ++++++++++++++++++ .../Services/LogServiceTests.cs | 5 +- 48 files changed, 1217 insertions(+), 94 deletions(-) rename src/NinjaTools.FluentMockServer.API/Extensions/{JsonExtensions.cs => StringExtensions.cs} (70%) create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluationWeight.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/HttpRequestEvaluator.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/IEvaluator.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationMessages.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationScore.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationUnsuccessfulResult.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/NormalizedMatcher.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Types/ChainElement.cs create mode 100644 test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index 1d4a7009..a22bddfb 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -15,7 +15,6 @@ - @@ -131,7 +130,7 @@ - + @@ -175,6 +174,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -184,6 +209,7 @@ + @@ -235,6 +261,11 @@ + + + + + diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml index 11208541..755b0ffa 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml @@ -538,6 +538,11 @@ + + + + + @@ -708,6 +713,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -788,6 +858,11 @@ + + + + + @@ -1008,6 +1083,11 @@ + + + + + diff --git a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationService.cs b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationService.cs index eba46fbb..8e74a0d9 100644 --- a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationService.cs +++ b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationService.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using NinjaTools.FluentMockServer.API.Configuration; +using NinjaTools.FluentMockServer.API.Services; -namespace NinjaTools.FluentMockServer.API.Services +namespace NinjaTools.FluentMockServer.API.Configuration { public interface IConfigurationService { diff --git a/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs b/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs index b51c477c..18a5d6ee 100644 --- a/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs +++ b/src/NinjaTools.FluentMockServer.API/Controllers/LogController.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.API.Logging.Models; using NinjaTools.FluentMockServer.API.Services; namespace NinjaTools.FluentMockServer.API.Controllers diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs index d5ec6115..6654c20a 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json.Converters; using NinjaTools.FluentMockServer.API.Configuration; using NinjaTools.FluentMockServer.API.Infrastructure; +using NinjaTools.FluentMockServer.API.Logging; using NinjaTools.FluentMockServer.API.Services; namespace NinjaTools.FluentMockServer.API.DependencyInjection diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs similarity index 70% rename from src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs rename to src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs index 554bf00f..842e9125 100644 --- a/src/NinjaTools.FluentMockServer.API/Extensions/JsonExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs @@ -1,11 +1,28 @@ using System; using System.ComponentModel; using System.Globalization; +using System.Text.RegularExpressions; namespace NinjaTools.FluentMockServer.API.Extensions { - public static class JsonExtensions + public static class StringExtensions { + public static bool IsValidRegex(this string pattern) + { + if (string.IsNullOrEmpty(pattern)) return false; + + try + { + Regex.Match("", pattern); + } + catch (ArgumentException) + { + return false; + } + + return true; + } + public static string GetDescription(this T e) where T : Enum, IConvertible { diff --git a/src/NinjaTools.FluentMockServer.API/Infrastructure/ILogRepository.cs b/src/NinjaTools.FluentMockServer.API/Infrastructure/ILogRepository.cs index 25dc5db2..06023b41 100644 --- a/src/NinjaTools.FluentMockServer.API/Infrastructure/ILogRepository.cs +++ b/src/NinjaTools.FluentMockServer.API/Infrastructure/ILogRepository.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.API.Logging.Models; -namespace NinjaTools.FluentMockServer.API.Services +namespace NinjaTools.FluentMockServer.API.Infrastructure { public interface ILogRepository { diff --git a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs index 9508a61e..a734926a 100644 --- a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs +++ b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs @@ -36,4 +36,5 @@ public void Add(Setup setup) : null; } } + } diff --git a/src/NinjaTools.FluentMockServer.API/Logging/FileSystemLogItem.cs b/src/NinjaTools.FluentMockServer.API/Logging/FileSystemLogItem.cs index 227d9bb0..4acf1f1e 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/FileSystemLogItem.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/FileSystemLogItem.cs @@ -1,7 +1,7 @@ using System.Diagnostics; -using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.API.Logging.Models; -namespace NinjaTools.FluentMockServer.API.Services +namespace NinjaTools.FluentMockServer.API.Logging { [DebuggerDisplay("Type={LogType}; Path={Path}; Log={Log};")] public class FileSystemLogItem diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs b/src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs index f48f07dd..5169aa7f 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs @@ -1,9 +1,9 @@ using System; using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Logging.Models; using NinjaTools.FluentMockServer.API.Models; -using NinjaTools.FluentMockServer.API.Models.Logging; -namespace NinjaTools.FluentMockServer.API.Helper +namespace NinjaTools.FluentMockServer.API.Logging { public delegate string GenerateId(); diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs b/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs index 6084f5fa..4b7f0eec 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs @@ -2,10 +2,10 @@ using System.IO; using System.IO.Abstractions; using System.Linq; -using NinjaTools.FluentMockServer.API.Logging; -using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.API.Infrastructure; +using NinjaTools.FluentMockServer.API.Logging.Models; -namespace NinjaTools.FluentMockServer.API.Services +namespace NinjaTools.FluentMockServer.API.Logging { /// public class LogRepository : ILogRepository diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs b/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs index af86c959..450808b7 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs @@ -3,10 +3,11 @@ using System.Linq; using JetBrains.Annotations; using Microsoft.Extensions.Logging; -using NinjaTools.FluentMockServer.API.Helper; -using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.API.Infrastructure; +using NinjaTools.FluentMockServer.API.Logging.Models; +using NinjaTools.FluentMockServer.API.Services; -namespace NinjaTools.FluentMockServer.API.Services +namespace NinjaTools.FluentMockServer.API.Logging { internal sealed class LogService : ILogService { diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs index 0278c885..52a2efe3 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs @@ -1,6 +1,6 @@ using NinjaTools.FluentMockServer.API.Types; -namespace NinjaTools.FluentMockServer.API.Models.Logging +namespace NinjaTools.FluentMockServer.API.Logging.Models { public interface ILogItem : IIdentifable { diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs index b1150d1b..403057a6 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json.Converters; using NinjaTools.FluentMockServer.API.Extensions; -namespace NinjaTools.FluentMockServer.API.Models.Logging +namespace NinjaTools.FluentMockServer.API.Logging.Models { [DebuggerDisplay("{DebuggerDisplay(),nq}")] public abstract class LogItem : ILogItem diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs index ecee2f30..6aaccf45 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs @@ -2,7 +2,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace NinjaTools.FluentMockServer.API.Models.Logging +namespace NinjaTools.FluentMockServer.API.Logging.Models { [JsonConverter(typeof(StringEnumConverter))] public enum LogType diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs index 8b950dec..da1312c9 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs @@ -1,7 +1,8 @@ using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Models; using NinjaTools.FluentMockServer.API.Models.ViewModels; -namespace NinjaTools.FluentMockServer.API.Models.Logging +namespace NinjaTools.FluentMockServer.API.Logging.Models { public class RequestMatchedLog : LogItem { diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs index 15cd19ac..f3b580d8 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Http; using NinjaTools.FluentMockServer.API.Models.ViewModels; -namespace NinjaTools.FluentMockServer.API.Models.Logging +namespace NinjaTools.FluentMockServer.API.Logging.Models { public class RequestUnmatchedLog : LogItem { diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs index 1e809194..815a706d 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs @@ -1,4 +1,6 @@ -namespace NinjaTools.FluentMockServer.API.Models.Logging +using NinjaTools.FluentMockServer.API.Models; + +namespace NinjaTools.FluentMockServer.API.Logging.Models { public sealed class SetupLog : LogItem { diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs index 781fd103..72482f26 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs @@ -10,6 +10,7 @@ namespace NinjaTools.FluentMockServer.API.Models { + [DebuggerDisplay("{DebuggerDisplay()}")] public class RequestMatcher { @@ -55,7 +56,11 @@ public string Path } public string Method { get; set; } - public Dictionary Headers { get; set; } + + public Dictionary? Headers { get; set; } + public Dictionary? Cookies { get; set; } + + public string? QueryString { get; set; } public bool IsMatch(HttpContext context) @@ -147,7 +152,7 @@ public string DebuggerDisplay() } public string? Content { get; set; } - public RequestBodyType Type { get; set; } + public RequestBodyKind Type { get; set; } public bool MatchExact { get; set; } @@ -169,7 +174,7 @@ public bool IsMatch([NotNull] HttpRequest request) } [JsonConverter(typeof(StringEnumConverter))] - public enum RequestBodyType + public enum RequestBodyKind { Text, Base64 diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 117ae254..faf1a8b0 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -3,6 +3,7 @@ netcoreapp3.1 enable + CS8600;CS8602;CS8603 preview 1.0.0 1.0.0 @@ -18,6 +19,7 @@ + diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs new file mode 100644 index 00000000..12d3a88c --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs @@ -0,0 +1,45 @@ +using System; +using System.Diagnostics; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation +{ + [DebuggerDisplay("{IsMatch} {Ratings} | Messages={Messages}; Matcher={Matcher};", Name = "EvaluatingContext")] + public class EvaluationContext + { + public EvaluationContext(HttpContext httpContext, NormalizedMatcher matcher) + { + HttpContext = httpContext; + Matcher = matcher; + Messages = new EvaluationMessages(); + Ratings = new EvaluationScore(); + } + + public HttpContext HttpContext { get; } + public NormalizedMatcher Matcher { get; } + public EvaluationMessages Messages { get; } + public EvaluationScore Ratings { get; } + public bool IsMatch { get; private set; } = true; + + public void AddExtraPoints(uint bonus) => Ratings.BonusPoints += bonus; + + + /// + public void Matches(EvaluationWeight weight, string? message = null) + { + Ratings.Add(weight); + if (message != null) + { + Messages.Messages.Add(message); + } + } + + public void LogError(Exception exception) + { + IsMatch = false; + Messages.Exceptions.Add(exception); + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs new file mode 100644 index 00000000..1f6029f8 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs @@ -0,0 +1,79 @@ +using AutoMapper; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation +{ + public static class EvaluationPipeline + { + private static readonly IEvaluator Pipeline; + + static EvaluationPipeline() + { + var evaluator = new HttpRequestEvaluator(); + evaluator.SetNext(new HttpPathEvaluator()) + .SetNext(new HttpMethodEvaluator()) + .SetNext(new HttpBodyEvaluator()) + .SetNext(new HttpCookieEvaluator()) + .SetNext(new QueryStringEvaluator()) + .SetNext(new HttpHeaderEvaluator()); + + Pipeline = evaluator; + } + + public static IEvaluationResult Evaluate(HttpContext httpContext, RequestMatcher matcher) + { + var context = new EvaluationContext(httpContext, matcher.Normalize()); + var evalResult = Pipeline.Evaluate(context); + return evalResult; + } + + + + + + private static IMapper? _mapper; + public static IMapper Mapper + { + get + { + if (_mapper is null) + { + var config = new MapperConfiguration(cfg => cfg.AddProfile()); + _mapper = config.CreateMapper(); + } + + return _mapper; + } + } + + public static NormalizedMatcher Normalize(this RequestMatcher matcher) + { + var result = Mapper.Map(matcher); + return result; + } + + public class RequestMatcherProfile : Profile + { + public RequestMatcherProfile() + { + ShouldMapField = _ => true; + ShouldMapProperty = _ => true; + EnableNullPropagationForQueryMapping = true; + AllowNullCollections = true; + AllowNullDestinationValues = true; + + CreateMap() + .ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.BodyMatcher.Content)) + .ForMember(dest => dest.MatchExact, opt => opt.MapFrom(src => src.BodyMatcher.MatchExact)) + .ForMember(dest => dest.BodyKind, opt => opt.MapFrom(src => src.BodyMatcher.Type)) + .ForMember(dest => dest.Query, opt => opt.MapFrom(src => src.QueryString)) + .ReverseMap(); + } + + + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluationWeight.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluationWeight.cs new file mode 100644 index 00000000..a1a8ec14 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluationWeight.cs @@ -0,0 +1,10 @@ +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators +{ + public enum EvaluationWeight : ushort + { + Minimal = 1, + Low = 2, + High = 3, + Max = 4, + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs new file mode 100644 index 00000000..afeb93d4 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs @@ -0,0 +1,54 @@ +using System; +using System.Diagnostics; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators +{ + [DebuggerDisplay("{Name}; IsLast={IsLast};", Name = "Evaluator")] + public abstract class EvaluatorBase : IEvaluator + { + protected virtual string FormatMatched(string subjectName, object match) => $"Matched {subjectName}. Value={match};"; + + protected abstract void EvaluateMember(EvaluationContext context); + + + public virtual string Name => GetType().Name; + public bool IsLast => _next is null; + + private IEvaluator? _next; + + /// + public IEvaluator SetNext(IEvaluator next) + { + _next = next; + return next; + } + + /// + public virtual IEvaluationResult Evaluate(EvaluationContext context) + { + if (_next == null) + { + goto skip; + } + + try + { + EvaluateMember(context); + return _next.Evaluate(context); + } + catch (Exception e) + { + context.LogError(e); + } + + skip: + + return context switch + { + { IsMatch: true } ctx => new EvaluationSuccessfulResult(ctx), + { } ctx => new EvaluationUnsuccessfulResult(ctx) + }; + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs new file mode 100644 index 00000000..47ef57ba --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs @@ -0,0 +1,46 @@ +using System.IO; +using System.Reflection; +using Microsoft.AspNetCore.Http; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators +{ + public class HttpBodyEvaluator : EvaluatorBase + { + /// + protected override void EvaluateMember(EvaluationContext context) + { + if (context.Matcher.HasBody != true) + { + context.Matches(EvaluationWeight.Low); + return; + } + + var request = context.HttpContext.Request; + + var bodyKind = context.Matcher.BodyKind; + var shouldMatchExact = context.Matcher.MatchExact; + var bodyContent = context.Matcher.Content; + + + request.EnableBuffering(); + request.Body.Position = 0; + using var reader = new StreamReader(request.Body); + var content = reader.ReadToEnd(); + + if (shouldMatchExact && bodyContent == content) + { + context.Matches(EvaluationWeight.Max); + context.AddExtraPoints(3); + + } + else if (!shouldMatchExact && content.Length > 0 && bodyContent.Contains(content)) + { + context.Matches(EvaluationWeight.Max); + } + else + { + context.LogError(new AmbiguousMatchException($"{nameof(HttpRequest.Body)} '{content}' did not match. Expected={bodyContent};")); + } + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs new file mode 100644 index 00000000..0a3770f2 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs @@ -0,0 +1,35 @@ +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators +{ + public class HttpCookieEvaluator : EvaluatorBase + { + /// + protected override void EvaluateMember(EvaluationContext context) + { + var request = context.HttpContext.Request; + var requestCookies = request.Cookies; + var cookies = context.Matcher.Cookies; + + if (cookies is null) + { + context.Matches(EvaluationWeight.Low); + return; + } + + var unsatisfiedCookies = cookies.Except(requestCookies).ToList(); + + if (unsatisfiedCookies.Any() != true) + { + context.Matches(EvaluationWeight.High); + return; + } + + context.LogError(new AmbiguousMatchException($"{nameof(HttpRequest)} didn't contain all configured Headers. {unsatisfiedCookies.Count} Remaining={JObject.FromObject(cookies).ToString(Formatting.Indented)};")); + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs new file mode 100644 index 00000000..b8302ae7 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs @@ -0,0 +1,36 @@ +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators +{ + public class HttpHeaderEvaluator : EvaluatorBase + { + /// + protected override void EvaluateMember(EvaluationContext context) + { + var request = context.HttpContext.Request; + var requestHeaderDictionary = request.Headers; + var headers = context.Matcher.Headers; + + if (headers is null) + { + context.Matches(EvaluationWeight.Low); + return; + } + + var requestHeaders = requestHeaderDictionary.ToDictionary(key => key.Key, val => val.Value.ToArray()); + var unsatisfiedHeaders = headers.Except(requestHeaders).ToList(); + + if (!unsatisfiedHeaders.Any()) + { + context.Matches(EvaluationWeight.Max); + return; + } + + context.LogError(new AmbiguousMatchException($"{nameof(HttpRequest)} didn't contain all configured Headers. {unsatisfiedHeaders.Count} Remaining={JObject.FromObject(headers).ToString(Formatting.Indented)};")); + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs new file mode 100644 index 00000000..0b481168 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators +{ + public class HttpMethodEvaluator : EvaluatorBase + { + /// + protected override void EvaluateMember(EvaluationContext context) + { + var request = context.HttpContext.Request; + var method = context.Matcher.Method; + + if (method is null) + { + context.Matches(EvaluationWeight.Low, $"Matched {nameof(RequestMatcher.Method)}. Value={method};"); + } + else if (method == request.Method) + { + context.Matches(EvaluationWeight.Max, $"Matched {nameof(RequestMatcher.Method)}. Value={method};"); + } + else + { + context.LogError(new ValidationException($"{nameof(HttpRequest.Method)} '{request.Method}' did not match setup. Expected={method ?? "*"}")); + } + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs new file mode 100644 index 00000000..fd2cb6a9 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Extensions; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators +{ + public class HttpPathEvaluator : EvaluatorBase + { + + /// + protected override void EvaluateMember(EvaluationContext context) + { + var request = context.HttpContext.Request; + var requestPath = request.Path.Value; + var path = context.Matcher.Path; + + if (path is null) + { + context.Matches(EvaluationWeight.Low); + return; + } + + if (path.IsValidRegex() &&Regex.IsMatch(requestPath, path, RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace)) + { + context.Matches(EvaluationWeight.High); + return; + } + + context.LogError(new AmbiguousMatchException($"{nameof(HttpRequest.Path)}-Regex '{path}' did not match. Actual={requestPath};"));; + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs new file mode 100644 index 00000000..2632b8c5 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs @@ -0,0 +1,28 @@ +using System.Reflection; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators +{ + public class QueryStringEvaluator : EvaluatorBase + { + /// + protected override void EvaluateMember(EvaluationContext context) + { + var request = context.HttpContext.Request; + var requestQueryString = request.QueryString; + var query = context.Matcher.Query; + + if (query is null) + { + context.Matches(EvaluationWeight.Low); + return; + } + else if(query == request.QueryString.ToString()) + { + context.Matches(EvaluationWeight.Max); + return; + } + + context.LogError(new AmbiguousMatchException($"Actual QueryString '{requestQueryString.ToString()}' didn't match. Expected={query};")); + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/HttpRequestEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/HttpRequestEvaluator.cs new file mode 100644 index 00000000..004aaa3e --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/HttpRequestEvaluator.cs @@ -0,0 +1,22 @@ +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation +{ + public class HttpRequestEvaluator : IEvaluator + { + private IEvaluator _evaluator; + + /// + public IEvaluator SetNext(IEvaluator next) + { + _evaluator = next; + return next; + } + + /// + public IEvaluationResult Evaluate(EvaluationContext context) + { + return _evaluator.Evaluate(context); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/IEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/IEvaluator.cs new file mode 100644 index 00000000..8d151600 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/IEvaluator.cs @@ -0,0 +1,11 @@ +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation +{ + public interface IEvaluator + { + IEvaluator SetNext(IEvaluator next); + + IEvaluationResult Evaluate(EvaluationContext context); + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationMessages.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationMessages.cs new file mode 100644 index 00000000..557dfc84 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationMessages.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models +{ + [DebuggerDisplay("[M: {MessageCount} E: {ErrorCount} ] | Messages={Messages}; Errors={Exceptions};", Name = "EvaluatingMessages")] + public class EvaluationMessages + { + public int ErrorCount => Exceptions.Count; + public int MessageCount => Messages.Count; + public List Exceptions { get; } + public List Messages { get; } + + public EvaluationMessages() + { + Exceptions = new List(); + Messages = new List(); + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs new file mode 100644 index 00000000..fcd2b0d1 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models +{ + public abstract class EvaluationResultBase : IEvaluationResult + { + public EvaluationResultBase([NotNull] EvaluationContext context) + { + Messages = context.Messages.Messages; + Errors = context.Messages.Exceptions; + Score = context.Ratings.Score; + } + + /// + public abstract bool IsMatch { get; } + /// + public uint Score { get; } + + /// + public IReadOnlyList Messages { get; } + + /// + public IReadOnlyList Errors { get; } + + /// + public int ErrorCount => Errors.Count; + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationScore.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationScore.cs new file mode 100644 index 00000000..82964f58 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationScore.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models +{ + [DebuggerDisplay("[{Score}] | EvaluationScore={WeightedEvaluationPoints}; BonusPoints={BonusPoints};")] + public class EvaluationScore : List + { + public uint Score => WeightedEvaluationPoints + BonusPoints; + public uint WeightedEvaluationPoints => Convert.ToUInt32(this.ToList().Sum(r => r.Points * (ushort) r.Weight)); + public uint BonusPoints { get; internal set; } + + public void Add(EvaluationWeight weight, [CallerMemberName] string caller= null) + { + Add(new Rating(1, weight, caller)); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs new file mode 100644 index 00000000..1208d6f7 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs @@ -0,0 +1,17 @@ +using System.Diagnostics; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models +{ + /// + [DebuggerDisplay("IsMatch={IsMatch}; Score={Score}; Errors={ErrorCount}; Messages={Messages};")] + public class EvaluationSuccessfulResult : EvaluationResultBase + { + /// + public EvaluationSuccessfulResult(EvaluationContext context) : base(context) + { + } + + /// + public override bool IsMatch => true; + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationUnsuccessfulResult.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationUnsuccessfulResult.cs new file mode 100644 index 00000000..e71f99f3 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationUnsuccessfulResult.cs @@ -0,0 +1,13 @@ +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models +{ + public class EvaluationUnsuccessfulResult : EvaluationResultBase + { + /// + public override bool IsMatch => false; + + /// + public EvaluationUnsuccessfulResult(EvaluationContext context) : base(context) + { + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs new file mode 100644 index 00000000..1403d1da --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models +{ + public interface IEvaluationResult + { + uint Score { get; } + bool IsMatch { get; } + IReadOnlyList Messages { get; } + IReadOnlyList Errors { get; } + int ErrorCount { get; } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/NormalizedMatcher.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/NormalizedMatcher.cs new file mode 100644 index 00000000..e3f3336f --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/NormalizedMatcher.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using NinjaTools.FluentMockServer.API.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models +{ + public class NormalizedMatcher + { + public string? Method { get; private protected set; } + public string? Path { get; private protected set; } + public string? Query { get; private protected set; } + public Dictionary? Headers { get; private protected set; } + public Dictionary? Cookies { get; private protected set; } + + + public bool IsHttps { get; private protected set; } + + public bool HasBody => Content != null; + public string? Content { get; private protected set; } + public RequestBodyKind BodyKind { get; private protected set; } + public bool MatchExact { get; private protected set; } + } + +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs new file mode 100644 index 00000000..4a46ea9d --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs @@ -0,0 +1,21 @@ +using System.Diagnostics; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models +{ + [DebuggerDisplay("{PropertyName} {Score} | Points={Points}; Weight={Weight};", Name = "Rating")] + public struct Rating + { + public Rating(uint points, EvaluationWeight weight, string propertyName) + { + PropertyName = propertyName; + Points = points; + Weight = weight; + } + + public uint Points { get; } + public EvaluationWeight Weight { get; } + public uint Score => Points * (ushort) Weight; + public string PropertyName { get; } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs b/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs index 65df3227..cc59a945 100644 --- a/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs +++ b/src/NinjaTools.FluentMockServer.API/Services/ILogService.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; -using NinjaTools.FluentMockServer.API.Helper; -using NinjaTools.FluentMockServer.API.Models.Logging; +using NinjaTools.FluentMockServer.API.Logging; +using NinjaTools.FluentMockServer.API.Logging.Models; namespace NinjaTools.FluentMockServer.API.Services { diff --git a/src/NinjaTools.FluentMockServer.API/Types/ChainElement.cs b/src/NinjaTools.FluentMockServer.API/Types/ChainElement.cs new file mode 100644 index 00000000..577c34c3 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Types/ChainElement.cs @@ -0,0 +1,134 @@ +namespace NinjaTools.FluentMockServer.API.Types +{ + // + // public interface IChainElement + // { + // T As(T defaultValue) where T : class; + // } + // + // + // public class NullChainElement : IChainElement + // { + // private static IChainElement instance; + // private NullChainElement() { } + // + // public static IChainElement Instance = instance ??= new NullChainElement() + // + // + // /// + // public T As(T defaultValue) where T : class + // { + // return defaultValue; + // } + // } + // + // public interface IA + // { + // string Serialize(IConfigFile file); + // IConfigFile Deserializer(string filepath); + // } + // + // + // + // public class YamlSerializer : ConfigFileSerializerChainElement + // { + // private SerializerBuilder SerializerBuilder { get; set; } + // private Func BuildSerializer { get; set; } + // + // private DeserializerBuilder DeserializerBuilder { get; set; } + // private Func BuildDeserializer { get; set; } + // + // private void Initialize() + // { + // BuildSerializer = () => SerializerBuilder.Build(); + // SerializerBuilder = SerializerBuilder + // .WithMaximumRecursion(10) + // .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) + // .JsonCompatible(); + // + // BuildDeserializer = () => DeserializerBuilder.Build(); + // DeserializerBuilder = new DeserializerBuilder() + // .IgnoreUnmatchedProperties(); + // } + // + // /// + // public YamlSerializer(IFileSystem fileSystem, IChainElement next) : base(fileSystem, next) + // { + // Initialize(); + // } + // + // /// + // public YamlSerializer(IFileSystem fileSystem) : base(fileSystem) + // { + // Initialize(); + // } + // + // /// + // public override string Serialize(IConfigFile file) + // { + // var serializer = BuildSerializer(); + // return serializer.Serialize(file); + // } + // + // /// + // public override IConfigFile Deserializer(string filepath) + // { + // var deserializer = BuildDeserializer(); + // return deserializer.Deserialize(filepath); + // } + // } + // + // + // + // + // public abstract class ConfigFileSerializerChainElement : ChainElement, IA + // { + // private readonly IFileSystem _fileSystem; + // + // public ConfigFileSerializerChainElement(IFileSystem fileSystem, IChainElement next) + // :base(next) + // { + // _fileSystem = fileSystem; + // } + // + // public ConfigFileSerializerChainElement(IFileSystem fileSystem) + // { + // _fileSystem = fileSystem; + // } + // + // /// + // public abstract string Serialize(IConfigFile file); + // + // /// + // public abstract IConfigFile Deserializer(string filepath); + // } + // + // + // + // public class ChainElement : IChainElement + // { + // private readonly IChainElement? _next; + // + // public ChainElement(IChainElement next) + // { + // _next = next; + // } + // + // protected ChainElement() : this(NullChainElement.Instance) + // { + // } + // + // public virtual T As(T defaultValue) where T : class + // { + // if (this is T cast) + // return cast; + // + // return _next switch + // { + // { } => _next.As(defaultValue), + // _ => default + // }; + // } + // + // } +} diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs index cd479fc9..d6ae1252 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs @@ -1,9 +1,9 @@ using System; using Moq; -using NinjaTools.FluentMockServer.API.Helper; using NinjaTools.FluentMockServer.API.Infrastructure; +using NinjaTools.FluentMockServer.API.Logging; +using NinjaTools.FluentMockServer.API.Logging.Models; using NinjaTools.FluentMockServer.API.Models; -using NinjaTools.FluentMockServer.API.Models.Logging; using NinjaTools.FluentMockServer.API.Services; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs index 860112a0..d7e59fb3 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs @@ -1,8 +1,8 @@ using FluentAssertions; using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Helper; +using NinjaTools.FluentMockServer.API.Logging; +using NinjaTools.FluentMockServer.API.Logging.Models; using NinjaTools.FluentMockServer.API.Models; -using NinjaTools.FluentMockServer.API.Models.Logging; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; using Xunit.Abstractions; diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestBodyMatcherTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestBodyMatcherTests.cs index b190eb12..c6fbd2e4 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestBodyMatcherTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestBodyMatcherTests.cs @@ -43,7 +43,7 @@ private HttpContext CreateContext([CanBeNull] string textContent = null, byte[] } [NotNull] - private RequestBodyMatcher CreateSubject(RequestBodyType type, bool matchExact, string content) + private RequestBodyMatcher CreateSubject(RequestBodyKind type, bool matchExact, string content) { return new RequestBodyMatcher { @@ -58,7 +58,7 @@ public void Should_Match_Exact_String_Body() { // Arrange const string content = "{\"hello\":\"world!\"}"; - var subject = CreateSubject(RequestBodyType.Text, true, content); + var subject = CreateSubject(RequestBodyKind.Text, true, content); var context = CreateContext(content); // Act & Assert @@ -70,7 +70,7 @@ public void Should_Match_String_Body_If_Contains_String() { // Arrange const string content = "{\"hello\":\"world!\"}"; - var subject = CreateSubject(RequestBodyType.Text, false, "world!\"}"); + var subject = CreateSubject(RequestBodyKind.Text, false, "world!\"}"); var context = CreateContext(content); // Act & Assert @@ -81,7 +81,7 @@ public void Should_Match_String_Body_If_Contains_String() public void Should_Not_Match_Exact_String_Body_When_Not_Exact_Same_String_Content() { // Arrange - var subject = CreateSubject(RequestBodyType.Text, true, "{\"hello\":\"car!\"}"); + var subject = CreateSubject(RequestBodyKind.Text, true, "{\"hello\":\"car!\"}"); var context = CreateContext("{\"hello\":\"world!\"}"); // Act & Assert @@ -93,7 +93,7 @@ public void Should_Not_Match_String_Body_If_Not_Contains_String() { // Arrange const string content = "{\"hello\":\"world!\"}"; - var subject = CreateSubject(RequestBodyType.Text, false, "car!\"}"); + var subject = CreateSubject(RequestBodyKind.Text, false, "car!\"}"); var context = CreateContext(content); // Act & Assert diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs index 0f71458e..5c887b76 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs @@ -12,31 +12,31 @@ namespace NinjaTools.FluentMockServer.API.Tests.Models public class RequestMatcherTests { [Theory] - [InlineData("GET")] - [InlineData("POST")] - [InlineData("PUT")] - [InlineData("OPTIONS")] - public void When_IsMatch_Returns_True_When_Method_Is_Equal(string method) + [InlineData("GET")] + [InlineData("POST")] + [InlineData("PUT")] + [InlineData("OPTIONS")] + public void When_IsMatch_Returns_True_When_Method_Is_Equal(string method) { // Arrange var subject = CreateSubject(method: method); var context = CreateContext(method); - + // Act& Assert subject.IsMatch(context).Should().BeTrue(); } [Theory] - [InlineData("/some(abc/path")] - [InlineData("/some/abc/path")] - [InlineData("/some/abc/path?query=json")] - [InlineData("/opt")] - public void When_IsMatch_Returns_True_When_Path_Is_Match(string path) + [InlineData("/some(abc/path")] + [InlineData("/some/abc/path")] + [InlineData("/some/abc/path?query=json")] + [InlineData("/opt")] + public void When_IsMatch_Returns_True_When_Path_Is_Match(string path) { // Arrange var subject = CreateSubject(path: path); var context = CreateContext(path: path); - + // Act& Assert subject.IsMatch(context).Should().BeTrue(); } @@ -58,7 +58,7 @@ public static IEnumerable GetHeaderTestData() { yield return new object[] { - new Dictionary(), + new Dictionary(), new Dictionary(), true }; @@ -126,33 +126,35 @@ private HttpContext CreateContext(string method = null, string path = null, Dict } }; - var request = context.Request; - request.Method = method; - request.Path = new PathString(path); - - if (queryString != null) - { - request.QueryString = queryString.Value; - } - foreach (var header in headers ?? new Dictionary()) - { - request.Headers.Add(header.Key, header.Value); - } - - return context; - } - - private RequestMatcher CreateSubject(string method = null, string path = null, IDictionary headers = null, RequestBodyMatcher bodyMatcher = null, QueryString? queryString = null) - { - return new RequestMatcher - { - Method = method, - Path = path, - Headers = new Dictionary (headers ?? new Dictionary()), - BodyMatcher = bodyMatcher, - QueryString = queryString?.Value - }; - } + var request = context.Request; + request.Method = method; + request.Path = new PathString(path); + + if (queryString != null) + { + request.QueryString = queryString.Value; + } + + foreach (var header in headers ?? new Dictionary()) + { + request.Headers.Add(header.Key, header.Value); + } + + return context; + } + + private RequestMatcher CreateSubject(string method = null, string path = null, IDictionary headers = null, RequestBodyMatcher bodyMatcher = null, + QueryString? queryString = null) + { + return new RequestMatcher + { + Method = method, + Path = path, + Headers = new Dictionary(headers ?? new Dictionary()), + BodyMatcher = bodyMatcher, + QueryString = queryString?.Value + }; + } [Fact] public void IsMatch_Should_Be_False_When_QueryString_Not_Same() @@ -160,7 +162,7 @@ public void IsMatch_Should_Be_False_When_QueryString_Not_Same() // Arrange var subject = CreateSubject(queryString: new QueryString("?color=green")); var context = CreateContext(queryString: new QueryString("?id=100")); - + // Act & Assert subject.IsMatch(context).Should().BeFalse(); } @@ -176,13 +178,13 @@ public void IsMatch_Should_Be_False_When_RequestBodyMatcher_IsNotMatch() var bodyMatcher = new RequestBodyMatcher { Content = "hello world", - Type = RequestBodyType.Text, + Type = RequestBodyKind.Text, MatchExact = true }; - - var subject = CreateSubject(bodyMatcher: bodyMatcher ); + + var subject = CreateSubject(bodyMatcher: bodyMatcher); var context = CreateContext(requestBodyStream: requestBodyStream); - + // Act & Assert subject.IsMatch(context).Should().BeFalse(); } @@ -194,7 +196,7 @@ public void IsMatch_Should_Be_True_When_No_RequestBodyMatcher() // Arrange var subject = CreateSubject(); var context = CreateContext(); - + // Act & Assert subject.IsMatch(context).Should().BeTrue(); } @@ -206,7 +208,7 @@ public void IsMatch_Should_Be_True_When_QueryString_IsMatch() var query = new QueryString("?id=100"); var subject = CreateSubject(queryString: query); var context = CreateContext(queryString: query); - + // Act & Assert subject.IsMatch(context).Should().BeTrue(); } @@ -218,7 +220,7 @@ public void IsMatch_Should_Be_True_When_QueryString_Not_Set() var query = new QueryString("?id=100"); var subject = CreateSubject(); var context = CreateContext(queryString: query); - + // Act & Assert subject.IsMatch(context).Should().BeTrue(); } @@ -234,14 +236,14 @@ public void IsMatch_Should_Be_True_When_RequestBodyMatcher_IsMatch() requestBodyStream.Write(bytes); var bodyMatcher = new RequestBodyMatcher { - Content = requestBodyContent, - Type = RequestBodyType.Text, - MatchExact = true + Content = requestBodyContent, + Type = RequestBodyKind.Text, + MatchExact = true }; - - var subject = CreateSubject(bodyMatcher: bodyMatcher ); + + var subject = CreateSubject(bodyMatcher: bodyMatcher); var context = CreateContext(requestBodyStream: requestBodyStream); - + // Act & Assert subject.IsMatch(context).Should().BeTrue(); } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs new file mode 100644 index 00000000..d1686ab3 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using FluentAssertions; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Moq; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.API.Tests.Proxy.Matchers +{ + + public class EvaluatorTestData : TheoryData + { + + protected static HttpContext GetContext(Action setup) + { + var context = new DefaultHttpContext(); + setup(context.Request); + return context; + } + + protected IEvaluationResult Eval(int score, [CanBeNull] EvaluationMessages messages = null) + { + messages ??= new EvaluationMessages(); + return Mock.Of(e => e.Score == score + && e.IsMatch == false + && e.Messages == messages.Messages + && e.Errors == messages.Exceptions); + } + + + + + public class ValidRequestMatcherTestData : EvaluatorTestData + { + public ValidRequestMatcherTestData() + { + Data1(); + } + + private void Data1() + { + // *should* match anything + var matcher = new RequestMatcher(); + var context = GetContext(r => + { + r.Method = HttpMethods.Delete; + r.Path = "/cars/buy/id/200"; + r.ContentType = "application/json"; + }); + var eval = Eval(10); + Add(matcher, context, eval); + } + } + + + public class InvalidRequestMatcherTestData : EvaluatorTestData + { + public InvalidRequestMatcherTestData() + { + Data1(); + Data2(); + } + + private void Data1() + { + var reqA = new RequestMatcher + { + Path = "/some/path", + Cookies = new Dictionary + { + {"abcd", "application/json"} + }, + QueryString = "?id=0", + Method = "GET", + Headers = new Dictionary + { + { "a", new [] {""}} + }, + BodyMatcher = new RequestBodyMatcher + { + Content = "?" + } + }; + var evalA = Eval(0); + var ctxA = GetContext(r => + { + r.Method = HttpMethods.Delete; + + }); + Add(reqA, ctxA, evalA); + } + + private void Data2() + { + var reqA = new RequestMatcher + { + Path = "/x&/y(", + QueryString = "?id=123" + }; + var evalA = Eval(6); + var ctxA = GetContext(r => { }); + + Add(reqA, ctxA, evalA); + } + } + } + + + + public class EvaluatorTests: XUnitTestBase + { + /// + public EvaluatorTests(ITestOutputHelper output) : base(output) + { } + + [Theory] + [ClassData(typeof(EvaluatorTestData.InvalidRequestMatcherTestData))] + public void Should_Return_EvaluationUnsuccessfulResult_When_Not_Matching(RequestMatcher matcher, HttpContext context, IEvaluationResult expectedResult) + { + // Act && Assert + + var result = EvaluationPipeline.Evaluate(context, matcher); + Dump(matcher, "matcher"); + result.Should().BeOfType(); + result.IsMatch.Should().BeFalse(); + result.Score.Should().Be(expectedResult.Score); + } + + [Theory] + [ClassData(typeof(EvaluatorTestData.ValidRequestMatcherTestData))] + public void Should_Return_EvaluationSuccessfulResult_When_Matching(RequestMatcher matcher, HttpContext context, IEvaluationResult expectedResult) + { + // Act && Assert + var result = EvaluationPipeline.Evaluate(context, matcher); + Dump(matcher, "matcher"); + result.Should().BeOfType(); + result.IsMatch.Should().Be(true); + result.Score.Should().Be(expectedResult.Score); + } + + + [Theory] + [MemberData(nameof(GetScoreTestData))] + public void Score_Should_Be_Higher_For_The_Matcher_Who_Is_Closer_To_The_HttpRequest(RequestMatcher more, HttpContext context, RequestMatcher less) + { + // Act + var a = EvaluationPipeline.Evaluate(context, more); + Dump(a, "more"); + + var b = EvaluationPipeline.Evaluate(context, less); + Dump(b, "less"); + + // Assert + a.Score.Should().BeGreaterOrEqualTo(b.Score); + } + + public static IEnumerable GetScoreTestData() + { + yield return Build(req => + { + req.Path = "/a/b"; + req.Method = "POST"; + }, + less => less.Path = "z", + ctx => + { + ctx.Request.Path = PathString.FromUriComponent("/a/b"); + ctx.Request.Method = "POST"; + }); + + yield return Build(req => + { + req.Path = "/a/b"; + req.Method = "POST"; + req.BodyMatcher = new RequestBodyMatcher + { + Content = "?", + MatchExact = true + }; + }, + less => + { + less.Path = "/some/path"; + less.Cookies = new Dictionary + { + {"abcd", "application/json"} + }; + less.QueryString = "?id=0"; + less.Method = "GET"; + less.Headers = new Dictionary + { + {"a", new[] {""}} + }; + }, + ctx => + { + ctx.Request.Cookies.Append(new KeyValuePair("abcd", "application/json")); + ctx.Request.Path = PathString.FromUriComponent("/a/b"); + ctx.Request.Method = "POST"; + }); + + yield return Build(req => + { + req.BodyMatcher = new RequestBodyMatcher + { + Content = "hello world", + MatchExact = true, + Type = RequestBodyKind.Text + }; + }, + less => less.Path = "/a/b/*", + ctx => + { + ctx.Request.Path = PathString.FromUriComponent("/a/b"); + ctx.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("hello world")); + }); + + yield return Build(req => req.Path = "/a/b/*", + less => less.Path = "/a/b", + ctx => + { + ctx.Request.Path = PathString.FromUriComponent("/a/b"); + ctx.Request.Method = "POST"; + ctx.Request.Cookies.Append(new KeyValuePair("abcd", "application/json")); + ctx.Request.Headers.Add("a", new[] {""}); + }); + } + + + + + + + + private static object[] Build(Action factory, Action lessAction, Action ctxFac) + { + var ctx = new DefaultHttpContext(); + ctxFac(ctx); + var more = new RequestMatcher(); + var less = new RequestMatcher(); + factory(more); + factory(less); + lessAction(less); + return new object[] {more, ctx, less }; + } + } +} diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs index 457178e4..82bb5172 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs @@ -2,9 +2,10 @@ using FluentAssertions; using Microsoft.AspNetCore.Http; using Moq; -using NinjaTools.FluentMockServer.API.Helper; +using NinjaTools.FluentMockServer.API.Infrastructure; +using NinjaTools.FluentMockServer.API.Logging; +using NinjaTools.FluentMockServer.API.Logging.Models; using NinjaTools.FluentMockServer.API.Models; -using NinjaTools.FluentMockServer.API.Models.Logging; using NinjaTools.FluentMockServer.API.Services; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; From 174f13ba00194bdac37266a457941ec4e9f33224 Mon Sep 17 00:00:00 2001 From: alex held Date: Sun, 12 Jan 2020 17:21:44 +0100 Subject: [PATCH 21/64] !SQUASH refactor(mock-server): use visitors to traverse and evaluate RequestMatcher --- .../.idea/contentModel.xml | 14 +- .../.idea/sonarIssues.xml | 50 +++++++ .../Extensions/EvaluationExtensions.cs | 55 +++++++ .../Models/RequestBodyMatcher.cs | 46 ++++++ .../Models/RequestMatcher.cs | 54 +++---- .../Models/ResponseAction.cs | 6 +- .../NinjaTools.FluentMockServer.API.csproj | 1 + .../Proxy/Evaluation/EvaluationContext.cs | 30 +--- .../Proxy/Evaluation/EvaluationPipeline.cs | 2 +- .../Evaluation/Models/EvaluationRatings.cs | 14 ++ .../Evaluation/Models/EvaluationResultBase.cs | 12 +- .../Evaluation/Models/EvaluationScore.cs | 22 --- .../Models/EvaluationSuccessfulResult.cs | 2 +- .../EvaluationWeight.cs | 3 +- .../Evaluation/Models/IEvaluationResult.cs | 4 +- .../Proxy/Evaluation/Models/Rating.cs | 12 +- .../Evaluation/Visitors/IPartialVisitor.cs | 17 +++ .../Proxy/Evaluation/Visitors/IRequest.cs | 10 ++ .../Proxy/Evaluation/Visitors/IRequestBody.cs | 9 ++ .../IRequestMatcherEvaluatorVistor.cs | 9 ++ .../Proxy/Evaluation/Visitors/IVisitor.cs | 11 ++ .../Visitors/RequestEvaluatorVistor.cs | 138 ++++++++++++++++++ .../Models/RequestMatcherTests.cs | 2 +- .../Proxy/Matchers/EvaluatorTests.cs | 6 +- 24 files changed, 423 insertions(+), 106 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationRatings.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationScore.cs rename src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/{Evaluators => Models}/EvaluationWeight.cs (91%) create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequest.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestBody.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestMatcherEvaluatorVistor.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index a22bddfb..1e894b6a 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -130,6 +130,7 @@ + @@ -153,6 +154,7 @@ + @@ -178,7 +180,6 @@ - @@ -191,14 +192,23 @@ + - + + + + + + + + + diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml index 755b0ffa..fc244ecb 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml @@ -523,6 +523,11 @@ + + + + + @@ -678,6 +683,11 @@ + + + + + @@ -713,6 +723,11 @@ + + + + + @@ -723,6 +738,16 @@ + + + + + + + + + + @@ -743,6 +768,16 @@ + + + + + + + + + + @@ -778,6 +813,21 @@ + + + + + + + + + + + + + + + diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs new file mode 100644 index 00000000..65e07b30 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs @@ -0,0 +1,55 @@ +using System.Linq; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; + +namespace NinjaTools.FluentMockServer.API.Extensions +{ + public static class EvaluationExtensions + { + public static void AddExtraPoints(this EvaluationContext context, int bonus) => context.Ratings.BonusPoints += bonus; + + + public static THttpRequestMember? EnsureNotNull(this EvaluationContext context,THttpRequestMember? httpRequestMember, TMockMember? item) + where TMockMember : class + where THttpRequestMember : class + { + if (item is null && httpRequestMember is null) + { + var rating = new Rating(1, EvaluationWeight.Low) + { + HttpRequestMember = new { Caller = typeof(THttpRequestMember).Name, Value =""}, + Member = new { Caller = typeof(TMockMember).Name, Value =""} + }; + + context.Ratings.Append(rating); + return null; + } + + return httpRequestMember; + } + + public static void Match(this EvaluationContext context, T? httpRequestMember, object? matcherMember)where T : class + { + var rating = new Rating(1, EvaluationWeight.Low) + { + HttpRequestMember = httpRequestMember, + Member = matcherMember + }; + + context.Ratings.Append(rating); + } + + + public static void Fail(this EvaluationContext context, T? httpRequestMember, object? matcherMember) where T : class + { + var rating = new Rating(0, EvaluationWeight.Non) + { + HttpRequestMember = httpRequestMember, + Member = matcherMember + }; + + context.Ratings.Append(rating); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs new file mode 100644 index 00000000..a064155f --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs @@ -0,0 +1,46 @@ +using System; +using System.Diagnostics; +using System.IO; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; + +namespace NinjaTools.FluentMockServer.API.Models +{ + [DebuggerDisplay("{DebuggerDisplay()}")] + public class RequestBodyMatcher : IRequestBody + { + public string DebuggerDisplay() + { + return $"Type={Type.ToString()}; MatchExact={MatchExact.ToString()}; Content={Content ?? ""}"; + } + + public string? Content { get; set; } + public RequestBodyKind Type { get; set; } + public bool MatchExact { get; set; } + + + public bool IsMatch([NotNull] HttpRequest request) + { + request.EnableBuffering(); + request.Body.Position = 0; + using var reader = new StreamReader(request.Body); + var content = reader.ReadToEnd(); + + if (MatchExact) + { + return Content == content; + } + + return content.Contains(Content); + } + + + /// + public void Accept(Func visitorFactory) + { + var visitor = visitorFactory(); + visitor.VisitBody(Content, MatchExact, Type); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs index 72482f26..a9423ead 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs @@ -1,18 +1,18 @@ +using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Linq; using System.Text; -using JetBrains.Annotations; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; namespace NinjaTools.FluentMockServer.API.Models { [DebuggerDisplay("{DebuggerDisplay()}")] - public class RequestMatcher + public class RequestMatcher : IRequest { public string DebuggerDisplay() { @@ -28,9 +28,9 @@ public string DebuggerDisplay() sb.Append($"Path={Path}; "); } - if (!string.IsNullOrEmpty(QueryString)) + if (!string.IsNullOrEmpty(Query)) { - sb.Append($"Query={QueryString}; "); + sb.Append($"Query={Query}; "); } sb.Append($"Headers={Headers?.Count ?? 0};\n"); @@ -61,7 +61,7 @@ public string Path public Dictionary? Cookies { get; set; } - public string? QueryString { get; set; } + public string? Query { get; set; } public bool IsMatch(HttpContext context) { @@ -75,9 +75,9 @@ public bool IsMatch(HttpContext context) private bool QueryMatches(QueryString requestQueryString) { - if (!string.IsNullOrWhiteSpace(QueryString)) + if (!string.IsNullOrWhiteSpace(Query)) { - return requestQueryString.Value == QueryString; + return requestQueryString.Value == Query; } return true; @@ -141,35 +141,25 @@ private bool HeadersMatching(IHeaderDictionary requestHeaders) return true; } - } - [DebuggerDisplay("{DebuggerDisplay()}")] - public class RequestBodyMatcher - { - public string DebuggerDisplay() + /// + public void Accept(Func visitorFactory) { - return $"Type={Type.ToString()}; MatchExact={MatchExact.ToString()}; Content={Content ?? ""}"; + var visitor = visitorFactory(); + visitor.VisitHeaders(Headers); + visitor.VisitMethod(Method); + visitor.VisitPath(Path); + visitor.VisitQuery(Query); + visitor.VisitCookies(Cookies); + visitor.VisitBody(BodyMatcher); } - public string? Content { get; set; } - public RequestBodyKind Type { get; set; } - public bool MatchExact { get; set; } - - - public bool IsMatch([NotNull] HttpRequest request) + /// + public T Accept(Func> visitorFactory) { - request.EnableBuffering(); - request.Body.Position = 0; - using var reader = new StreamReader(request.Body); - var content = reader.ReadToEnd(); - - if (MatchExact) - { - return Content == content; - } - - - return content.Contains(Content); + var visitor = visitorFactory(); + var result = visitor.Evaluate(); + return result; } } diff --git a/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs b/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs index d4be939d..21709ebe 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs @@ -16,11 +16,7 @@ public string DebuggerDisplay() [DebuggerDisplay("{DebuggerDisplay()}")] public class HttpResponse { - public string DebuggerDisplay() - { - return $"Status={StatusCode}; Body={Body}"; - } - + public string DebuggerDisplay() => $"Status={StatusCode}; Body={Body}"; public int StatusCode { get; set; } public string Body { get; set; } } diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index faf1a8b0..4d2e6fc4 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -23,6 +23,7 @@ + diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs index 12d3a88c..730d1bc1 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs @@ -1,7 +1,5 @@ -using System; using System.Diagnostics; using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation @@ -14,32 +12,14 @@ public EvaluationContext(HttpContext httpContext, NormalizedMatcher matcher) HttpContext = httpContext; Matcher = matcher; Messages = new EvaluationMessages(); - Ratings = new EvaluationScore(); + Ratings = new EvaluationRatings(); + IsMatch = true; } public HttpContext HttpContext { get; } public NormalizedMatcher Matcher { get; } public EvaluationMessages Messages { get; } - public EvaluationScore Ratings { get; } - public bool IsMatch { get; private set; } = true; - - public void AddExtraPoints(uint bonus) => Ratings.BonusPoints += bonus; - - - /// - public void Matches(EvaluationWeight weight, string? message = null) - { - Ratings.Add(weight); - if (message != null) - { - Messages.Messages.Add(message); - } - } - - public void LogError(Exception exception) - { - IsMatch = false; - Messages.Exceptions.Add(exception); - } + public EvaluationRatings Ratings { get; } + public bool IsMatch { get; internal set; } } -} \ No newline at end of file +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs index 1f6029f8..cde51250 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs @@ -69,7 +69,7 @@ public RequestMatcherProfile() .ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.BodyMatcher.Content)) .ForMember(dest => dest.MatchExact, opt => opt.MapFrom(src => src.BodyMatcher.MatchExact)) .ForMember(dest => dest.BodyKind, opt => opt.MapFrom(src => src.BodyMatcher.Type)) - .ForMember(dest => dest.Query, opt => opt.MapFrom(src => src.QueryString)) + .ForMember(dest => dest.Query, opt => opt.MapFrom(src => src.Query)) .ReverseMap(); } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationRatings.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationRatings.cs new file mode 100644 index 00000000..ff07c5ab --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationRatings.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models +{ + [DebuggerDisplay("[{Score}] | EvaluationScore={WeightedEvaluationPoints}; BonusPoints={BonusPoints};")] + public class EvaluationRatings : List + { + public long Score => WeightedEvaluationPoints + BonusPoints; + public long WeightedEvaluationPoints => this.Sum(rating => rating.Score); + public int BonusPoints { get; internal set; } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs index fcd2b0d1..1308164a 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs @@ -10,21 +10,21 @@ public EvaluationResultBase([NotNull] EvaluationContext context) { Messages = context.Messages.Messages; Errors = context.Messages.Exceptions; - Score = context.Ratings.Score; + Ratings = context.Ratings; } /// public abstract bool IsMatch { get; } + /// - public uint Score { get; } + public EvaluationRatings Ratings { get; } + + public long Score => Ratings.Score; /// public IReadOnlyList Messages { get; } /// public IReadOnlyList Errors { get; } - - /// - public int ErrorCount => Errors.Count; } -} \ No newline at end of file +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationScore.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationScore.cs deleted file mode 100644 index 82964f58..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationScore.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.CompilerServices; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models -{ - [DebuggerDisplay("[{Score}] | EvaluationScore={WeightedEvaluationPoints}; BonusPoints={BonusPoints};")] - public class EvaluationScore : List - { - public uint Score => WeightedEvaluationPoints + BonusPoints; - public uint WeightedEvaluationPoints => Convert.ToUInt32(this.ToList().Sum(r => r.Points * (ushort) r.Weight)); - public uint BonusPoints { get; internal set; } - - public void Add(EvaluationWeight weight, [CallerMemberName] string caller= null) - { - Add(new Rating(1, weight, caller)); - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs index 1208d6f7..40bb4413 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs @@ -3,7 +3,7 @@ namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models { /// - [DebuggerDisplay("IsMatch={IsMatch}; Score={Score}; Errors={ErrorCount}; Messages={Messages};")] + [DebuggerDisplay("IsMatch={IsMatch}; Score={Ratings}; Errors={ErrorCount}; Messages={Messages};")] public class EvaluationSuccessfulResult : EvaluationResultBase { /// diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluationWeight.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationWeight.cs similarity index 91% rename from src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluationWeight.cs rename to src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationWeight.cs index a1a8ec14..aa5b5184 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluationWeight.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationWeight.cs @@ -2,9 +2,10 @@ namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators { public enum EvaluationWeight : ushort { + Non = 0, Minimal = 1, Low = 2, High = 3, Max = 4, } -} \ No newline at end of file +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs index 1403d1da..d16ce0a9 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs @@ -5,10 +5,10 @@ namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models { public interface IEvaluationResult { - uint Score { get; } + long Score { get; } bool IsMatch { get; } IReadOnlyList Messages { get; } IReadOnlyList Errors { get; } - int ErrorCount { get; } + int ErrorCount => Errors.Count; } } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs index 4a46ea9d..e1aa0bc0 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs @@ -3,12 +3,11 @@ namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models { - [DebuggerDisplay("{PropertyName} {Score} | Points={Points}; Weight={Weight};", Name = "Rating")] - public struct Rating + [DebuggerDisplay("{HttpRequestMember} {Score} | Points={Points}; Weight={Weight};", Name = "Rating")] + public class Rating { - public Rating(uint points, EvaluationWeight weight, string propertyName) + public Rating(uint points, EvaluationWeight weight) { - PropertyName = propertyName; Points = points; Weight = weight; } @@ -16,6 +15,9 @@ public Rating(uint points, EvaluationWeight weight, string propertyName) public uint Points { get; } public EvaluationWeight Weight { get; } public uint Score => Points * (ushort) Weight; - public string PropertyName { get; } + + + public object? HttpRequestMember { get; set; } + public object? Member { get; set; } } } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs new file mode 100644 index 00000000..174d9cc4 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using NinjaTools.FluentMockServer.API.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors +{ + public interface IPartialVisitor : IVisitor + { + void VisitCookies(IDictionary? cookies); + void VisitHeaders(IDictionary? headers); + void VisitPath(string? path); + void VisitMethod(string? method); + void VisitQuery(string? query); + + void VisitBody(RequestBodyMatcher? bodyMatcher) => VisitBody(bodyMatcher.Content, bodyMatcher.MatchExact, bodyMatcher.Type); + void VisitBody(string? requestBody, bool exactMatch, RequestBodyKind kind); + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequest.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequest.cs new file mode 100644 index 00000000..f10c47d4 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequest.cs @@ -0,0 +1,10 @@ +using System; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors +{ + public interface IRequest + { + void Accept(Func visitorFactory); + T Accept(Func> visitorFactory); + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestBody.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestBody.cs new file mode 100644 index 00000000..3fc7cd06 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestBody.cs @@ -0,0 +1,9 @@ +using System; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors +{ + public interface IRequestBody + { + void Accept(Func visitorFactory); + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestMatcherEvaluatorVistor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestMatcherEvaluatorVistor.cs new file mode 100644 index 00000000..0cc2bef0 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestMatcherEvaluatorVistor.cs @@ -0,0 +1,9 @@ +using NinjaTools.FluentMockServer.API.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors +{ + public interface IRequestMatcherEvaluatorVistor : IPartialVisitor + { + void Visit(RequestMatcher matcher); + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs new file mode 100644 index 00000000..c1256598 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs @@ -0,0 +1,11 @@ +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors +{ + public interface IRequestMatcherEvaluatorVistor + { + T Evaluate(); + } + + public interface IVisitor + { + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs new file mode 100644 index 00000000..45cb123b --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs @@ -0,0 +1,138 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Extensions; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; + +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors +{ + public class RequestEvaluatorVistor : IRequestMatcherEvaluatorVistor + { + + private readonly EvaluationContext _evaluationContext; + private HttpRequest HttpRequest => _evaluationContext.HttpContext.Request; + + public RequestEvaluatorVistor(EvaluationContext evaluationContext) + { + _evaluationContext = evaluationContext; + } + + /// + public IEvaluationResult Evaluate() + { + return _evaluationContext switch + { + { IsMatch: true } ctx => new EvaluationSuccessfulResult(ctx), + { } ctx => new EvaluationUnsuccessfulResult(ctx) + }; + } + + /// + public void VisitCookies(IDictionary? cookies) + { + if (_evaluationContext.EnsureNotNull(HttpRequest.Cookies, cookies) is {} httpCookies) + { + if (cookies.Except(httpCookies).Any()) + { + _evaluationContext.Fail(httpCookies, cookies); + } + else + { + _evaluationContext.Match(httpCookies, cookies); + } + } + } + + + + /// + public void VisitHeaders(IDictionary? headers) + { + if (_evaluationContext.EnsureNotNull(HttpRequest.Headers, headers) is {} httpRequestMember) + { + if (headers.Except(httpRequestMember.ToDictionary(k => k.Key, v => v.Value.ToArray())).Any()) + { + _evaluationContext.Fail(httpRequestMember, headers); + } + else + { + _evaluationContext.Match(httpRequestMember, headers); + } + } + } + + /// + public void VisitPath(string? path) + { + if (_evaluationContext.EnsureNotNull(HttpRequest.Path.Value, path) is {} httpPath) + { + if(httpPath != path) + { + _evaluationContext.Fail(httpPath, path); + } + else + { + _evaluationContext.Match(httpPath, path); + } + } + } + + /// + public void VisitMethod(string? method) + { + if (_evaluationContext.EnsureNotNull(HttpRequest.Method, method) is {} httpMethod) + { + if (httpMethod != method) + { + _evaluationContext.Fail(httpMethod, method); + } + else + { + _evaluationContext.Match(httpMethod, method); + } + } + } + + /// + public void VisitQuery(string? query) + { + if (_evaluationContext.EnsureNotNull(HttpRequest.QueryString.Value, query) is {} httpQuery) + { + if (httpQuery != query) + { + _evaluationContext.Fail(httpQuery, query); + } + else + { + _evaluationContext.Match(httpQuery, query); + } + } + } + + /// + public void VisitBody(string? requestBody, bool exactMatch, RequestBodyKind kind) + { + HttpRequest.EnableBuffering(); + if (_evaluationContext.EnsureNotNull(HttpRequest.Body, requestBody) is { } httpBodyStream && httpBodyStream.CanSeek) + { + httpBodyStream.Seek(0, SeekOrigin.Begin); + using var reader = new StreamReader(HttpRequest.Body); + var httpBody = reader.ReadToEnd(); + + if (requestBody is { } body && HttpRequest.Body.CanSeek) + { + if (exactMatch && httpBody == body) + { + _evaluationContext.Match(httpBody, body); + } + else + { + _evaluationContext.Fail(httpBody, body); + } + } + } + } + } +} diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs index 5c887b76..a6ef8c1b 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs @@ -152,7 +152,7 @@ private RequestMatcher CreateSubject(string method = null, string path = null, I Path = path, Headers = new Dictionary(headers ?? new Dictionary()), BodyMatcher = bodyMatcher, - QueryString = queryString?.Value + Query = queryString?.Value }; } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs index d1686ab3..ad3e7f3f 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs @@ -79,7 +79,7 @@ private void Data1() { {"abcd", "application/json"} }, - QueryString = "?id=0", + Query = "?id=0", Method = "GET", Headers = new Dictionary { @@ -104,7 +104,7 @@ private void Data2() var reqA = new RequestMatcher { Path = "/x&/y(", - QueryString = "?id=123" + Query = "?id=123" }; var evalA = Eval(6); var ctxA = GetContext(r => { }); @@ -194,7 +194,7 @@ public static IEnumerable GetScoreTestData() { {"abcd", "application/json"} }; - less.QueryString = "?id=0"; + less.Query = "?id=0"; less.Method = "GET"; less.Headers = new Dictionary { From a7db8f52d75adffc603b26c30ea9205eaa17f86f Mon Sep 17 00:00:00 2001 From: alex held Date: Sun, 12 Jan 2020 17:49:14 +0100 Subject: [PATCH 22/64] !SQUASH disable Evaluators --- .../.idea/contentModel.xml | 9 - .../NinjaTools.FluentMockServer.API.csproj | 8 + .../Proxy/Evaluation/EvaluationPipeline.cs | 158 +++++++++--------- .../Evaluation/Evaluators/EvaluatorBase.cs | 28 +--- 4 files changed, 91 insertions(+), 112 deletions(-) diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index 1e894b6a..44bbf9b4 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -179,15 +179,6 @@ - - - - - - - - - diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 4d2e6fc4..94d35a5e 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -9,6 +9,11 @@ 1.0.0 + + + + + bin\Release\NinjaTools.FluentMockServer.API.xml @@ -35,4 +40,7 @@ + + + diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs index cde51250..0b833f1e 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs @@ -1,79 +1,79 @@ -using AutoMapper; -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Models; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation -{ - public static class EvaluationPipeline - { - private static readonly IEvaluator Pipeline; - - static EvaluationPipeline() - { - var evaluator = new HttpRequestEvaluator(); - evaluator.SetNext(new HttpPathEvaluator()) - .SetNext(new HttpMethodEvaluator()) - .SetNext(new HttpBodyEvaluator()) - .SetNext(new HttpCookieEvaluator()) - .SetNext(new QueryStringEvaluator()) - .SetNext(new HttpHeaderEvaluator()); - - Pipeline = evaluator; - } - - public static IEvaluationResult Evaluate(HttpContext httpContext, RequestMatcher matcher) - { - var context = new EvaluationContext(httpContext, matcher.Normalize()); - var evalResult = Pipeline.Evaluate(context); - return evalResult; - } - - - - - - private static IMapper? _mapper; - public static IMapper Mapper - { - get - { - if (_mapper is null) - { - var config = new MapperConfiguration(cfg => cfg.AddProfile()); - _mapper = config.CreateMapper(); - } - - return _mapper; - } - } - - public static NormalizedMatcher Normalize(this RequestMatcher matcher) - { - var result = Mapper.Map(matcher); - return result; - } - - public class RequestMatcherProfile : Profile - { - public RequestMatcherProfile() - { - ShouldMapField = _ => true; - ShouldMapProperty = _ => true; - EnableNullPropagationForQueryMapping = true; - AllowNullCollections = true; - AllowNullDestinationValues = true; - - CreateMap() - .ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.BodyMatcher.Content)) - .ForMember(dest => dest.MatchExact, opt => opt.MapFrom(src => src.BodyMatcher.MatchExact)) - .ForMember(dest => dest.BodyKind, opt => opt.MapFrom(src => src.BodyMatcher.Type)) - .ForMember(dest => dest.Query, opt => opt.MapFrom(src => src.Query)) - .ReverseMap(); - } - - - } - } -} +// using AutoMapper; +// using Microsoft.AspNetCore.Http; +// using NinjaTools.FluentMockServer.API.Models; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; +// +// namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation +// { +// public static class EvaluationPipeline +// { +// private static readonly IEvaluator Pipeline; +// +// static EvaluationPipeline() +// { +// var evaluator = new HttpRequestEvaluator(); +// evaluator.SetNext(new HttpPathEvaluator()) +// .SetNext(new HttpMethodEvaluator()) +// .SetNext(new HttpBodyEvaluator()) +// .SetNext(new HttpCookieEvaluator()) +// .SetNext(new QueryStringEvaluator()) +// .SetNext(new HttpHeaderEvaluator()); +// +// Pipeline = evaluator; +// } +// +// public static IEvaluationResult Evaluate(HttpContext httpContext, RequestMatcher matcher) +// { +// var context = new EvaluationContext(httpContext, matcher.Normalize()); +// var evalResult = Pipeline.Evaluate(context); +// return evalResult; +// } +// +// +// +// +// +// private static IMapper? _mapper; +// public static IMapper Mapper +// { +// get +// { +// if (_mapper is null) +// { +// var config = new MapperConfiguration(cfg => cfg.AddProfile()); +// _mapper = config.CreateMapper(); +// } +// +// return _mapper; +// } +// } +// +// public static NormalizedMatcher Normalize(this RequestMatcher matcher) +// { +// var result = Mapper.Map(matcher); +// return result; +// } +// +// public class RequestMatcherProfile : Profile +// { +// public RequestMatcherProfile() +// { +// ShouldMapField = _ => true; +// ShouldMapProperty = _ => true; +// EnableNullPropagationForQueryMapping = true; +// AllowNullCollections = true; +// AllowNullDestinationValues = true; +// +// CreateMap() +// .ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.BodyMatcher.Content)) +// .ForMember(dest => dest.MatchExact, opt => opt.MapFrom(src => src.BodyMatcher.MatchExact)) +// .ForMember(dest => dest.BodyKind, opt => opt.MapFrom(src => src.BodyMatcher.Type)) +// .ForMember(dest => dest.Query, opt => opt.MapFrom(src => src.Query)) +// .ReverseMap(); +// } +// +// +// } +// } +// } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs index afeb93d4..56d9715f 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs @@ -7,12 +7,8 @@ namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators [DebuggerDisplay("{Name}; IsLast={IsLast};", Name = "Evaluator")] public abstract class EvaluatorBase : IEvaluator { - protected virtual string FormatMatched(string subjectName, object match) => $"Matched {subjectName}. Value={match};"; - + public string Name => GetType().Name; protected abstract void EvaluateMember(EvaluationContext context); - - - public virtual string Name => GetType().Name; public bool IsLast => _next is null; private IEvaluator? _next; @@ -27,28 +23,12 @@ public IEvaluator SetNext(IEvaluator next) /// public virtual IEvaluationResult Evaluate(EvaluationContext context) { - if (_next == null) - { - goto skip; - } - - try - { - EvaluateMember(context); - return _next.Evaluate(context); - } - catch (Exception e) - { - context.LogError(e); - } - - skip: - - return context switch + EvaluateMember(context); + return _next != null ? _next.Evaluate(context) : context switch { { IsMatch: true } ctx => new EvaluationSuccessfulResult(ctx), { } ctx => new EvaluationUnsuccessfulResult(ctx) }; } } -} \ No newline at end of file +} From 3ecfdb333e44f0f72c60d5843ccc674a99412828 Mon Sep 17 00:00:00 2001 From: alex held Date: Sun, 12 Jan 2020 20:30:15 +0100 Subject: [PATCH 23/64] !SQUASH refactor(mock-server): re-enable Evaluators --- .../.idea/contentModel.xml | 9 +++ .../.idea/sonarIssues.xml | 30 ++++++++ docs/http-requests/create-setup.http | 2 + .../Extensions/EvaluationExtensions.cs | 7 +- .../Models/RequestBodyMatcher.cs | 2 +- .../Models/RequestMatcher.cs | 9 ++- .../NinjaTools.FluentMockServer.API.csproj | 8 +- .../Proxy/Evaluation/EvaluationContext.cs | 3 +- .../Evaluators/HttpBodyEvaluator.cs | 46 ++++++------ .../Evaluators/HttpCookieEvaluator.cs | 30 +++----- .../Evaluators/HttpHeaderEvaluator.cs | 34 ++++----- .../Evaluators/HttpMethodEvaluator.cs | 23 ++---- .../Evaluators/HttpPathEvaluator.cs | 26 ++----- .../Evaluators/QueryStringEvaluator.cs | 24 +++--- .../Evaluation/Models/IEvaluationResult.cs | 7 +- .../Evaluation/Visitors/IPartialVisitor.cs | 5 +- .../Proxy/Evaluation/Visitors/IVisitor.cs | 2 +- .../Visitors/RequestEvaluatorVistor.cs | 31 +++++++- .../Proxy/Matchers/EvaluatorTests.cs | 73 ++++++++++--------- 19 files changed, 199 insertions(+), 172 deletions(-) diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index 44bbf9b4..1e894b6a 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -179,6 +179,15 @@ + + + + + + + + + diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml index fc244ecb..cffd6c2c 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/sonarIssues.xml @@ -748,11 +748,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -818,6 +843,11 @@ + + + + + diff --git a/docs/http-requests/create-setup.http b/docs/http-requests/create-setup.http index ef0f8b89..ee4587aa 100644 --- a/docs/http-requests/create-setup.http +++ b/docs/http-requests/create-setup.http @@ -1,3 +1,5 @@ + + POST http://localhost:1080/setup/create Content-Type: application/json diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs index 65e07b30..d9fcbfd0 100644 --- a/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs @@ -14,7 +14,7 @@ public static class EvaluationExtensions where TMockMember : class where THttpRequestMember : class { - if (item is null && httpRequestMember is null) + if (item is null && httpRequestMember != null) { var rating = new Rating(1, EvaluationWeight.Low) { @@ -25,6 +25,11 @@ public static class EvaluationExtensions context.Ratings.Append(rating); return null; } + else if(item != null && httpRequestMember == null) + { + context.Fail(httpRequestMember, item); + return null; + } return httpRequestMember; } diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs index a064155f..576a26ef 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs @@ -40,7 +40,7 @@ public bool IsMatch([NotNull] HttpRequest request) public void Accept(Func visitorFactory) { var visitor = visitorFactory(); - visitor.VisitBody(Content, MatchExact, Type); + visitor.VisitBody(this); } } } diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs index a9423ead..c621298d 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs @@ -146,20 +146,23 @@ private bool HeadersMatching(IHeaderDictionary requestHeaders) public void Accept(Func visitorFactory) { var visitor = visitorFactory(); + this.BodyMatcher.Accept(() => visitor); + visitor.VisitHeaders(Headers); visitor.VisitMethod(Method); visitor.VisitPath(Path); visitor.VisitQuery(Query); visitor.VisitCookies(Cookies); visitor.VisitBody(BodyMatcher); + visitor.Visit(this); } /// public T Accept(Func> visitorFactory) { - var visitor = visitorFactory(); - var result = visitor.Evaluate(); - return result; + var visitor = visitorFactory(); + Accept(() => (IRequestMatcherEvaluatorVistor) visitor); + return visitor.Evaluate(); } } diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 94d35a5e..e11fcbbb 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -10,9 +10,11 @@ - - - + + + + + bin\Release\NinjaTools.FluentMockServer.API.xml diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs index 730d1bc1..4c4eea43 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs @@ -7,10 +7,9 @@ namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation [DebuggerDisplay("{IsMatch} {Ratings} | Messages={Messages}; Matcher={Matcher};", Name = "EvaluatingContext")] public class EvaluationContext { - public EvaluationContext(HttpContext httpContext, NormalizedMatcher matcher) + public EvaluationContext(HttpContext httpContext) { HttpContext = httpContext; - Matcher = matcher; Messages = new EvaluationMessages(); Ratings = new EvaluationRatings(); IsMatch = true; diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs index 47ef57ba..3a4fc9b2 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs @@ -1,6 +1,6 @@ using System.IO; -using System.Reflection; using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Extensions; namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators { @@ -9,37 +9,33 @@ public class HttpBodyEvaluator : EvaluatorBase /// protected override void EvaluateMember(EvaluationContext context) { - if (context.Matcher.HasBody != true) + if (context.EnsureNotNull(context.Matcher, context.HttpContext.Request.Body) is {} m) { - context.Matches(EvaluationWeight.Low); - return; - } - var request = context.HttpContext.Request; + var request = context.HttpContext.Request; - var bodyKind = context.Matcher.BodyKind; - var shouldMatchExact = context.Matcher.MatchExact; - var bodyContent = context.Matcher.Content; + var bodyKind = context.Matcher.BodyKind; + var shouldMatchExact = context.Matcher.MatchExact; + var bodyContent = context.Matcher.Content; - request.EnableBuffering(); - request.Body.Position = 0; - using var reader = new StreamReader(request.Body); - var content = reader.ReadToEnd(); + request.EnableBuffering(); + request.Body.Position = 0; + using var reader = new StreamReader(request.Body); + var content = reader.ReadToEnd(); - if (shouldMatchExact && bodyContent == content) - { - context.Matches(EvaluationWeight.Max); - context.AddExtraPoints(3); + if (shouldMatchExact && bodyContent == content) + { + context.Match(content, bodyContent); + return; + } + else if (!shouldMatchExact && content.Length > 0 && bodyContent.Contains(content)) + { + context.Match(content, bodyContent); + return; + } - } - else if (!shouldMatchExact && content.Length > 0 && bodyContent.Contains(content)) - { - context.Matches(EvaluationWeight.Max); - } - else - { - context.LogError(new AmbiguousMatchException($"{nameof(HttpRequest.Body)} '{content}' did not match. Expected={bodyContent};")); + context.Fail(context.HttpContext.Request.Body, bodyContent); } } } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs index 0a3770f2..4b7f7166 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs @@ -1,8 +1,5 @@ using System.Linq; -using System.Reflection; -using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using NinjaTools.FluentMockServer.API.Extensions; namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators { @@ -11,25 +8,18 @@ public class HttpCookieEvaluator : EvaluatorBase /// protected override void EvaluateMember(EvaluationContext context) { - var request = context.HttpContext.Request; - var requestCookies = request.Cookies; var cookies = context.Matcher.Cookies; - - if (cookies is null) + if (context.EnsureNotNull(context.HttpContext.Request.Cookies, cookies) is {} httpCookies) { - context.Matches(EvaluationWeight.Low); - return; - } - - var unsatisfiedCookies = cookies.Except(requestCookies).ToList(); + var unsatisfiedCookies = cookies.Except(httpCookies).ToList(); - if (unsatisfiedCookies.Any() != true) - { - context.Matches(EvaluationWeight.High); - return; + if (unsatisfiedCookies.Any() != true) + { + context.Match(httpCookies, cookies); + return; + } + context.Fail(httpCookies, cookies); } - - context.LogError(new AmbiguousMatchException($"{nameof(HttpRequest)} didn't contain all configured Headers. {unsatisfiedCookies.Count} Remaining={JObject.FromObject(cookies).ToString(Formatting.Indented)};")); } } -} \ No newline at end of file +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs index b8302ae7..c0e054e8 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs @@ -1,8 +1,5 @@ using System.Linq; -using System.Reflection; -using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using NinjaTools.FluentMockServer.API.Extensions; namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators { @@ -11,26 +8,21 @@ public class HttpHeaderEvaluator : EvaluatorBase /// protected override void EvaluateMember(EvaluationContext context) { - var request = context.HttpContext.Request; - var requestHeaderDictionary = request.Headers; - var headers = context.Matcher.Headers; - - if (headers is null) + var header = context.Matcher.Headers; + if (context.EnsureNotNull(context.HttpContext.Request.Headers, header) is {} httpHeader) { - context.Matches(EvaluationWeight.Low); - return; - } + var unsatisfiedHeaders = header.Except(httpHeader + .ToDictionary(k => k.Key, v => v.Value.ToArray())); - var requestHeaders = requestHeaderDictionary.ToDictionary(key => key.Key, val => val.Value.ToArray()); - var unsatisfiedHeaders = headers.Except(requestHeaders).ToList(); - if (!unsatisfiedHeaders.Any()) - { - context.Matches(EvaluationWeight.Max); - return; - } + if (unsatisfiedHeaders.Any() != true) + { + context.Match(httpHeader, header); + return; + } - context.LogError(new AmbiguousMatchException($"{nameof(HttpRequest)} didn't contain all configured Headers. {unsatisfiedHeaders.Count} Remaining={JObject.FromObject(headers).ToString(Formatting.Indented)};")); + context.Fail(httpHeader, header); + } } } -} \ No newline at end of file +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs index 0b481168..ae2e64b3 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs @@ -1,6 +1,4 @@ -using System.ComponentModel.DataAnnotations; -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Extensions; namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators { @@ -9,20 +7,15 @@ public class HttpMethodEvaluator : EvaluatorBase /// protected override void EvaluateMember(EvaluationContext context) { - var request = context.HttpContext.Request; var method = context.Matcher.Method; - - if (method is null) - { - context.Matches(EvaluationWeight.Low, $"Matched {nameof(RequestMatcher.Method)}. Value={method};"); - } - else if (method == request.Method) - { - context.Matches(EvaluationWeight.Max, $"Matched {nameof(RequestMatcher.Method)}. Value={method};"); - } - else + if (context.EnsureNotNull(context.HttpContext.Request.Method, method) is {} httpMethod) { - context.LogError(new ValidationException($"{nameof(HttpRequest.Method)} '{request.Method}' did not match setup. Expected={method ?? "*"}")); + if (method is null) + { + context.Match(httpMethod, method); + return; + } + context.Fail(httpMethod, method); } } } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs index fd2cb6a9..8e8194bd 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs @@ -1,6 +1,3 @@ -using System.Reflection; -using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Http; using NinjaTools.FluentMockServer.API.Extensions; namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators @@ -11,23 +8,16 @@ public class HttpPathEvaluator : EvaluatorBase /// protected override void EvaluateMember(EvaluationContext context) { - var request = context.HttpContext.Request; - var requestPath = request.Path.Value; var path = context.Matcher.Path; - - if (path is null) + if (context.EnsureNotNull(context.HttpContext.Request.Path.Value, path) is {} httpPath) { - context.Matches(EvaluationWeight.Low); - return; + if (path is null) + { + context.Match(httpPath, path); + return; + } + context.Fail(httpPath, path); } - - if (path.IsValidRegex() &&Regex.IsMatch(requestPath, path, RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace)) - { - context.Matches(EvaluationWeight.High); - return; - } - - context.LogError(new AmbiguousMatchException($"{nameof(HttpRequest.Path)}-Regex '{path}' did not match. Actual={requestPath};"));; } } -} \ No newline at end of file +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs index 2632b8c5..0d4b1326 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using NinjaTools.FluentMockServer.API.Extensions; namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators { @@ -7,22 +7,16 @@ public class QueryStringEvaluator : EvaluatorBase /// protected override void EvaluateMember(EvaluationContext context) { - var request = context.HttpContext.Request; - var requestQueryString = request.QueryString; var query = context.Matcher.Query; - - if (query is null) - { - context.Matches(EvaluationWeight.Low); - return; - } - else if(query == request.QueryString.ToString()) + if (context.EnsureNotNull(context.HttpContext.Request.QueryString.Value, query) is {} httpQuery) { - context.Matches(EvaluationWeight.Max); - return; + if (query is null) + { + context.Match(httpQuery, query); + return; + } + context.Fail(httpQuery, query); } - - context.LogError(new AmbiguousMatchException($"Actual QueryString '{requestQueryString.ToString()}' didn't match. Expected={query};")); } } -} \ No newline at end of file +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs index d16ce0a9..96d0bc8b 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs @@ -1,14 +1,9 @@ -using System; -using System.Collections.Generic; - namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models { public interface IEvaluationResult { long Score { get; } bool IsMatch { get; } - IReadOnlyList Messages { get; } - IReadOnlyList Errors { get; } - int ErrorCount => Errors.Count; + EvaluationRatings Ratings { get; } } } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs index 174d9cc4..402e3f08 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs @@ -11,7 +11,6 @@ public interface IPartialVisitor : IVisitor void VisitMethod(string? method); void VisitQuery(string? query); - void VisitBody(RequestBodyMatcher? bodyMatcher) => VisitBody(bodyMatcher.Content, bodyMatcher.MatchExact, bodyMatcher.Type); - void VisitBody(string? requestBody, bool exactMatch, RequestBodyKind kind); + void VisitBody(RequestBodyMatcher? bodyMatcher);// => VisitBody(bodyMatcher.Content, bodyMatcher.MatchExact, bodyMatcher.Type); } -} \ No newline at end of file +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs index c1256598..98b974ce 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs @@ -1,6 +1,6 @@ namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors { - public interface IRequestMatcherEvaluatorVistor + public interface IRequestMatcherEvaluatorVistor : IRequestMatcherEvaluatorVistor { T Evaluate(); } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs index 45cb123b..a659cb69 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs @@ -8,7 +8,7 @@ namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors { - public class RequestEvaluatorVistor : IRequestMatcherEvaluatorVistor + internal class RequestEvaluatorVistor : IRequestMatcherEvaluatorVistor { private readonly EvaluationContext _evaluationContext; @@ -20,7 +20,19 @@ public RequestEvaluatorVistor(EvaluationContext evaluationContext) } /// - public IEvaluationResult Evaluate() + public void Visit(RequestMatcher matcher) + { + VisitHeaders(matcher.Headers); + VisitMethod(matcher.Method); + VisitPath(matcher.Path); + VisitQuery(matcher.Query); + VisitCookies(matcher.Cookies); + VisitBody(matcher.BodyMatcher); + matcher.Accept(() => this); + } + + /// + public EvaluationResultBase Evaluate() { return _evaluationContext switch { @@ -52,7 +64,7 @@ public void VisitHeaders(IDictionary? headers) { if (_evaluationContext.EnsureNotNull(HttpRequest.Headers, headers) is {} httpRequestMember) { - if (headers.Except(httpRequestMember.ToDictionary(k => k.Key, v => v.Value.ToArray())).Any()) + if (headers.Except(httpRequestMember?.ToDictionary(k => k.Key, v => v.Value.ToArray())).Any()) { _evaluationContext.Fail(httpRequestMember, headers); } @@ -112,7 +124,16 @@ public void VisitQuery(string? query) } /// - public void VisitBody(string? requestBody, bool exactMatch, RequestBodyKind kind) + public void VisitBody(RequestBodyMatcher? bodyMatcher) + { + if (bodyMatcher != null) + { + VisitBody(bodyMatcher.Content, bodyMatcher.MatchExact, bodyMatcher.Type); + } + } + + /// + private void VisitBody(string? requestBody, bool exactMatch, RequestBodyKind kind) { HttpRequest.EnableBuffering(); if (_evaluationContext.EnsureNotNull(HttpRequest.Body, requestBody) is { } httpBodyStream && httpBodyStream.CanSeek) @@ -134,5 +155,7 @@ public void VisitBody(string? requestBody, bool exactMatch, RequestBodyKind kind } } } + + } } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs index ad3e7f3f..62d6cf61 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs @@ -4,12 +4,11 @@ using System.Linq; using System.Text; using FluentAssertions; -using JetBrains.Annotations; using Microsoft.AspNetCore.Http; -using Moq; using NinjaTools.FluentMockServer.API.Models; using NinjaTools.FluentMockServer.API.Proxy.Evaluation; using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; +using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; using Xunit.Abstractions; @@ -17,7 +16,7 @@ namespace NinjaTools.FluentMockServer.API.Tests.Proxy.Matchers { - public class EvaluatorTestData : TheoryData + public class EvaluatorTestData : TheoryData { protected static HttpContext GetContext(Action setup) @@ -27,16 +26,21 @@ protected static HttpContext GetContext(Action setup) return context; } - protected IEvaluationResult Eval(int score, [CanBeNull] EvaluationMessages messages = null) + protected EvaluationResultBase Eval(int score, bool valid) { - messages ??= new EvaluationMessages(); - return Mock.Of(e => e.Score == score - && e.IsMatch == false - && e.Messages == messages.Messages - && e.Errors == messages.Exceptions); + return new ResultProxy(valid, new EvaluationContext(new DefaultHttpContext())); } + public class ResultProxy : EvaluationResultBase +{ + /// + public ResultProxy(bool math, EvaluationContext context) : base(context) + { + } + /// + public override bool IsMatch { get; } +} public class ValidRequestMatcherTestData : EvaluatorTestData @@ -56,7 +60,7 @@ private void Data1() r.Path = "/cars/buy/id/200"; r.ContentType = "application/json"; }); - var eval = Eval(10); + var eval = Eval(10, true); Add(matcher, context, eval); } } @@ -90,7 +94,7 @@ private void Data1() Content = "?" } }; - var evalA = Eval(0); + var evalA = Eval(0, false); var ctxA = GetContext(r => { r.Method = HttpMethods.Delete; @@ -106,7 +110,7 @@ private void Data2() Path = "/x&/y(", Query = "?id=123" }; - var evalA = Eval(6); + var evalA = Eval(6, false); var ctxA = GetContext(r => { }); Add(reqA, ctxA, evalA); @@ -124,44 +128,45 @@ public EvaluatorTests(ITestOutputHelper output) : base(output) [Theory] [ClassData(typeof(EvaluatorTestData.InvalidRequestMatcherTestData))] - public void Should_Return_EvaluationUnsuccessfulResult_When_Not_Matching(RequestMatcher matcher, HttpContext context, IEvaluationResult expectedResult) + public void Should_Return_EvaluationUnsuccessfulResult_When_Not_Matching(RequestMatcher matcher, HttpContext context, EvaluationResultBase expectedResult) { // Act && Assert - - var result = EvaluationPipeline.Evaluate(context, matcher); + var visitor = new RequestEvaluatorVistor(new EvaluationContext(context)); + var result = visitor.Evaluate(); Dump(matcher, "matcher"); - result.Should().BeOfType(); + result.Should().As().Should().Be(false); result.IsMatch.Should().BeFalse(); result.Score.Should().Be(expectedResult.Score); } [Theory] [ClassData(typeof(EvaluatorTestData.ValidRequestMatcherTestData))] - public void Should_Return_EvaluationSuccessfulResult_When_Matching(RequestMatcher matcher, HttpContext context, IEvaluationResult expectedResult) + public void Should_Return_EvaluationSuccessfulResult_When_Matching(RequestMatcher matcher, HttpContext context, EvaluationResultBase expectedResult) { // Act && Assert - var result = EvaluationPipeline.Evaluate(context, matcher); + var visitor = new RequestEvaluatorVistor(new EvaluationContext(context)); + var result = matcher.Accept(() => visitor); Dump(matcher, "matcher"); - result.Should().BeOfType(); + result.Should().As().Should().Be(false); result.IsMatch.Should().Be(true); result.Score.Should().Be(expectedResult.Score); } - - [Theory] - [MemberData(nameof(GetScoreTestData))] - public void Score_Should_Be_Higher_For_The_Matcher_Who_Is_Closer_To_The_HttpRequest(RequestMatcher more, HttpContext context, RequestMatcher less) - { - // Act - var a = EvaluationPipeline.Evaluate(context, more); - Dump(a, "more"); - - var b = EvaluationPipeline.Evaluate(context, less); - Dump(b, "less"); - - // Assert - a.Score.Should().BeGreaterOrEqualTo(b.Score); - } + // + // [Theory] + // [MemberData(nameof(GetScoreTestData))] + // public void Score_Should_Be_Higher_For_The_Matcher_Who_Is_Closer_To_The_HttpRequest(RequestMatcher more, HttpContext context, RequestMatcher less) + // { + // // Act + // var a = EvaluationPipeline.Evaluate(context, more); + // Dump(a, "more"); + // + // var b = EvaluationPipeline.Evaluate(context, less); + // Dump(b, "less"); + // + // // Assert + // a.Score.Should().BeGreaterOrEqualTo(b.Score); + // } public static IEnumerable GetScoreTestData() { From 591f92d4d4bc08ddc4a8e43e3a5aaf4ac9190d71 Mon Sep 17 00:00:00 2001 From: alex held Date: Mon, 13 Jan 2020 22:46:43 +0100 Subject: [PATCH 24/64] chore(deps): switch to stable Microsoft.NET.Test.Sdk 16.4.0 --- .../NinjaTools.FluentMockServer.API.Tests.csproj | 2 +- .../NinjaTools.FluentMockServer.TestContainers.Tests.csproj | 2 +- .../NinjaTools.FluentMockServer.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj index 3b3b03cc..6fd2cfa4 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj @@ -11,7 +11,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/test/NinjaTools.FluentMockServer.TestContainers.Tests/NinjaTools.FluentMockServer.TestContainers.Tests.csproj b/test/NinjaTools.FluentMockServer.TestContainers.Tests/NinjaTools.FluentMockServer.TestContainers.Tests.csproj index 4a7fc0e6..0bd9416b 100644 --- a/test/NinjaTools.FluentMockServer.TestContainers.Tests/NinjaTools.FluentMockServer.TestContainers.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.TestContainers.Tests/NinjaTools.FluentMockServer.TestContainers.Tests.csproj @@ -13,7 +13,7 @@ all - + diff --git a/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj b/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj index 7deb99e3..f9922d2b 100644 --- a/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj @@ -20,7 +20,7 @@ - + From 04314835d5f5a2d9a055ef81988640bfc973f219 Mon Sep 17 00:00:00 2001 From: alex held Date: Tue, 14 Jan 2020 17:21:38 +0100 Subject: [PATCH 25/64] chore: add /build as solution folder --- NinjaTools.FluentMockServer.sln | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NinjaTools.FluentMockServer.sln b/NinjaTools.FluentMockServer.sln index 4ba628cd..2ef86d8a 100644 --- a/NinjaTools.FluentMockServer.sln +++ b/NinjaTools.FluentMockServer.sln @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NinjaTools.FluentMockServer EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NinjaTools.FluentMockServer.API.Tests", "test\NinjaTools.FluentMockServer.API.Tests\NinjaTools.FluentMockServer.API.Tests.csproj", "{175B41A0-DF12-499C-AD27-9E4EC09A82DA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{4B00F08C-15F0-4A37-8A23-95EFDDC85C93}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 53405c941f2fb3ad08239e1ad6cae333cbb85fd8 Mon Sep 17 00:00:00 2001 From: alex held Date: Tue, 14 Jan 2020 20:33:42 +0100 Subject: [PATCH 26/64] (script): use csharp script to build to project --- NinjaTools.FluentMockServer.sln | 4 + build/build.csx | 78 +++++++++++++++++++ build/nuget-pack.yaml | 54 +++++++++++++ build/publish-nuget.yaml | 23 ++---- .../NinjaTools.FluentMockServer.csproj | 5 +- 5 files changed, 146 insertions(+), 18 deletions(-) create mode 100644 build/build.csx create mode 100644 build/nuget-pack.yaml diff --git a/NinjaTools.FluentMockServer.sln b/NinjaTools.FluentMockServer.sln index 2ef86d8a..1a7ea56e 100644 --- a/NinjaTools.FluentMockServer.sln +++ b/NinjaTools.FluentMockServer.sln @@ -16,6 +16,10 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NinjaTools.FluentMockServer.API.Tests", "test\NinjaTools.FluentMockServer.API.Tests\NinjaTools.FluentMockServer.API.Tests.csproj", "{175B41A0-DF12-499C-AD27-9E4EC09A82DA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{4B00F08C-15F0-4A37-8A23-95EFDDC85C93}" +ProjectSection(SolutionItems) = preProject + build\nuget-pack.yaml = build\nuget-pack.yaml + build\build.csx = build\build.csx +EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/build/build.csx b/build/build.csx new file mode 100644 index 00000000..50a7f3b9 --- /dev/null +++ b/build/build.csx @@ -0,0 +1,78 @@ +#load "nuget:Dotnet.Build, 0.7.1" +#load "nuget:dotnet-steps, 0.0.2" + + +using static FileUtils; +using System.Xml.Linq; +using System.Text.RegularExpressions; + +public string rootPath = Directory.GetParent(FileUtils.GetScriptFolder()).FullName; +string BuildId = Args.FirstOrDefault(); + +foreach (var arg in Args) +{ + Console.WriteLine(arg); +} + +[StepDescription("Creates all NuGet packages and the release zip for GitHub releases")] +Step pack = () => +{ + build(); + CreateNuGetPackage("NinjaTools.FluentMockServer"); +}; + +[StepDescription("Builds the projects..")] +[DefaultStep] +Step build = () => +{ + var version = GetVersion("NinjaTools.FluentMockServer"); + var projectPath = GetProjectPath("NinjaTools.FluentMockServer"); + Command.Execute($"dotnet", $"build {projectPath} -c Release -p:Version={version}"); +}; + +await ExecuteSteps(Args); + +public string GetProjectPath(string project) => Directory.GetFiles(rootPath, $"{project}.csproj", SearchOption.AllDirectories).Single(); + +public string GetVersion(string project) +{ + var projectPath = GetProjectPath(project); + var projectFile = XDocument.Load(projectPath); + var versionElement = projectFile.Descendants("Version").Single(); + var version = versionElement.Value; + var match = Regex.Match(version, @"^(?\d*)\.(?\d*)\.(?\d*)"); + var major = int.Parse(match.Groups["major"].Value); + var minor = int.Parse(match.Groups["minor"].Value); + var patch = int.Parse(match.Groups["patch"].Value); + + var head = File.ReadAllText(Path.Combine(rootPath, ".git", "HEAD")); + if(head.Contains("refs/heads/master") || Args.Count() < 2 ) + { + Console.WriteLine($"[{project}] Using stable version: {version}"); + return version; + } + else + { + version= $"{++major}.{minor}.{patch}-pre{Args[1]}"; + Console.WriteLine($"[{project}] Using prerelease version: {version}"); + + return version; + } +} + + + + +private void CreateNuGetPackage(string project) +{ + var version = GetVersion(project); + var projectPath = GetProjectPath(project); + + if(Args.Count() == 3) + { + var output = Args[2]; + Command.Execute("dotnet", $"pack {projectPath} --no-build -p:Version={version} -c Release --output {output} -v d /nologo"); + } + else + Command.Execute("dotnet", $"pack {projectPath} --no-build -p:Version={version} -c Release -v d /nologo"); +} diff --git a/build/nuget-pack.yaml b/build/nuget-pack.yaml new file mode 100644 index 00000000..7859a88c --- /dev/null +++ b/build/nuget-pack.yaml @@ -0,0 +1,54 @@ +# See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference + +trigger: + branches: + include: + - release/* + - master + - develop + +pr: none + +variables: + sdkVersion: "3.1.100" + buildConfiguration: "Release" + projects: "src/**/*.csproj" + +jobs: + - job: Pack + displayName: "🎁 Pack 📦" + steps: + - task: UseDotNet@2 + inputs: + packageType: "sdk" + version: $(sdkVersion) + includePreviewVersions: true + - task: DotNetCoreCLI@2 + inputs: + command: 'custom' + custom: 'tool' + arguments: 'install -g dotnet-script' + - task: DotNetCoreCLI@2 + inputs: + command: 'custom' + custom: 'script' + arguments: "build/build.csx pack $(Build.BuildNumber) $(Build.ArtifactStagingDirectory)/packages/releases" + - publish: "$(Build.ArtifactStagingDirectory)/packages" + artifact: "packages" + condition: succeeded() + + - job: Push + displayName: "🚀 Push 📨" + dependsOn: Pack + condition: succeeded() + steps: + - checkout: none + - download: current + artifact: "packages" + - task: NuGetCommand@2 + displayName: "Publish Prerelease NuGet Package" + inputs: + command: "push" + packagesToPush: "$(Pipeline.Workspace)/packages/**/*.nupkg" + nuGetFeedType: "external" + publishFeedCredentials: "nuget ninjatools" diff --git a/build/publish-nuget.yaml b/build/publish-nuget.yaml index 41067d35..be13c1f0 100644 --- a/build/publish-nuget.yaml +++ b/build/publish-nuget.yaml @@ -8,9 +8,10 @@ trigger: pr: none -variables: +parameters: sdkVersion: "3.1.100" buildConfiguration: "Release" + projects: "src/**/*.csproj" jobs: - job: Pack @@ -27,23 +28,15 @@ jobs: projects: "src/**/*.csproj" arguments: "--configuration $(buildConfiguration)" - task: DotNetCoreCLI@2 - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - displayName: "Stable" inputs: - command: "pack" - packagesToPack: "**/*.csproj" - packDirectory: "$(Build.ArtifactStagingDirectory)/packages/releases" - nobuild: true - versioningScheme: "off" + command: 'custom' + custom: 'tool' + arguments: 'install -g dotnet-script' - task: DotNetCoreCLI@2 - displayName: "Prerelease" - condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master')) inputs: - command: "pack" - packagesToPack: "**/*.csproj" - packDirectory: "$(Build.ArtifactStagingDirectory)/packages/prereleases" - arguments: "--configuration $(buildConfiguration)" - buildProperties: 'VersionSuffix="$(Build.BuildNumber)"' + command: 'custom' + custom: 'script' + arguments: "/build/build.csx pack $(Build.BuildNumber) $(Build.ArtifactStagingDirectory)/packages/releases" - publish: "$(Build.ArtifactStagingDirectory)/packages" artifact: "packages" condition: succeeded() diff --git a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj index df414630..ff673dae 100644 --- a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj +++ b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj @@ -1,4 +1,4 @@ - + 36A37D14-D7FC-4E32-8F48-5FB606818757 @@ -13,8 +13,7 @@ true true mockserver, test, mock, docker, xunit, api, soap, rest - $(VersionSuffix) - 1.1.0 + 1.1.0 From 76e61d758bf92ae19dabdce3be2c560f2c1fa16c Mon Sep 17 00:00:00 2001 From: alex held Date: Tue, 14 Jan 2020 21:54:57 +0100 Subject: [PATCH 27/64] fix(test): remove leading '/' in WithPath("/test") --- .../Xunit/ManualMockServerSetupTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs index 7b7a5bee..6a532f5d 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs @@ -3,8 +3,6 @@ using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; -using NinjaTools.FluentMockServer.FluentAPI.Builders.HttpEntities; -using NinjaTools.FluentMockServer.Models; using NinjaTools.FluentMockServer.Models.ValueTypes; using NinjaTools.FluentMockServer.Xunit; using Xunit; @@ -59,7 +57,7 @@ public async Task Should_Verify_Expecation_Was_Met_On_MockServer() { // Act await MockClient.SetupAsync(exp => - exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) + exp.OnHandling(HttpMethod.Get, req => req.WithPath("test")) .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) .Setup()); From 2c5bc38ca028ec26cfa58015be36e0faf4213b0c Mon Sep 17 00:00:00 2001 From: alex held Date: Wed, 15 Jan 2020 10:17:20 +0100 Subject: [PATCH 28/64] refactor(test): add 200ms timeout between each test --- .../Xunit/ManualMockServerSetupTests.cs | 14 ++++---------- .../Xunit/VerificationTests.cs | 2 ++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs index 6a532f5d..0a048a70 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using FluentAssertions; using NinjaTools.FluentMockServer.Models.ValueTypes; @@ -11,7 +12,6 @@ [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] namespace NinjaTools.FluentMockServer.Tests.Xunit { - public class ManualMockServerSetupTests : MockServerTestBase { /// @@ -55,20 +55,14 @@ await MockClient.SetupAsync(exp => [Fact] public async Task Should_Verify_Expecation_Was_Met_On_MockServer() { - // Act - await MockClient.SetupAsync(exp => - exp.OnHandling(HttpMethod.Get, req => req.WithPath("test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) - .Setup()); - + // Arrange var request = new HttpRequestMessage(HttpMethod.Get, new Uri("test", UriKind.Relative)); - var response = await MockClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + await HttpClient.SendAsync(request); // Act var (isValid, responseMessage) = await MockClient.VerifyAsync(v => v .WithMethod(HttpMethod.Get) - .WithPath("test"), VerificationTimes.Once); + .WithPath("test")); // Assert isValid.Should().BeTrue(); diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs index facb204e..6ee9ad37 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs @@ -1,4 +1,5 @@ using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using FluentAssertions; using NinjaTools.FluentMockServer.Models.ValueTypes; @@ -57,6 +58,7 @@ public abstract class MockServerTestBase : XUnitTestBase, IClassFixture Date: Wed, 15 Jan 2020 22:40:50 +0100 Subject: [PATCH 29/64] refactor(test-container): replace constant initial delay with iterative tries --- .../Xunit/MockServerContainer.cs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs index 8a33ba6f..4866139f 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs @@ -91,19 +91,23 @@ private async Task WaitUntilContainerStarted() var httpClient = new HttpClient(); var stopwatch = new Stopwatch(); stopwatch.Start(); - await Task.Delay(5000); - + while (stopwatch.IsRunning && stopwatch.Elapsed < TimeSpan.FromMinutes(2)) { - var request = new HttpRequestMessage(HttpMethod.Put, MockServerBaseUrl + "/mockserver/status"); - var response = await httpClient.SendAsync(request); - if (response.IsSuccessStatusCode) + try { - httpClient.Dispose(); - return; + var request = new HttpRequestMessage(HttpMethod.Put, MockServerBaseUrl + "/mockserver/status"); + var response = await httpClient.SendAsync(request); + if (response.IsSuccessStatusCode) + { + httpClient.Dispose(); + return; + } + } + catch (Exception e) + { + await Task.Delay(TimeSpan.FromMilliseconds(100)); } - - await Task.Delay(TimeSpan.FromSeconds(5)); } httpClient.Dispose(); From f6411fb40a5dbcd78d7fb0b2a36a2cf25331d349 Mon Sep 17 00:00:00 2001 From: alex held Date: Thu, 23 Jan 2020 18:55:59 +0100 Subject: [PATCH 30/64] fix(test): verify AT LEAST one GET /test has been made --- .../Xunit/ManualMockServerSetupTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs index 0a048a70..85b3f86e 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs @@ -1,7 +1,6 @@ using System; using System.Net; using System.Net.Http; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; using NinjaTools.FluentMockServer.Models.ValueTypes; @@ -62,7 +61,7 @@ public async Task Should_Verify_Expecation_Was_Met_On_MockServer() // Act var (isValid, responseMessage) = await MockClient.VerifyAsync(v => v .WithMethod(HttpMethod.Get) - .WithPath("test")); + .WithPath("test"), VerificationTimes.MoreThan(1)); // Assert isValid.Should().BeTrue(); From 4c409d575673202d8040eb576d1aadd8d85f97fd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 17:59:54 +0000 Subject: [PATCH 31/64] chore(deps): bump YamlDotNet from 8.0.0 to 8.1.0 Bumps [YamlDotNet](https://github.com/aaubry/YamlDotNet) from 8.0.0 to 8.1.0. - [Release notes](https://github.com/aaubry/YamlDotNet/releases) - [Changelog](https://github.com/aaubry/YamlDotNet/blob/master/RELEASE_NOTES.md) - [Commits](https://github.com/aaubry/YamlDotNet/compare/v8.0.0...v8.1.0) Signed-off-by: dependabot-preview[bot] --- .../NinjaTools.FluentMockServer.API.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 89b2526a..552b579d 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -24,7 +24,7 @@ - + From 89df5a30b5aefb59f0047dad9786bb94cd28d450 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 18:10:40 +0000 Subject: [PATCH 32/64] chore(deps): bump System.IO.Abstractions.TestingHelpers Bumps [System.IO.Abstractions.TestingHelpers](https://github.com/System-IO-Abstractions/System.IO.Abstractions) from 7.1.3 to 8.0.3. - [Release notes](https://github.com/System-IO-Abstractions/System.IO.Abstractions/releases) - [Commits](https://github.com/System-IO-Abstractions/System.IO.Abstractions/compare/v7.1.3...v8.0.3) Signed-off-by: dependabot-preview[bot] --- .../NinjaTools.FluentMockServer.API.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj index 6fd2cfa4..559f950d 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj @@ -13,7 +13,7 @@ - + From bb01f92c917aa6aa43d25ac0e37ab79151686f9f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2020 18:15:24 +0000 Subject: [PATCH 33/64] chore(deps): bump System.IO.Abstractions from 7.1.3 to 8.0.3 Bumps [System.IO.Abstractions](https://github.com/System-IO-Abstractions/System.IO.Abstractions) from 7.1.3 to 8.0.3. - [Release notes](https://github.com/System-IO-Abstractions/System.IO.Abstractions/releases) - [Commits](https://github.com/System-IO-Abstractions/System.IO.Abstractions/compare/v7.1.3...v8.0.3) Signed-off-by: dependabot-preview[bot] --- .../NinjaTools.FluentMockServer.API.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 552b579d..95caddbe 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -23,7 +23,7 @@ - + From 4470c844e6faae4e9a67a41788474175dad4b69d Mon Sep 17 00:00:00 2001 From: alex held Date: Thu, 23 Jan 2020 19:26:00 +0100 Subject: [PATCH 34/64] chore: set eol to auto --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index aa88753d..fdfb4a15 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ # Auto detect text files that will always have CRLF line endings on checkout. -* text eol=crlf +* text eol=auto # Denote all files that are truly binary and should not be modified. *.png binary From 00f2bde34b2547438233d7cc2bbe80e7c83996a1 Mon Sep 17 00:00:00 2001 From: alex held Date: Sun, 26 Jan 2020 01:38:00 +0100 Subject: [PATCH 35/64] feat(server): calculate best matching incoming http request --- .../.idea/GitScope.xml | 4 +- .../.idea/contentModel.xml | 104 ++-- .../Extensions/EvaluationExtensions.cs | 120 ++-- .../Infrastructure/SetupRepository.cs | 8 +- .../Models/RequestBodyMatcher.cs | 91 ++- .../Models/RequestMatcher.cs | 190 +------ .../Models/Setup.cs | 6 - .../NinjaTools.FluentMockServer.API.csproj | 16 + .../Proxy/Evaluation/Rules/IRule.cs | 7 + .../Visitors/Collections/HeadersCollection.cs | 123 +++++ .../Proxy/Visitors/ComparasionVisitor.cs | 221 ++++++++ .../Proxy/Visitors/IVisitor.cs | 51 ++ ...ols.FluentMockServer.TestContainers.csproj | 0 .../Models/RequestBodyMatcherTests.cs | 206 +++---- .../Models/RequestMatcherTests.cs | 502 ++++++++--------- .../Proxy/ComparasionVisitorTests.cs | 282 ++++++++++ .../Proxy/Matchers/EvaluatorTests.cs | 522 +++++++++--------- 17 files changed, 1480 insertions(+), 973 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Rules/IRule.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeadersCollection.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitor.cs delete mode 100644 src/NinjaTools.FluentMockServer.TestContainers/NinjaTools.FluentMockServer.TestContainers.csproj create mode 100644 test/NinjaTools.FluentMockServer.API.Tests/Proxy/ComparasionVisitorTests.cs diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/GitScope.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/GitScope.xml index aeaf904e..f4df0854 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/GitScope.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/GitScope.xml @@ -1,7 +1,7 @@ - \ No newline at end of file diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index 1e894b6a..6589af08 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -2,7 +2,7 @@ - + @@ -12,20 +12,24 @@ - + + + + + - + @@ -84,6 +88,18 @@ + + + + + + + + + + + + @@ -101,6 +117,10 @@ + + + + @@ -129,7 +149,7 @@ - + @@ -176,41 +196,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - @@ -225,20 +218,6 @@ - - - - - - - - - - - - - - @@ -250,7 +229,7 @@ - + @@ -272,6 +251,7 @@ + @@ -284,20 +264,6 @@ - - - - - - - - - - - - - - @@ -384,6 +350,12 @@ + + + + + + diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs index d9fcbfd0..cb602021 100644 --- a/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/Extensions/EvaluationExtensions.cs @@ -1,60 +1,60 @@ -using System.Linq; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; - -namespace NinjaTools.FluentMockServer.API.Extensions -{ - public static class EvaluationExtensions - { - public static void AddExtraPoints(this EvaluationContext context, int bonus) => context.Ratings.BonusPoints += bonus; - - - public static THttpRequestMember? EnsureNotNull(this EvaluationContext context,THttpRequestMember? httpRequestMember, TMockMember? item) - where TMockMember : class - where THttpRequestMember : class - { - if (item is null && httpRequestMember != null) - { - var rating = new Rating(1, EvaluationWeight.Low) - { - HttpRequestMember = new { Caller = typeof(THttpRequestMember).Name, Value =""}, - Member = new { Caller = typeof(TMockMember).Name, Value =""} - }; - - context.Ratings.Append(rating); - return null; - } - else if(item != null && httpRequestMember == null) - { - context.Fail(httpRequestMember, item); - return null; - } - - return httpRequestMember; - } - - public static void Match(this EvaluationContext context, T? httpRequestMember, object? matcherMember)where T : class - { - var rating = new Rating(1, EvaluationWeight.Low) - { - HttpRequestMember = httpRequestMember, - Member = matcherMember - }; - - context.Ratings.Append(rating); - } - - - public static void Fail(this EvaluationContext context, T? httpRequestMember, object? matcherMember) where T : class - { - var rating = new Rating(0, EvaluationWeight.Non) - { - HttpRequestMember = httpRequestMember, - Member = matcherMember - }; - - context.Ratings.Append(rating); - } - } -} +// using System.Linq; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; +// +// namespace NinjaTools.FluentMockServer.API.Extensions +// { +// public static class EvaluationExtensions +// { +// public static void AddExtraPoints(this EvaluationContext context, int bonus) => context.Ratings.BonusPoints += bonus; +// +// +// public static THttpRequestMember? EnsureNotNull(this EvaluationContext context,THttpRequestMember? httpRequestMember, TMockMember? item) +// where TMockMember : class +// where THttpRequestMember : class +// { +// if (item is null && httpRequestMember != null) +// { +// var rating = new Rating(1, EvaluationWeight.Low) +// { +// HttpRequestMember = new { Caller = typeof(THttpRequestMember).Name, Value =""}, +// Member = new { Caller = typeof(TMockMember).Name, Value =""} +// }; +// +// context.Ratings.Append(rating); +// return null; +// } +// else if(item != null && httpRequestMember == null) +// { +// context.Fail(httpRequestMember, item); +// return null; +// } +// +// return httpRequestMember; +// } +// +// public static void Match(this EvaluationContext context, T? httpRequestMember, object? matcherMember)where T : class +// { +// var rating = new Rating(1, EvaluationWeight.Low) +// { +// HttpRequestMember = httpRequestMember, +// Member = matcherMember +// }; +// +// context.Ratings.Append(rating); +// } +// +// +// public static void Fail(this EvaluationContext context, T? httpRequestMember, object? matcherMember) where T : class +// { +// var rating = new Rating(0, EvaluationWeight.Non) +// { +// HttpRequestMember = httpRequestMember, +// Member = matcherMember +// }; +// +// context.Ratings.Append(rating); +// } +// } +// } diff --git a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs index a734926a..47036ad1 100644 --- a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs +++ b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; @@ -31,9 +32,10 @@ public void Add(Setup setup) [CanBeNull] public Setup? TryGetMatchingSetup([NotNull] HttpContext context) { - return GetAll().FirstOrDefault(s => s.Matcher?.IsMatch(context) ?? false) is {} setup - ? setup - : null; + throw new NotImplementedException(); + // return GetAll().FirstOrDefault(s => s.Matcher?.IsMatch(context) ?? false) is {} setup + // ? setup + // : null; } } diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs index 576a26ef..fa39a22e 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs @@ -1,46 +1,45 @@ -using System; -using System.Diagnostics; -using System.IO; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; - -namespace NinjaTools.FluentMockServer.API.Models -{ - [DebuggerDisplay("{DebuggerDisplay()}")] - public class RequestBodyMatcher : IRequestBody - { - public string DebuggerDisplay() - { - return $"Type={Type.ToString()}; MatchExact={MatchExact.ToString()}; Content={Content ?? ""}"; - } - - public string? Content { get; set; } - public RequestBodyKind Type { get; set; } - public bool MatchExact { get; set; } - - - public bool IsMatch([NotNull] HttpRequest request) - { - request.EnableBuffering(); - request.Body.Position = 0; - using var reader = new StreamReader(request.Body); - var content = reader.ReadToEnd(); - - if (MatchExact) - { - return Content == content; - } - - return content.Contains(Content); - } - - - /// - public void Accept(Func visitorFactory) - { - var visitor = visitorFactory(); - visitor.VisitBody(this); - } - } -} +// using System; +// using System.Collections.Generic; +// using System.Diagnostics; +// using System.IO; +// using JetBrains.Annotations; +// using Microsoft.AspNetCore.Http; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Converters; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; +// +// namespace NinjaTools.FluentMockServer.API.Models +// { +// public class RequestBodyMatcher +// { +// public string? Content { get; set; } +// public RequestMatcher.RequestBodyKind Type { get; set; } +// public bool MatchExact { get; set; } +// } +// +// public class RequestMatcher +// { +// private string? _path; +// public string? Path +// { +// get => _path; +// set => _path = value is null +// ? null +// : $"/{value.TrimStart('/')}"; +// } +// +// public RequestBodyMatcher? BodyMatcher { get; set; } +// public string Method { get; set; } +// public Dictionary? Headers { get; set; } +// public Dictionary? Cookies { get; set; } +// public string? Query { get; set; } +// +// +// [JsonConverter(typeof(StringEnumConverter))] +// public enum RequestBodyKind +// { +// Text, +// Base64 +// } +// } +// } diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs index c621298d..1dd98ab8 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs @@ -1,175 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; - -namespace NinjaTools.FluentMockServer.API.Models -{ - - [DebuggerDisplay("{DebuggerDisplay()}")] - public class RequestMatcher : IRequest - { - public string DebuggerDisplay() - { - var sb = new StringBuilder(); - - if (!string.IsNullOrEmpty(Method)) - { - sb.Append($"Method={Method}; "); - } - - if (!string.IsNullOrEmpty(Path)) - { - sb.Append($"Path={Path}; "); - } - - if (!string.IsNullOrEmpty(Query)) - { - sb.Append($"Query={Query}; "); - } - - sb.Append($"Headers={Headers?.Count ?? 0};\n"); - - if (BodyMatcher != null) - { - sb.AppendLine(BodyMatcher.DebuggerDisplay()); - } - - return sb.ToString(); - } - - private string _path; - - public RequestBodyMatcher BodyMatcher { get; set; } - - public string Path - { - get => _path; - set => _path = value is null - ? null - : $"/{value.TrimStart('/')}"; - } - - public string Method { get; set; } - - public Dictionary? Headers { get; set; } - public Dictionary? Cookies { get; set; } - - - public string? Query { get; set; } - - public bool IsMatch(HttpContext context) - { - var request = context.Request; - return PathMatches(request.Path.Value) - && MethodMatches(request.Method) - && HeadersMatching(request.Headers) - && BodyMatches(request) - && QueryMatches(request.QueryString); - } - - private bool QueryMatches(QueryString requestQueryString) - { - if (!string.IsNullOrWhiteSpace(Query)) - { - return requestQueryString.Value == Query; - } - - return true; - } - - private bool BodyMatches(HttpRequest httpRequest) - { - - if (BodyMatcher != null) - { - return BodyMatcher.IsMatch(httpRequest); - } - - return true; - } - - private bool PathMatches(string path) - { - if (string.IsNullOrWhiteSpace(Path)) - { - return true; - } - - return Path.StartsWith(path); - } - - private bool MethodMatches(string requestMethod) - { - if (string.IsNullOrWhiteSpace(Method)) - { - return true; - } - - return requestMethod.ToUpper() == Method.ToUpper(); - } - - - private bool HeadersMatching(IHeaderDictionary requestHeaders) - { - if (Headers is null) - { - return true; - } - - foreach (var header in Headers) - { - - if (!requestHeaders.ContainsKey(header.Key)) - { - return false; - } - - if (requestHeaders.TryGetValue(header.Key, out var value)) - { - if (value.ToList().Except(header.Value).Any()) - { - return false; - } - } - } - - return true; - } - - /// - public void Accept(Func visitorFactory) - { - var visitor = visitorFactory(); - this.BodyMatcher.Accept(() => visitor); - - visitor.VisitHeaders(Headers); - visitor.VisitMethod(Method); - visitor.VisitPath(Path); - visitor.VisitQuery(Query); - visitor.VisitCookies(Cookies); - visitor.VisitBody(BodyMatcher); - visitor.Visit(this); - } - - /// - public T Accept(Func> visitorFactory) - { - var visitor = visitorFactory(); - Accept(() => (IRequestMatcherEvaluatorVistor) visitor); - return visitor.Evaluate(); - } - } - - [JsonConverter(typeof(StringEnumConverter))] - public enum RequestBodyKind - { - Text, - Base64 - } -} +// using System; +// using System.Collections.Generic; +// using System.Diagnostics; +// using System.Linq; +// using System.Text; +// using Microsoft.AspNetCore.Http; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Converters; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; +// +// namespace NinjaTools.FluentMockServer.API.Models +// { +// +// +// } diff --git a/src/NinjaTools.FluentMockServer.API/Models/Setup.cs b/src/NinjaTools.FluentMockServer.API/Models/Setup.cs index 5f14b824..6d6f5d1e 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/Setup.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/Setup.cs @@ -3,14 +3,8 @@ namespace NinjaTools.FluentMockServer.API.Models { - [DebuggerDisplay("{DebuggerDisplay()}")] public class Setup { - public string DebuggerDisplay() - { - return $"Matcher={Matcher?.DebuggerDisplay() ?? "*"}; Action={Action?.DebuggerDisplay() ?? ""}"; - } - [CanBeNull] public RequestMatcher? Matcher { get; set; } diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 58d4fd5b..cb30c996 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -41,6 +41,22 @@ + + + + + + + + + + + + + + + + diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Rules/IRule.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Rules/IRule.cs new file mode 100644 index 00000000..c4d351e4 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Rules/IRule.cs @@ -0,0 +1,7 @@ +namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Rules +{ + public interface IRule + { + + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeadersCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeadersCollection.cs new file mode 100644 index 00000000..8ba523a4 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeadersCollection.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Http; + +namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections +{ + + public class HeaderCollection : HeaderDictionary, IVisitable + { + public HeaderCollection() : this(new Dictionary()) + { } + + public HeaderCollection(IDictionary dict) + { + dict ??= new Dictionary(); + foreach (var (key, value) in dict) + { + Add(key, value); + } + } + + public static implicit operator HeaderCollection(Dictionary headers) => From(headers); + public static implicit operator Dictionary(HeaderCollection headers) => + headers.ToDictionary(k => k.Key, + v => v.Value.ToArray()); + + public static HeaderCollection From(IDictionary headers) + { + var headerCollection = new HeaderCollection(); + headers ??= new Dictionary(); + foreach (var header in headers) + { + headerCollection.Add(header.Key, header.Value); + } + + return headerCollection; + } + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } + + public class CookieCollection : Dictionary, IVisitable + { + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } + + public class PathCollection : IVisitable + { + public static implicit operator string(PathCollection? query) => query?.Path; + public static implicit operator PathCollection(string? str) => new PathCollection {PathString = $"/{str?.TrimStart('/')}"}; + + /// + public PathString PathString { get; set; } + public string Path => PathString.Value; + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } + + public class QueryCollection : IVisitable + { + public static implicit operator string(QueryCollection query) => query.Query; + public static implicit operator QueryCollection(QueryString query) => new QueryCollection {QuryString = query}; + public static implicit operator QueryCollection(string str) => new QueryCollection {QuryString = new QueryString($"?{str.TrimStart('?')}")}; + + /// + public QueryString QuryString { get; set; } + public string Query => QuryString.Value; + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } + + public class HttpMethodWrapper : IVisitable + { + public HttpMethodWrapper(string methodString) + { + MethodString = methodString; + } + + public HttpMethodWrapper() + { } + + public string MethodString { get; set; } + public static implicit operator string(HttpMethodWrapper wrapper) => wrapper.MethodString; + public static implicit operator HttpMethodWrapper(string str) => new HttpMethodWrapper {MethodString = str}; + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs new file mode 100644 index 00000000..e3867967 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs @@ -0,0 +1,221 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections; +using QueryCollection = NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections.QueryCollection; +// ReSharper disable PossibleLossOfFraction + +namespace NinjaTools.FluentMockServer.API.Proxy.Visitors +{ + public class ComparasionVisitor : IVisitor, + IVisitor, + IVisitor, + IVisitor, + IVisitor, + IVisitor, + IVisitor, + IVisitor + { + protected HttpContext HttpContext { get; } + protected HttpRequest HttpRequest => HttpContext.Request; + public bool IsSuccess { get; set; } = true; + public double Score { get; set; } = 0; + + + protected virtual void Fail() + { + IsSuccess = false; + //throw new ExecutionEngineException("FAIL"); + } + + protected virtual double? Pass() + { + Score += 1; + return Score; + } + + public ComparasionVisitor(HttpContext httpContext) + { + HttpContext = httpContext; + } + + /// + /// ✅ Entrypoint ✅ + public void Visit(RequestMatcher visitable, CancellationToken token = default) + { + Visit(visitable.Cookies); + Visit(visitable.Headers); + Visit(visitable.Query); + Visit(visitable.Path); + Visit(visitable.Method); + Visit(visitable.BodyMatcher); + } + + /// + public void Visit(RequestBodyMatcher visitable, CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + if (visitable is null || string.IsNullOrWhiteSpace(visitable.Content)) + { + Pass(); + return; + } + + if (HttpRequest.Body is null) + { + Fail(); + return; + } + + HttpRequest.EnableBuffering(); + HttpRequest.Body.Seek(0, SeekOrigin.Begin); + using var reader = new StreamReader(HttpRequest.Body); + var httpBody = reader.ReadToEnd(); + + if (visitable.MatchExact && httpBody == visitable.Content) + { + Pass(); + return; + } + + Fail(); + + } + + /// + public void Visit(HeaderDictionary visitable, CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + if (visitable is null) + { + Pass(); + return; + } + + if (HttpRequest.Headers is null && visitable is { } v && v.Any()) + { + Fail(); + return; + } + + var rest = visitable?.Except(HttpRequest.Headers); + + if (rest is {} r && r.Any() ) + { + Fail(); + return; + } + + Pass(); + return; + + } + + /// + public void Visit(CookieCollection visitable, CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + if (visitable is null) + { + Pass(); + return; + } + if (HttpRequest.Cookies is null && visitable is { } v && v.Any()) + { + Fail(); + return; + } + + var rest = visitable?.Except(HttpRequest.Cookies); + + if (rest is {} r && r.Any() ) + { + Fail(); + return; + } + Pass(); + return; + } + + /// + public void Visit(QueryCollection visitable, CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + if (visitable is null) + { + Pass(); + return; + } + + if (!HttpRequest.QueryString.HasValue && visitable is { } v && v.QuryString.HasValue) + { + Fail(); + return; + } + if (visitable?.Query == HttpRequest.QueryString.Value) + { + Pass(); + return; + } + + Fail(); + } + + /// + public void Visit(PathCollection visitable, CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + if (visitable is null) + { + Pass(); + return; + } + if (!HttpRequest.Path.HasValue && visitable is { } v && v.PathString.HasValue) + { + Fail(); + return; + } + if (visitable?.Path == HttpRequest.Path.Value) + { + Pass(); + return; + } + + Fail(); + return; + + } + + /// + public void Visit(HttpMethodWrapper visitable, CancellationToken token = default) + { + token.ThrowIfCancellationRequested(); + + if (visitable is null) + { + Pass(); + return; + } + if (string.IsNullOrWhiteSpace(HttpRequest.Method) && visitable is { } v && !string.IsNullOrWhiteSpace(v.MethodString)) + { + Fail(); + return; + } + if (visitable.MethodString == HttpRequest.Method) + { + Pass(); + return; + } + + Fail(); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitor.cs new file mode 100644 index 00000000..9468e2f7 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitor.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Threading; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections; +using QueryCollection = NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections.QueryCollection; + +namespace NinjaTools.FluentMockServer.API.Models +{ + public class RequestBodyMatcher + { + public string? Content { get; set; } + public RequestBodyKind Kind { get; set; } + public bool MatchExact { get; set; } + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum RequestBodyKind + { + Text, + Base64 + } + public class RequestMatcher + { + public RequestBodyMatcher? BodyMatcher { get; set; } + public HttpMethodWrapper? Method { get; set; } + public HeaderCollection? Headers { get; set; } + public CookieCollection? Cookies { get; set; } + public QueryCollection? Query { get; set; } + public PathCollection? Path { get; set; } + } +} + +namespace NinjaTools.FluentMockServer.API.Proxy.Visitors +{ + public interface IVisitor + { } + + public interface IVisitor + { + void Visit(TVisitable visitable) => Visit(visitable,default); + void Visit(TVisitable visitable, CancellationToken token); + } + + public interface IVisitable + { + void Accept(IVisitor visitor); + } +} diff --git a/src/NinjaTools.FluentMockServer.TestContainers/NinjaTools.FluentMockServer.TestContainers.csproj b/src/NinjaTools.FluentMockServer.TestContainers/NinjaTools.FluentMockServer.TestContainers.csproj deleted file mode 100644 index e69de29b..00000000 diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestBodyMatcherTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestBodyMatcherTests.cs index c6fbd2e4..f0806d95 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestBodyMatcherTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestBodyMatcherTests.cs @@ -1,103 +1,103 @@ -using System.IO; -using System.Text; -using FluentAssertions; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Models; -using NinjaTools.FluentMockServer.Tests.TestHelpers; -using Xunit; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.API.Tests.Models -{ - public class RequestBodyMatcherTests : XUnitTestBase - { - /// - public RequestBodyMatcherTests(ITestOutputHelper output) : base(output) - { - } - - private HttpContext CreateContext([CanBeNull] string textContent = null, byte[] byteContent = null) - { - if (textContent != null) - { - byteContent = Encoding.UTF8.GetBytes(textContent); - } - - if (byteContent != null) - { - var stream = new MemoryStream(); - stream.Write(byteContent); - - return new DefaultHttpContext - { - Request = - { - Body = stream, - ContentLength = stream.Length - } - }; - } - - return new DefaultHttpContext(); - } - - [NotNull] - private RequestBodyMatcher CreateSubject(RequestBodyKind type, bool matchExact, string content) - { - return new RequestBodyMatcher - { - Content = content, - Type = type, - MatchExact = matchExact - }; - } - - [Fact] - public void Should_Match_Exact_String_Body() - { - // Arrange - const string content = "{\"hello\":\"world!\"}"; - var subject = CreateSubject(RequestBodyKind.Text, true, content); - var context = CreateContext(content); - - // Act & Assert - subject.IsMatch(context.Request).Should().BeTrue(); - } - - [Fact] - public void Should_Match_String_Body_If_Contains_String() - { - // Arrange - const string content = "{\"hello\":\"world!\"}"; - var subject = CreateSubject(RequestBodyKind.Text, false, "world!\"}"); - var context = CreateContext(content); - - // Act & Assert - subject.IsMatch(context.Request).Should().BeTrue(); - } - - [Fact] - public void Should_Not_Match_Exact_String_Body_When_Not_Exact_Same_String_Content() - { - // Arrange - var subject = CreateSubject(RequestBodyKind.Text, true, "{\"hello\":\"car!\"}"); - var context = CreateContext("{\"hello\":\"world!\"}"); - - // Act & Assert - subject.IsMatch(context.Request).Should().BeFalse(); - } - - [Fact] - public void Should_Not_Match_String_Body_If_Not_Contains_String() - { - // Arrange - const string content = "{\"hello\":\"world!\"}"; - var subject = CreateSubject(RequestBodyKind.Text, false, "car!\"}"); - var context = CreateContext(content); - - // Act & Assert - subject.IsMatch(context.Request).Should().BeFalse(); - } - } -} +// using System.IO; +// using System.Text; +// using FluentAssertions; +// using JetBrains.Annotations; +// using Microsoft.AspNetCore.Http; +// using NinjaTools.FluentMockServer.API.Models; +// using NinjaTools.FluentMockServer.Tests.TestHelpers; +// using Xunit; +// using Xunit.Abstractions; +// +// namespace NinjaTools.FluentMockServer.API.Tests.Models +// { +// public class RequestBodyMatcherTests : XUnitTestBase +// { +// /// +// public RequestBodyMatcherTests(ITestOutputHelper output) : base(output) +// { +// } +// +// private HttpContext CreateContext([CanBeNull] string textContent = null, byte[] byteContent = null) +// { +// if (textContent != null) +// { +// byteContent = Encoding.UTF8.GetBytes(textContent); +// } +// +// if (byteContent != null) +// { +// var stream = new MemoryStream(); +// stream.Write(byteContent); +// +// return new DefaultHttpContext +// { +// Request = +// { +// Body = stream, +// ContentLength = stream.Length +// } +// }; +// } +// +// return new DefaultHttpContext(); +// } +// +// [NotNull] +// private RequestBodyMatcher CreateSubject(RequestBodyKind type, bool matchExact, string content) +// { +// return new RequestBodyMatcher +// { +// Content = content, +// Type = type, +// MatchExact = matchExact +// }; +// } +// +// [Fact] +// public void Should_Match_Exact_String_Body() +// { +// // Arrange +// const string content = "{\"hello\":\"world!\"}"; +// var subject = CreateSubject(RequestBodyKind.Text, true, content); +// var context = CreateContext(content); +// +// // Act & Assert +// subject.IsMatch(context.Request).Should().BeTrue(); +// } +// +// [Fact] +// public void Should_Match_String_Body_If_Contains_String() +// { +// // Arrange +// const string content = "{\"hello\":\"world!\"}"; +// var subject = CreateSubject(RequestBodyKind.Text, false, "world!\"}"); +// var context = CreateContext(content); +// +// // Act & Assert +// subject.IsMatch(context.Request).Should().BeTrue(); +// } +// +// [Fact] +// public void Should_Not_Match_Exact_String_Body_When_Not_Exact_Same_String_Content() +// { +// // Arrange +// var subject = CreateSubject(RequestBodyKind.Text, true, "{\"hello\":\"car!\"}"); +// var context = CreateContext("{\"hello\":\"world!\"}"); +// +// // Act & Assert +// subject.IsMatch(context.Request).Should().BeFalse(); +// } +// +// [Fact] +// public void Should_Not_Match_String_Body_If_Not_Contains_String() +// { +// // Arrange +// const string content = "{\"hello\":\"world!\"}"; +// var subject = CreateSubject(RequestBodyKind.Text, false, "car!\"}"); +// var context = CreateContext(content); +// +// // Act & Assert +// subject.IsMatch(context.Request).Should().BeFalse(); +// } +// } +// } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs index a6ef8c1b..90efb72f 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/RequestMatcherTests.cs @@ -1,251 +1,251 @@ -using System.Collections.Generic; -using System.IO; -using System.Text; -using FluentAssertions; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Models; -using Xunit; - -namespace NinjaTools.FluentMockServer.API.Tests.Models -{ - public class RequestMatcherTests - { - [Theory] - [InlineData("GET")] - [InlineData("POST")] - [InlineData("PUT")] - [InlineData("OPTIONS")] - public void When_IsMatch_Returns_True_When_Method_Is_Equal(string method) - { - // Arrange - var subject = CreateSubject(method: method); - var context = CreateContext(method); - - // Act& Assert - subject.IsMatch(context).Should().BeTrue(); - } - - [Theory] - [InlineData("/some(abc/path")] - [InlineData("/some/abc/path")] - [InlineData("/some/abc/path?query=json")] - [InlineData("/opt")] - public void When_IsMatch_Returns_True_When_Path_Is_Match(string path) - { - // Arrange - var subject = CreateSubject(path: path); - var context = CreateContext(path: path); - - // Act& Assert - subject.IsMatch(context).Should().BeTrue(); - } - - [Theory] - [MemberData(nameof(GetHeaderTestData))] - public void When_IsMatch_Returns_True_When_Headers_Are_Equal(Dictionary request, Dictionary contextHeaders, bool isValid) - { - // Arrange - var subject = CreateSubject(headers: request); - var context = CreateContext(headers: contextHeaders); - - // Act& Assert - subject.IsMatch(context).Should().Be(isValid); - } - - [ItemNotNull] - public static IEnumerable GetHeaderTestData() - { - yield return new object[] - { - new Dictionary(), - new Dictionary(), - true - }; - yield return new object[] - { - null, - new Dictionary(), - true - }; - yield return new object[] - { - new Dictionary - { - { - "Host", new[] {"Mock-Server"} - } - }, - new Dictionary(), - false - }; - yield return new object[] - { - new Dictionary - { - { - "Host", new[] {"Mock-Server"} - } - }, - new Dictionary - { - { - "Host", new[] {"Mock-Server"} - } - }, - true - }; - yield return new object[] - { - new Dictionary - { - { - "Some-Header", new[] {true.ToString(), 123.ToString()} - } - }, - new Dictionary - { - { - "another.header", new[] {"with some other value"} - } - }, - false - }; - } - - - private HttpContext CreateContext(string method = null, string path = null, Dictionary headers = null, Stream requestBodyStream = null, QueryString? queryString = null) - { - var context = requestBodyStream is null - ? new DefaultHttpContext() - : new DefaultHttpContext - { - Request = - { - Body = requestBodyStream - } - }; - - var request = context.Request; - request.Method = method; - request.Path = new PathString(path); - - if (queryString != null) - { - request.QueryString = queryString.Value; - } - - foreach (var header in headers ?? new Dictionary()) - { - request.Headers.Add(header.Key, header.Value); - } - - return context; - } - - private RequestMatcher CreateSubject(string method = null, string path = null, IDictionary headers = null, RequestBodyMatcher bodyMatcher = null, - QueryString? queryString = null) - { - return new RequestMatcher - { - Method = method, - Path = path, - Headers = new Dictionary(headers ?? new Dictionary()), - BodyMatcher = bodyMatcher, - Query = queryString?.Value - }; - } - - [Fact] - public void IsMatch_Should_Be_False_When_QueryString_Not_Same() - { - // Arrange - var subject = CreateSubject(queryString: new QueryString("?color=green")); - var context = CreateContext(queryString: new QueryString("?id=100")); - - // Act & Assert - subject.IsMatch(context).Should().BeFalse(); - } - - [Fact] - public void IsMatch_Should_Be_False_When_RequestBodyMatcher_IsNotMatch() - { - // Arrange - var requestBodyContent = "some content"; - var requestBodyStream = new MemoryStream(); - var bytes = Encoding.UTF8.GetBytes(requestBodyContent); - requestBodyStream.Write(bytes); - var bodyMatcher = new RequestBodyMatcher - { - Content = "hello world", - Type = RequestBodyKind.Text, - MatchExact = true - }; - - var subject = CreateSubject(bodyMatcher: bodyMatcher); - var context = CreateContext(requestBodyStream: requestBodyStream); - - // Act & Assert - subject.IsMatch(context).Should().BeFalse(); - } - - - [Fact] - public void IsMatch_Should_Be_True_When_No_RequestBodyMatcher() - { - // Arrange - var subject = CreateSubject(); - var context = CreateContext(); - - // Act & Assert - subject.IsMatch(context).Should().BeTrue(); - } - - [Fact] - public void IsMatch_Should_Be_True_When_QueryString_IsMatch() - { - // Arrange - var query = new QueryString("?id=100"); - var subject = CreateSubject(queryString: query); - var context = CreateContext(queryString: query); - - // Act & Assert - subject.IsMatch(context).Should().BeTrue(); - } - - [Fact] - public void IsMatch_Should_Be_True_When_QueryString_Not_Set() - { - // Arrange - var query = new QueryString("?id=100"); - var subject = CreateSubject(); - var context = CreateContext(queryString: query); - - // Act & Assert - subject.IsMatch(context).Should().BeTrue(); - } - - - [Fact] - public void IsMatch_Should_Be_True_When_RequestBodyMatcher_IsMatch() - { - // Arrange - var requestBodyContent = "some content"; - var requestBodyStream = new MemoryStream(); - var bytes = Encoding.UTF8.GetBytes(requestBodyContent); - requestBodyStream.Write(bytes); - var bodyMatcher = new RequestBodyMatcher - { - Content = requestBodyContent, - Type = RequestBodyKind.Text, - MatchExact = true - }; - - var subject = CreateSubject(bodyMatcher: bodyMatcher); - var context = CreateContext(requestBodyStream: requestBodyStream); - - // Act & Assert - subject.IsMatch(context).Should().BeTrue(); - } - } -} +// using System.Collections.Generic; +// using System.IO; +// using System.Text; +// using FluentAssertions; +// using JetBrains.Annotations; +// using Microsoft.AspNetCore.Http; +// using NinjaTools.FluentMockServer.API.Models; +// using Xunit; +// +// namespace NinjaTools.FluentMockServer.API.Tests.Models +// { +// public class RequestMatcherTests +// { +// [Theory] +// [InlineData("GET")] +// [InlineData("POST")] +// [InlineData("PUT")] +// [InlineData("OPTIONS")] +// public void When_IsMatch_Returns_True_When_Method_Is_Equal(string method) +// { +// // Arrange +// var subject = CreateSubject(method: method); +// var context = CreateContext(method); +// +// // Act& Assert +// subject.IsMatch(context).Should().BeTrue(); +// } +// +// [Theory] +// [InlineData("/some(abc/path")] +// [InlineData("/some/abc/path")] +// [InlineData("/some/abc/path?query=json")] +// [InlineData("/opt")] +// public void When_IsMatch_Returns_True_When_Path_Is_Match(string path) +// { +// // Arrange +// var subject = CreateSubject(path: path); +// var context = CreateContext(path: path); +// +// // Act& Assert +// subject.IsMatch(context).Should().BeTrue(); +// } +// +// [Theory] +// [MemberData(nameof(GetHeaderTestData))] +// public void When_IsMatch_Returns_True_When_Headers_Are_Equal(Dictionary request, Dictionary contextHeaders, bool isValid) +// { +// // Arrange +// var subject = CreateSubject(headers: request); +// var context = CreateContext(headers: contextHeaders); +// +// // Act& Assert +// subject.IsMatch(context).Should().Be(isValid); +// } +// +// [ItemNotNull] +// public static IEnumerable GetHeaderTestData() +// { +// yield return new object[] +// { +// new Dictionary(), +// new Dictionary(), +// true +// }; +// yield return new object[] +// { +// null, +// new Dictionary(), +// true +// }; +// yield return new object[] +// { +// new Dictionary +// { +// { +// "Host", new[] {"Mock-Server"} +// } +// }, +// new Dictionary(), +// false +// }; +// yield return new object[] +// { +// new Dictionary +// { +// { +// "Host", new[] {"Mock-Server"} +// } +// }, +// new Dictionary +// { +// { +// "Host", new[] {"Mock-Server"} +// } +// }, +// true +// }; +// yield return new object[] +// { +// new Dictionary +// { +// { +// "Some-Header", new[] {true.ToString(), 123.ToString()} +// } +// }, +// new Dictionary +// { +// { +// "another.header", new[] {"with some other value"} +// } +// }, +// false +// }; +// } +// +// +// private HttpContext CreateContext(string method = null, string path = null, Dictionary headers = null, Stream requestBodyStream = null, QueryString? queryString = null) +// { +// var context = requestBodyStream is null +// ? new DefaultHttpContext() +// : new DefaultHttpContext +// { +// Request = +// { +// Body = requestBodyStream +// } +// }; +// +// var request = context.Request; +// request.Method = method; +// request.Path = new PathString(path); +// +// if (queryString != null) +// { +// request.QueryString = queryString.Value; +// } +// +// foreach (var header in headers ?? new Dictionary()) +// { +// request.Headers.Add(header.Key, header.Value); +// } +// +// return context; +// } +// +// private RequestMatcher CreateSubject(string method = null, string path = null, IDictionary headers = null, RequestBodyMatcher bodyMatcher = null, +// QueryString? queryString = null) +// { +// return new RequestMatcher +// { +// Method = method, +// Path = path, +// Headers = new Dictionary(headers ?? new Dictionary()), +// BodyMatcher = bodyMatcher, +// Query = queryString?.Value +// }; +// } +// +// [Fact] +// public void IsMatch_Should_Be_False_When_QueryString_Not_Same() +// { +// // Arrange +// var subject = CreateSubject(queryString: new QueryString("?color=green")); +// var context = CreateContext(queryString: new QueryString("?id=100")); +// +// // Act & Assert +// subject.IsMatch(context).Should().BeFalse(); +// } +// +// [Fact] +// public void IsMatch_Should_Be_False_When_RequestBodyMatcher_IsNotMatch() +// { +// // Arrange +// var requestBodyContent = "some content"; +// var requestBodyStream = new MemoryStream(); +// var bytes = Encoding.UTF8.GetBytes(requestBodyContent); +// requestBodyStream.Write(bytes); +// var bodyMatcher = new RequestBodyMatcher +// { +// Content = "hello world", +// Type = RequestBodyKind.Text, +// MatchExact = true +// }; +// +// var subject = CreateSubject(bodyMatcher: bodyMatcher); +// var context = CreateContext(requestBodyStream: requestBodyStream); +// +// // Act & Assert +// subject.IsMatch(context).Should().BeFalse(); +// } +// +// +// [Fact] +// public void IsMatch_Should_Be_True_When_No_RequestBodyMatcher() +// { +// // Arrange +// var subject = CreateSubject(); +// var context = CreateContext(); +// +// // Act & Assert +// subject.IsMatch(context).Should().BeTrue(); +// } +// +// [Fact] +// public void IsMatch_Should_Be_True_When_QueryString_IsMatch() +// { +// // Arrange +// var query = new QueryString("?id=100"); +// var subject = CreateSubject(queryString: query); +// var context = CreateContext(queryString: query); +// +// // Act & Assert +// subject.IsMatch(context).Should().BeTrue(); +// } +// +// [Fact] +// public void IsMatch_Should_Be_True_When_QueryString_Not_Set() +// { +// // Arrange +// var query = new QueryString("?id=100"); +// var subject = CreateSubject(); +// var context = CreateContext(queryString: query); +// +// // Act & Assert +// subject.IsMatch(context).Should().BeTrue(); +// } +// +// +// [Fact] +// public void IsMatch_Should_Be_True_When_RequestBodyMatcher_IsMatch() +// { +// // Arrange +// var requestBodyContent = "some content"; +// var requestBodyStream = new MemoryStream(); +// var bytes = Encoding.UTF8.GetBytes(requestBodyContent); +// requestBodyStream.Write(bytes); +// var bodyMatcher = new RequestBodyMatcher +// { +// Content = requestBodyContent, +// Type = RequestBodyKind.Text, +// MatchExact = true +// }; +// +// var subject = CreateSubject(bodyMatcher: bodyMatcher); +// var context = CreateContext(requestBodyStream: requestBodyStream); +// +// // Act & Assert +// subject.IsMatch(context).Should().BeTrue(); +// } +// } +// } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/ComparasionVisitorTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/ComparasionVisitorTests.cs new file mode 100644 index 00000000..0243a1d2 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/ComparasionVisitorTests.cs @@ -0,0 +1,282 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using FluentAssertions; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Proxy.Visitors; +using NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.API.Tests.Proxy +{ + public class ComparasionVisitorTests : XUnitTestBase + { + /// + public ComparasionVisitorTests(ITestOutputHelper output) : base(output) + { } + + [Theory] + [InlineData("GET")] + [InlineData("POST")] + [InlineData("PUT")] + [InlineData("OPTIONS")] + public void When_IsMatch_Returns_True_When_Method_Is_Equal(string method) + { + // Arrange + var compare = new HttpMethodWrapper(method); + var context = CreateContext(method); + var subject = new ComparasionVisitor(context); + + // Act& Assert + subject.Visit(compare); + subject.IsSuccess.Should().BeTrue(); + subject.Score.Should().Be(1); + } + + [Theory] + [InlineData("/some(abc/path")] + [InlineData("/some/abc/path")] + [InlineData("/some/abc/path?query=json")] + [InlineData("/opt")] + public void When_IsMatch_Returns_True_When_Path_Is_Match(string path) + { + // Arrange + var compare = CreateObject(path: path); + var context = CreateContext(path: path); + var subject = new ComparasionVisitor(context); + + // Act& Assert + subject.Visit(compare); + subject.IsSuccess.Should().BeTrue(); + subject.Score.Should().BeGreaterThan(1); + } + + [Theory] + [MemberData(nameof(GetHeaderTestData))] + public void When_IsMatch_Returns_True_When_Headers_Are_Equal(Dictionary request, Dictionary contextHeaders, bool isValid) + { + // Arrange + var compare = new HeaderCollection(request); + var context = CreateContext(headers: contextHeaders); + var subject = new ComparasionVisitor(context); + + // Act& Assert + subject.Visit(compare); + subject.IsSuccess.Should().Be(isValid); + subject.Score.Should().Be(isValid ? 1 : 0); + } + + [ItemNotNull] + public static IEnumerable GetHeaderTestData() + { + yield return new object[] + { + new Dictionary(), + new Dictionary(), + true + }; + yield return new object[] + { + null, + new Dictionary(), + true + }; + yield return new object[] + { + new Dictionary + { + { + "Host", new[] {"Mock-Server"} + } + }, + new Dictionary(), + false + }; + yield return new object[] + { + new Dictionary + { + { + "Host", new[] {"Mock-Server"} + } + }, + new Dictionary + { + { + "Host", new[] {"Mock-Server"} + } + }, + true + }; + yield return new object[] + { + new Dictionary + { + { + "Some-Header", new[] {true.ToString(), 123.ToString()} + } + }, + new Dictionary + { + { + "another.header", new[] {"with some other value"} + } + }, + false + }; + } + + + private HttpContext CreateContext(string method = null, string path = null, Dictionary headers = null, Stream requestBodyStream = null, QueryString? queryString = null) + { + var context = requestBodyStream is null + ? new DefaultHttpContext() + : new DefaultHttpContext + { + Request = + { + Body = requestBodyStream + } + }; + + var request = context.Request; + request.Method = method; + request.Path = new PathString(path); + + if (queryString != null) + { + request.QueryString = queryString.Value; + } + + foreach (var header in headers ?? new Dictionary()) + { + request.Headers.Add(header.Key, header.Value); + } + + return context; + } + + private RequestMatcher CreateObject( + string method = null, + string path = null, + Dictionary headers = null, + RequestBodyMatcher bodyMatcher = null, + QueryString? queryString = null) + { + return new RequestMatcher + { + Method = method, + Path = path, + Headers = headers, + BodyMatcher = bodyMatcher, + Query = queryString + }; + } + + [Fact] + public void IsMatch_Should_Be_False_When_QueryString_Not_Same() + { + // Arrange + var context = CreateContext(queryString: new QueryString("?id=100")); + var sut = new ComparasionVisitor(context); + + // Act & Assert + sut.Visit(new QueryString("?color=green")); + sut.IsSuccess.Should().BeFalse(); + } + + [Fact] + public void IsMatch_Should_Be_False_When_RequestBodyMatcher_IsNotMatch() + { + // Arrange + var requestBodyContent = "some content"; + var requestBodyStream = new MemoryStream(); + var bytes = Encoding.UTF8.GetBytes(requestBodyContent); + requestBodyStream.Write(bytes); + var bodyMatcher = new RequestBodyMatcher + { + Content = "hello world", + Kind = RequestBodyKind.Text, + MatchExact = true + }; + + var context = CreateContext(requestBodyStream: requestBodyStream); + var subject = new ComparasionVisitor(context); + + // Act& Assert + subject.Visit(bodyMatcher); + subject.IsSuccess.Should().BeFalse(); + subject.Score.Should().Be(0); + } + + + [Fact] + public void IsMatch_Should_Be_True_When_No_RequestBodyMatcher() + { + // Arrange + var context = CreateContext(); + var subject = new ComparasionVisitor(context); + + // Act& Assert + subject.Visit((RequestBodyMatcher) null); + subject.IsSuccess.Should().BeTrue(); + subject.Score.Should().Be(1); + } + + [Fact] + public void IsMatch_Should_Be_True_When_QueryString_IsMatch() + { + // Arrange + var query = new QueryString("?id=100"); + var context = CreateContext(queryString: query); + var subject = new ComparasionVisitor(context); + + // Act& Assert + subject.Visit(query); + subject.IsSuccess.Should().BeTrue(); + subject.Score.Should().Be(1); + } + + [Fact] + public void IsMatch_Should_Be_True_When_QueryString_Not_Set() + { + // Arrange + var context = CreateContext(queryString: new QueryString("?id=100")); + var subject = new ComparasionVisitor(context); + + // Act& Assert + subject.Visit((API.Proxy.Visitors.Collections.QueryCollection) null); + subject.IsSuccess.Should().BeTrue(); + subject.Score.Should().Be(1); + } + + + [Fact] + public void IsMatch_Should_Be_True_When_RequestBodyMatcher_IsMatch() + { + // Arrange + var requestBodyContent = "some content"; + var requestBodyStream = new MemoryStream(); + var bytes = Encoding.UTF8.GetBytes(requestBodyContent); + requestBodyStream.Write(bytes); + var bodyMatcher = new RequestBodyMatcher + { + Content = requestBodyContent, + Kind = RequestBodyKind.Text, + MatchExact = true + }; + + var context = CreateContext(requestBodyStream: requestBodyStream); + var subject = new ComparasionVisitor(context); + + // Act& Assert + subject.Visit(bodyMatcher); + subject.IsSuccess.Should().BeTrue(); + subject.Score.Should().Be(1); + } + } +} diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs index 62d6cf61..2e66a8ba 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/Matchers/EvaluatorTests.cs @@ -1,261 +1,261 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using FluentAssertions; -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Models; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; -using NinjaTools.FluentMockServer.Tests.TestHelpers; -using Xunit; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.API.Tests.Proxy.Matchers -{ - - public class EvaluatorTestData : TheoryData - { - - protected static HttpContext GetContext(Action setup) - { - var context = new DefaultHttpContext(); - setup(context.Request); - return context; - } - - protected EvaluationResultBase Eval(int score, bool valid) - { - return new ResultProxy(valid, new EvaluationContext(new DefaultHttpContext())); - } - - public class ResultProxy : EvaluationResultBase -{ - /// - public ResultProxy(bool math, EvaluationContext context) : base(context) - { - } - - /// - public override bool IsMatch { get; } -} - - - public class ValidRequestMatcherTestData : EvaluatorTestData - { - public ValidRequestMatcherTestData() - { - Data1(); - } - - private void Data1() - { - // *should* match anything - var matcher = new RequestMatcher(); - var context = GetContext(r => - { - r.Method = HttpMethods.Delete; - r.Path = "/cars/buy/id/200"; - r.ContentType = "application/json"; - }); - var eval = Eval(10, true); - Add(matcher, context, eval); - } - } - - - public class InvalidRequestMatcherTestData : EvaluatorTestData - { - public InvalidRequestMatcherTestData() - { - Data1(); - Data2(); - } - - private void Data1() - { - var reqA = new RequestMatcher - { - Path = "/some/path", - Cookies = new Dictionary - { - {"abcd", "application/json"} - }, - Query = "?id=0", - Method = "GET", - Headers = new Dictionary - { - { "a", new [] {""}} - }, - BodyMatcher = new RequestBodyMatcher - { - Content = "?" - } - }; - var evalA = Eval(0, false); - var ctxA = GetContext(r => - { - r.Method = HttpMethods.Delete; - - }); - Add(reqA, ctxA, evalA); - } - - private void Data2() - { - var reqA = new RequestMatcher - { - Path = "/x&/y(", - Query = "?id=123" - }; - var evalA = Eval(6, false); - var ctxA = GetContext(r => { }); - - Add(reqA, ctxA, evalA); - } - } - } - - - - public class EvaluatorTests: XUnitTestBase - { - /// - public EvaluatorTests(ITestOutputHelper output) : base(output) - { } - - [Theory] - [ClassData(typeof(EvaluatorTestData.InvalidRequestMatcherTestData))] - public void Should_Return_EvaluationUnsuccessfulResult_When_Not_Matching(RequestMatcher matcher, HttpContext context, EvaluationResultBase expectedResult) - { - // Act && Assert - var visitor = new RequestEvaluatorVistor(new EvaluationContext(context)); - var result = visitor.Evaluate(); - Dump(matcher, "matcher"); - result.Should().As().Should().Be(false); - result.IsMatch.Should().BeFalse(); - result.Score.Should().Be(expectedResult.Score); - } - - [Theory] - [ClassData(typeof(EvaluatorTestData.ValidRequestMatcherTestData))] - public void Should_Return_EvaluationSuccessfulResult_When_Matching(RequestMatcher matcher, HttpContext context, EvaluationResultBase expectedResult) - { - // Act && Assert - var visitor = new RequestEvaluatorVistor(new EvaluationContext(context)); - var result = matcher.Accept(() => visitor); - Dump(matcher, "matcher"); - result.Should().As().Should().Be(false); - result.IsMatch.Should().Be(true); - result.Score.Should().Be(expectedResult.Score); - } - - // - // [Theory] - // [MemberData(nameof(GetScoreTestData))] - // public void Score_Should_Be_Higher_For_The_Matcher_Who_Is_Closer_To_The_HttpRequest(RequestMatcher more, HttpContext context, RequestMatcher less) - // { - // // Act - // var a = EvaluationPipeline.Evaluate(context, more); - // Dump(a, "more"); - // - // var b = EvaluationPipeline.Evaluate(context, less); - // Dump(b, "less"); - // - // // Assert - // a.Score.Should().BeGreaterOrEqualTo(b.Score); - // } - - public static IEnumerable GetScoreTestData() - { - yield return Build(req => - { - req.Path = "/a/b"; - req.Method = "POST"; - }, - less => less.Path = "z", - ctx => - { - ctx.Request.Path = PathString.FromUriComponent("/a/b"); - ctx.Request.Method = "POST"; - }); - - yield return Build(req => - { - req.Path = "/a/b"; - req.Method = "POST"; - req.BodyMatcher = new RequestBodyMatcher - { - Content = "?", - MatchExact = true - }; - }, - less => - { - less.Path = "/some/path"; - less.Cookies = new Dictionary - { - {"abcd", "application/json"} - }; - less.Query = "?id=0"; - less.Method = "GET"; - less.Headers = new Dictionary - { - {"a", new[] {""}} - }; - }, - ctx => - { - ctx.Request.Cookies.Append(new KeyValuePair("abcd", "application/json")); - ctx.Request.Path = PathString.FromUriComponent("/a/b"); - ctx.Request.Method = "POST"; - }); - - yield return Build(req => - { - req.BodyMatcher = new RequestBodyMatcher - { - Content = "hello world", - MatchExact = true, - Type = RequestBodyKind.Text - }; - }, - less => less.Path = "/a/b/*", - ctx => - { - ctx.Request.Path = PathString.FromUriComponent("/a/b"); - ctx.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("hello world")); - }); - - yield return Build(req => req.Path = "/a/b/*", - less => less.Path = "/a/b", - ctx => - { - ctx.Request.Path = PathString.FromUriComponent("/a/b"); - ctx.Request.Method = "POST"; - ctx.Request.Cookies.Append(new KeyValuePair("abcd", "application/json")); - ctx.Request.Headers.Add("a", new[] {""}); - }); - } - - - - - - - - private static object[] Build(Action factory, Action lessAction, Action ctxFac) - { - var ctx = new DefaultHttpContext(); - ctxFac(ctx); - var more = new RequestMatcher(); - var less = new RequestMatcher(); - factory(more); - factory(less); - lessAction(less); - return new object[] {more, ctx, less }; - } - } -} +// using System; +// using System.Collections.Generic; +// using System.IO; +// using System.Linq; +// using System.Text; +// using FluentAssertions; +// using Microsoft.AspNetCore.Http; +// using NinjaTools.FluentMockServer.API.Models; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; +// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; +// using NinjaTools.FluentMockServer.Tests.TestHelpers; +// using Xunit; +// using Xunit.Abstractions; +// +// namespace NinjaTools.FluentMockServer.API.Tests.Proxy.Matchers +// { +// +// public class EvaluatorTestData : TheoryData +// { +// +// protected static HttpContext GetContext(Action setup) +// { +// var context = new DefaultHttpContext(); +// setup(context.Request); +// return context; +// } +// +// protected EvaluationResultBase Eval(int score, bool valid) +// { +// return new ResultProxy(valid, new EvaluationContext(new DefaultHttpContext())); +// } +// +// public class ResultProxy : EvaluationResultBase +// { +// /// +// public ResultProxy(bool math, EvaluationContext context) : base(context) +// { +// } +// +// /// +// public override bool IsMatch { get; } +// } +// +// +// public class ValidRequestMatcherTestData : EvaluatorTestData +// { +// public ValidRequestMatcherTestData() +// { +// Data1(); +// } +// +// private void Data1() +// { +// // *should* match anything +// var matcher = new RequestMatcher(); +// var context = GetContext(r => +// { +// r.Method = HttpMethods.Delete; +// r.Path = "/cars/buy/id/200"; +// r.ContentType = "application/json"; +// }); +// var eval = Eval(10, true); +// Add(matcher, context, eval); +// } +// } +// +// +// public class InvalidRequestMatcherTestData : EvaluatorTestData +// { +// public InvalidRequestMatcherTestData() +// { +// Data1(); +// Data2(); +// } +// +// private void Data1() +// { +// var reqA = new RequestMatcher +// { +// Path = "/some/path", +// Cookies = new Dictionary +// { +// {"abcd", "application/json"} +// }, +// Query = "?id=0", +// Method = "GET", +// Headers = new Dictionary +// { +// { "a", new [] {""}} +// }, +// BodyMatcher = new RequestBodyMatcher +// { +// Content = "?" +// } +// }; +// var evalA = Eval(0, false); +// var ctxA = GetContext(r => +// { +// r.Method = HttpMethods.Delete; +// +// }); +// Add(reqA, ctxA, evalA); +// } +// +// private void Data2() +// { +// var reqA = new RequestMatcher +// { +// Path = "/x&/y(", +// Query = "?id=123" +// }; +// var evalA = Eval(6, false); +// var ctxA = GetContext(r => { }); +// +// Add(reqA, ctxA, evalA); +// } +// } +// } +// +// +// +// public class EvaluatorTests: XUnitTestBase +// { +// /// +// public EvaluatorTests(ITestOutputHelper output) : base(output) +// { } +// +// [Theory] +// [ClassData(typeof(EvaluatorTestData.InvalidRequestMatcherTestData))] +// public void Should_Return_EvaluationUnsuccessfulResult_When_Not_Matching(RequestMatcher matcher, HttpContext context, EvaluationResultBase expectedResult) +// { +// // Act && Assert +// var visitor = new RequestEvaluatorVistor(new EvaluationContext(context)); +// var result = visitor.Evaluate(); +// Dump(matcher, "matcher"); +// result.Should().As().Should().Be(false); +// result.IsMatch.Should().BeFalse(); +// result.Score.Should().Be(expectedResult.Score); +// } +// +// [Theory] +// [ClassData(typeof(EvaluatorTestData.ValidRequestMatcherTestData))] +// public void Should_Return_EvaluationSuccessfulResult_When_Matching(RequestMatcher matcher, HttpContext context, EvaluationResultBase expectedResult) +// { +// // Act && Assert +// var visitor = new RequestEvaluatorVistor(new EvaluationContext(context)); +// var result = matcher.Accept(() => visitor); +// Dump(matcher, "matcher"); +// result.Should().As().Should().Be(false); +// result.IsMatch.Should().Be(true); +// result.Score.Should().Be(expectedResult.Score); +// } +// +// // +// // [Theory] +// // [MemberData(nameof(GetScoreTestData))] +// // public void Score_Should_Be_Higher_For_The_Matcher_Who_Is_Closer_To_The_HttpRequest(RequestMatcher more, HttpContext context, RequestMatcher less) +// // { +// // // Act +// // var a = EvaluationPipeline.Evaluate(context, more); +// // Dump(a, "more"); +// // +// // var b = EvaluationPipeline.Evaluate(context, less); +// // Dump(b, "less"); +// // +// // // Assert +// // a.Score.Should().BeGreaterOrEqualTo(b.Score); +// // } +// +// public static IEnumerable GetScoreTestData() +// { +// yield return Build(req => +// { +// req.Path = "/a/b"; +// req.Method = "POST"; +// }, +// less => less.Path = "z", +// ctx => +// { +// ctx.Request.Path = PathString.FromUriComponent("/a/b"); +// ctx.Request.Method = "POST"; +// }); +// +// yield return Build(req => +// { +// req.Path = "/a/b"; +// req.Method = "POST"; +// req.BodyMatcher = new RequestBodyMatcher +// { +// Content = "?", +// MatchExact = true +// }; +// }, +// less => +// { +// less.Path = "/some/path"; +// less.Cookies = new Dictionary +// { +// {"abcd", "application/json"} +// }; +// less.Query = "?id=0"; +// less.Method = "GET"; +// less.Headers = new Dictionary +// { +// {"a", new[] {""}} +// }; +// }, +// ctx => +// { +// ctx.Request.Cookies.Append(new KeyValuePair("abcd", "application/json")); +// ctx.Request.Path = PathString.FromUriComponent("/a/b"); +// ctx.Request.Method = "POST"; +// }); +// +// yield return Build(req => +// { +// req.BodyMatcher = new RequestBodyMatcher +// { +// Content = "hello world", +// MatchExact = true, +// Type = RequestBodyKind.Text +// }; +// }, +// less => less.Path = "/a/b/*", +// ctx => +// { +// ctx.Request.Path = PathString.FromUriComponent("/a/b"); +// ctx.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("hello world")); +// }); +// +// yield return Build(req => req.Path = "/a/b/*", +// less => less.Path = "/a/b", +// ctx => +// { +// ctx.Request.Path = PathString.FromUriComponent("/a/b"); +// ctx.Request.Method = "POST"; +// ctx.Request.Cookies.Append(new KeyValuePair("abcd", "application/json")); +// ctx.Request.Headers.Add("a", new[] {""}); +// }); +// } +// +// +// +// +// +// +// +// private static object[] Build(Action factory, Action lessAction, Action ctxFac) +// { +// var ctx = new DefaultHttpContext(); +// ctxFac(ctx); +// var more = new RequestMatcher(); +// var less = new RequestMatcher(); +// factory(more); +// factory(less); +// lessAction(less); +// return new object[] {more, ctx, less }; +// } +// } +// } From bf84c2751b93fbe9e30e556492cfd09670a6a321 Mon Sep 17 00:00:00 2001 From: alex held Date: Sun, 26 Jan 2020 01:57:01 +0100 Subject: [PATCH 36/64] refactor(server): move types into separate files --- .../Visitors/Collections/CookieCollection.cs | 16 +++ .../Visitors/Collections/HeaderCollection.cs | 47 +++++++ .../Visitors/Collections/HeadersCollection.cs | 123 ------------------ .../Visitors/Collections/HttpMethodWrapper.cs | 26 ++++ .../Visitors/Collections/PathCollection.cs | 23 ++++ .../Visitors/Collections/QueryCollection.cs | 24 ++++ 6 files changed, 136 insertions(+), 123 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/CookieCollection.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeaderCollection.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeadersCollection.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HttpMethodWrapper.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/PathCollection.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/QueryCollection.cs diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/CookieCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/CookieCollection.cs new file mode 100644 index 00000000..7296c1df --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/CookieCollection.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections +{ + public class CookieCollection : Dictionary, IVisitable + { + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeaderCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeaderCollection.cs new file mode 100644 index 00000000..e311fcb6 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeaderCollection.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Http; + +namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections +{ + public class HeaderCollection : HeaderDictionary, IVisitable + { + public HeaderCollection() : this(new Dictionary()) + { } + + public HeaderCollection(IDictionary dict) + { + dict ??= new Dictionary(); + foreach (var (key, value) in dict) + { + Add(key, value); + } + } + + public static implicit operator HeaderCollection(Dictionary headers) => From(headers); + public static implicit operator Dictionary(HeaderCollection headers) => + headers.ToDictionary(k => k.Key, + v => v.Value.ToArray()); + + public static HeaderCollection From(IDictionary headers) + { + var headerCollection = new HeaderCollection(); + headers ??= new Dictionary(); + foreach (var header in headers) + { + headerCollection.Add(header.Key, header.Value); + } + + return headerCollection; + } + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeadersCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeadersCollection.cs deleted file mode 100644 index 8ba523a4..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeadersCollection.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Http; - -namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections -{ - - public class HeaderCollection : HeaderDictionary, IVisitable - { - public HeaderCollection() : this(new Dictionary()) - { } - - public HeaderCollection(IDictionary dict) - { - dict ??= new Dictionary(); - foreach (var (key, value) in dict) - { - Add(key, value); - } - } - - public static implicit operator HeaderCollection(Dictionary headers) => From(headers); - public static implicit operator Dictionary(HeaderCollection headers) => - headers.ToDictionary(k => k.Key, - v => v.Value.ToArray()); - - public static HeaderCollection From(IDictionary headers) - { - var headerCollection = new HeaderCollection(); - headers ??= new Dictionary(); - foreach (var header in headers) - { - headerCollection.Add(header.Key, header.Value); - } - - return headerCollection; - } - - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } - - public class CookieCollection : Dictionary, IVisitable - { - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } - - public class PathCollection : IVisitable - { - public static implicit operator string(PathCollection? query) => query?.Path; - public static implicit operator PathCollection(string? str) => new PathCollection {PathString = $"/{str?.TrimStart('/')}"}; - - /// - public PathString PathString { get; set; } - public string Path => PathString.Value; - - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } - - public class QueryCollection : IVisitable - { - public static implicit operator string(QueryCollection query) => query.Query; - public static implicit operator QueryCollection(QueryString query) => new QueryCollection {QuryString = query}; - public static implicit operator QueryCollection(string str) => new QueryCollection {QuryString = new QueryString($"?{str.TrimStart('?')}")}; - - /// - public QueryString QuryString { get; set; } - public string Query => QuryString.Value; - - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } - - public class HttpMethodWrapper : IVisitable - { - public HttpMethodWrapper(string methodString) - { - MethodString = methodString; - } - - public HttpMethodWrapper() - { } - - public string MethodString { get; set; } - public static implicit operator string(HttpMethodWrapper wrapper) => wrapper.MethodString; - public static implicit operator HttpMethodWrapper(string str) => new HttpMethodWrapper {MethodString = str}; - - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HttpMethodWrapper.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HttpMethodWrapper.cs new file mode 100644 index 00000000..966e2b24 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HttpMethodWrapper.cs @@ -0,0 +1,26 @@ +namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections +{ + public class HttpMethodWrapper : IVisitable + { + public HttpMethodWrapper(string methodString) + { + MethodString = methodString; + } + + public HttpMethodWrapper() + { } + + public string MethodString { get; set; } + public static implicit operator string(HttpMethodWrapper wrapper) => wrapper.MethodString; + public static implicit operator HttpMethodWrapper(string str) => new HttpMethodWrapper {MethodString = str}; + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/PathCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/PathCollection.cs new file mode 100644 index 00000000..4db15725 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/PathCollection.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Http; + +namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections +{ + public class PathCollection : IVisitable + { + public static implicit operator string(PathCollection? query) => query?.Path; + public static implicit operator PathCollection(string? str) => new PathCollection {PathString = $"/{str?.TrimStart('/')}"}; + + /// + public PathString PathString { get; set; } + public string Path => PathString.Value; + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/QueryCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/QueryCollection.cs new file mode 100644 index 00000000..307ac15d --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/QueryCollection.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Http; + +namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections +{ + public class QueryCollection : IVisitable + { + public static implicit operator string(QueryCollection query) => query.Query; + public static implicit operator QueryCollection(QueryString query) => new QueryCollection {QuryString = query}; + public static implicit operator QueryCollection(string str) => new QueryCollection {QuryString = new QueryString($"?{str.TrimStart('?')}")}; + + /// + public QueryString QuryString { get; set; } + public string Query => QuryString.Value; + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } +} \ No newline at end of file From c3aa04ac1a050a32f03b6a6eed04b2d98a214ad3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2020 13:33:56 +0000 Subject: [PATCH 37/64] chore(deps): bump Newtonsoft.Json.Schema from 3.0.12-beta1 to 3.0.13 Bumps [Newtonsoft.Json.Schema](https://github.com/JamesNK/Newtonsoft.Json.Schema) from 3.0.12-beta1 to 3.0.13. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json.Schema/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json.Schema/commits/3.0.13) Signed-off-by: dependabot-preview[bot] --- .../NinjaTools.FluentMockServer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj b/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj index f9922d2b..67381dee 100644 --- a/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj @@ -22,7 +22,7 @@ - + From 75c47de621e4c6f065e9bca64ed2a8883501ed36 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2020 05:01:14 +0000 Subject: [PATCH 38/64] chore(deps): bump System.IO.Abstractions.TestingHelpers Bumps [System.IO.Abstractions.TestingHelpers](https://github.com/System-IO-Abstractions/System.IO.Abstractions) from 8.0.3 to 8.0.4. - [Release notes](https://github.com/System-IO-Abstractions/System.IO.Abstractions/releases) - [Commits](https://github.com/System-IO-Abstractions/System.IO.Abstractions/compare/v8.0.3...v8.0.4) Signed-off-by: dependabot-preview[bot] --- .../NinjaTools.FluentMockServer.API.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj index 559f950d..b45b8a0a 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj @@ -13,7 +13,7 @@ - + From 1a241688a3b9ae011d87069b0562214de9820165 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2020 05:05:32 +0000 Subject: [PATCH 39/64] chore(deps): bump System.IO.Abstractions from 8.0.3 to 8.0.4 Bumps [System.IO.Abstractions](https://github.com/System-IO-Abstractions/System.IO.Abstractions) from 8.0.3 to 8.0.4. - [Release notes](https://github.com/System-IO-Abstractions/System.IO.Abstractions/releases) - [Commits](https://github.com/System-IO-Abstractions/System.IO.Abstractions/compare/v8.0.3...v8.0.4) Signed-off-by: dependabot-preview[bot] --- .../NinjaTools.FluentMockServer.API.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 95caddbe..17418780 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -23,7 +23,7 @@ - + From 67ce6ebd881ae5766794900575e176a04bebd8dc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 07:52:59 +0000 Subject: [PATCH 40/64] chore(deps): bump System.IO.Abstractions from 8.0.4 to 8.0.5 Bumps [System.IO.Abstractions](https://github.com/System-IO-Abstractions/System.IO.Abstractions) from 8.0.4 to 8.0.5. - [Release notes](https://github.com/System-IO-Abstractions/System.IO.Abstractions/releases) - [Commits](https://github.com/System-IO-Abstractions/System.IO.Abstractions/compare/v8.0.4...v8.0.5) Signed-off-by: dependabot-preview[bot] --- .../NinjaTools.FluentMockServer.API.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 17418780..031a02b6 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -23,7 +23,7 @@ - + From 5ee82e76a6cfa8519ba664d26544a22274b7dc4e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2020 07:53:05 +0000 Subject: [PATCH 41/64] chore(deps): bump System.IO.Abstractions.TestingHelpers Bumps [System.IO.Abstractions.TestingHelpers](https://github.com/System-IO-Abstractions/System.IO.Abstractions) from 8.0.4 to 8.0.5. - [Release notes](https://github.com/System-IO-Abstractions/System.IO.Abstractions/releases) - [Commits](https://github.com/System-IO-Abstractions/System.IO.Abstractions/compare/v8.0.4...v8.0.5) Signed-off-by: dependabot-preview[bot] --- .../NinjaTools.FluentMockServer.API.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj index b45b8a0a..e83ccc17 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj @@ -13,7 +13,7 @@ - + From 02e6271e93cfde70c318a6e2d793dfc1535a4eaf Mon Sep 17 00:00:00 2001 From: alex held Date: Sun, 26 Jan 2020 13:57:12 +0100 Subject: [PATCH 42/64] =?UTF-8?q?refactor(server):=20major=20overhaul=20of?= =?UTF-8?q?=20internal=20type-system=20=F0=9F=9A=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.idea/contentModel.xml | 23 +- .../riderModule.iml | 2 +- .../Configuration/ConfigFileProvider.cs | 26 ++- .../Configuration/ConfigurationInitializer.cs | 1 - .../DependencyInjection/MockServerBuilder.cs | 1 - .../ServiceCollectionExtensions.cs | 1 - .../Extensions/LINQ.cs | 60 +++++ .../Extensions/StringExtensions.cs | 1 - .../Infrastructure/SetupRepository.cs | 1 - .../Logging/LogFactory.cs | 25 +- .../Logging/LogRepository.cs | 6 +- .../Logging/LoggingInitializer.cs | 15 -- .../Logging/MockServerPaths.cs | 17 ++ .../Logging/Models/ILogItem.cs | 1 + .../Logging/Models/LogItem.cs | 10 +- .../Logging/Models/LogKind.cs | 2 + .../Logging/Models/RequestMatchedLog.cs | 9 +- .../Logging/Models/RequestUnmatchedLog.cs | 6 +- .../Logging/Models/SetupLog.cs | 2 + .../Models/Cookies.cs | 143 ++++++++++++ .../Models/Headers.cs | 131 +++++++++++ .../Models/Method.cs | 42 ++++ .../Models/Path.cs | 46 ++++ .../Models/Query.cs | 63 +++++ .../Models/RequestBodyKind.cs | 12 + .../Models/RequestBodyMatcher.cs | 59 ++--- .../Models/RequestMatcher.cs | 48 ++-- .../Models/ResponseAction.cs | 11 +- .../Models/Setup.cs | 5 - .../Models/ViewModels/HttpRequestViewModel.cs | 4 +- .../Models/ViewModels/MatchedRequest.cs | 12 +- .../NinjaTools.FluentMockServer.API.csproj | 43 +--- .../Proxy/Evaluation/EvaluationContext.cs | 24 -- .../Proxy/Evaluation/EvaluationPipeline.cs | 79 ------- .../Evaluation/Evaluators/EvaluatorBase.cs | 34 --- .../Evaluators/HttpBodyEvaluator.cs | 42 ---- .../Evaluators/HttpCookieEvaluator.cs | 25 -- .../Evaluators/HttpHeaderEvaluator.cs | 28 --- .../Evaluators/HttpMethodEvaluator.cs | 22 -- .../Evaluators/HttpPathEvaluator.cs | 23 -- .../Evaluators/QueryStringEvaluator.cs | 22 -- .../Proxy/Evaluation/HttpRequestEvaluator.cs | 22 -- .../Proxy/Evaluation/IEvaluator.cs | 11 - .../Evaluation/Models/EvaluationMessages.cs | 21 -- .../Evaluation/Models/EvaluationRatings.cs | 14 -- .../Evaluation/Models/EvaluationResultBase.cs | 30 --- .../Models/EvaluationSuccessfulResult.cs | 17 -- .../Models/EvaluationUnsuccessfulResult.cs | 13 -- .../Evaluation/Models/EvaluationWeight.cs | 11 - .../Evaluation/Models/IEvaluationResult.cs | 9 - .../Evaluation/Models/NormalizedMatcher.cs | 23 -- .../Proxy/Evaluation/Models/Rating.cs | 23 -- .../Proxy/Evaluation/Rules/IRule.cs | 7 - .../Evaluation/Visitors/IPartialVisitor.cs | 16 -- .../Proxy/Evaluation/Visitors/IRequest.cs | 10 - .../Proxy/Evaluation/Visitors/IRequestBody.cs | 9 - .../IRequestMatcherEvaluatorVistor.cs | 9 - .../Proxy/Evaluation/Visitors/IVisitor.cs | 11 - .../Visitors/RequestEvaluatorVistor.cs | 161 ------------- .../Visitors/Collections/CookieCollection.cs | 16 -- .../Visitors/Collections/HeaderCollection.cs | 47 ---- .../Visitors/Collections/HttpMethodWrapper.cs | 26 --- .../Visitors/Collections/PathCollection.cs | 23 -- .../Visitors/Collections/QueryCollection.cs | 24 -- .../Proxy/Visitors/ComparasionVisitor.cs | 221 +++++++----------- .../Proxy/Visitors/IVisitable.cs | 7 + .../Proxy/Visitors/IVisitor.cs | 40 +--- .../Converters/HeadersConverter.cs | 41 ++++ .../Converters/MethodConverter.cs | 51 ++++ .../Serialization/Converters/PathConverter.cs | 49 ++++ .../Types/IDontRenderWhenEmpty.cs | 11 + .../NinjaTools.FluentMockServer.csproj | 3 +- .../Serialization/ExpectationConverter.cs | 9 - .../Configuration/ConfigFileProviderTests.cs | 20 +- .../Extensions/EnumExtensions.cs | 2 - .../Infrastructure/SetupRepositoryTests.cs | 2 +- .../Models/Logging/LogItemTests.cs | 13 +- ...njaTools.FluentMockServer.API.Tests.csproj | 3 +- .../Proxy/ComparasionVisitorTests.cs | 67 +++--- .../Serilization/HeadersTests.cs | 76 ++++++ .../Services/LogServiceTests.cs | 9 +- .../Services/SetupServiceTests.cs | 2 +- .../Types/InitializerTests.cs | 1 - .../NinjaTools.FluentMockServer.Tests.csproj | 2 +- .../TestHelpers/XUnitTestBase.cs | 9 +- 85 files changed, 1055 insertions(+), 1263 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer.API/Extensions/LINQ.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Logging/MockServerPaths.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Cookies.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Headers.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Method.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Path.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/Query.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Models/RequestBodyKind.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/HttpRequestEvaluator.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/IEvaluator.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationMessages.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationRatings.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationUnsuccessfulResult.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationWeight.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/NormalizedMatcher.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Rules/IRule.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequest.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestBody.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestMatcherEvaluatorVistor.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/CookieCollection.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeaderCollection.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HttpMethodWrapper.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/PathCollection.cs delete mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/QueryCollection.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitable.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Serialization/Converters/HeadersConverter.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Serialization/Converters/MethodConverter.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Serialization/Converters/PathConverter.cs create mode 100644 src/NinjaTools.FluentMockServer.API/Types/IDontRenderWhenEmpty.cs create mode 100644 test/NinjaTools.FluentMockServer.API.Tests/Serilization/HeadersTests.cs diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index 6589af08..52c276c2 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -151,6 +151,7 @@ + @@ -164,6 +165,7 @@ + @@ -174,6 +176,12 @@ + + + + + + @@ -198,13 +206,18 @@ - - - + + + + + + + + @@ -213,6 +226,7 @@ + @@ -256,6 +270,9 @@ + + + diff --git a/.idea/.idea.NinjaTools.FluentMockServer/riderModule.iml b/.idea/.idea.NinjaTools.FluentMockServer/riderModule.iml index 0dafa754..70639b1e 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/riderModule.iml +++ b/.idea/.idea.NinjaTools.FluentMockServer/riderModule.iml @@ -1,7 +1,7 @@ - + diff --git a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFileProvider.cs b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFileProvider.cs index 2ee3cd6f..cca324cd 100644 --- a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFileProvider.cs +++ b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFileProvider.cs @@ -3,9 +3,10 @@ using System.IO; using System.IO.Abstractions; using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json; using NinjaTools.FluentMockServer.API.Logging; using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Serialization.Converters; using YamlDotNet.Serialization; namespace NinjaTools.FluentMockServer.API.Configuration @@ -16,7 +17,7 @@ public interface IConfigFileProvider IEnumerable GetConfigFiles(); } - public class ConfigFileProvider : IConfigFileProvider + public class ConfigFileProvider : IConfigFileProvider { private readonly IFileSystem _fs; private readonly ILogger _logger; @@ -28,8 +29,7 @@ public ConfigFileProvider(IFileSystem fs, ILogger logger) } public ConfigFileProvider(ILogger logger) : this(new FileSystem(), logger) - { - } + { } public IEnumerable GetConfigFiles() @@ -55,18 +55,24 @@ public IEnumerable GetConfigFiles() } } - private ConfigFile ParseJson(string path) + private ConfigFile ParseJson(string path) { - var jo = JArray.Parse(_fs.File.ReadAllText(path)); - var setups = jo.ToObject>(); - + var text = _fs.File.ReadAllText(path); + var setups = JsonConvert.DeserializeObject>(text); return new ConfigFile(path, setups.ToArray()); } - private IConfigFile ParseYaml(string path) + private IConfigFile ParseYaml(string path) { using var reader = new StringReader(_fs.File.ReadAllText(path)); - var deserializer = new DeserializerBuilder().Build(); + + var deserializer = new DeserializerBuilder() + .IgnoreFields() + .IgnoreUnmatchedProperties() + .WithTypeConverter(new MethodConverter()) + .WithTypeConverter(new HeadersConverter()) + .WithTypeConverter(new PathConverter()) + .Build(); var setups = deserializer.Deserialize>(reader); return new ConfigFile(path, setups.ToArray()); diff --git a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationInitializer.cs b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationInitializer.cs index e0324b80..3044c570 100644 --- a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationInitializer.cs +++ b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationInitializer.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using NinjaTools.FluentMockServer.API.Services; using NinjaTools.FluentMockServer.API.Types; namespace NinjaTools.FluentMockServer.API.Configuration diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs index 6654c20a..e46ac3ba 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs @@ -39,7 +39,6 @@ public MockServerBuilder([NotNull] IServiceCollection services, [NotNull] IConfi opt.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; }); - Services.AddMiddlewareAnalysis(); Services.AddLogging(); Services.AddInitializers(); } diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs index 43c0db69..69452ec3 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ServiceCollectionExtensions.cs @@ -10,7 +10,6 @@ using Microsoft.OpenApi.Models; using NinjaTools.FluentMockServer.API.Configuration; using NinjaTools.FluentMockServer.API.Logging; -using NinjaTools.FluentMockServer.API.Services; using NinjaTools.FluentMockServer.API.Types; namespace NinjaTools.FluentMockServer.API.DependencyInjection diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/LINQ.cs b/src/NinjaTools.FluentMockServer.API/Extensions/LINQ.cs new file mode 100644 index 00000000..04be35f8 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Extensions/LINQ.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; +using NinjaTools.FluentMockServer.API.Models; + +namespace NinjaTools.FluentMockServer.API.Extensions +{ + public static class LINQ + { + + + public static Dictionary ToDictionary(this T headers) where T : IHeaderDictionary + { + return headers.ToDictionary( + k => k.Key, + v => v.Value.ToArray()); + } + + [NotNull] + public static IReadOnlyList EnsureNotNullNotEmpty([CanBeNull] this IEnumerable? input) + { + var collection = (input ?? throw new InvalidOperationException(nameof(input))).ToList(); + + if (!collection.Any()) + throw new InvalidOperationException("EnsureNotNullNotEmpty; EMPTY SEQUENCE"); + + if (collection.Any(item => item is null)) + throw new InvalidOperationException("EnsureNotNullNotEmpty; NULL ITEM IN SEQUENCE"); + + return collection; + } + + public static Headers ToHeaderCollection(this T headers) where T : IHeaderDictionary + { + return new Headers(headers.ToDictionary( + k => k.Key, + v => v.Value.ToArray())); + } + + public static HeaderDictionary ToHeaderCollection(this IDictionary headers) + { + return new HeaderDictionary(headers.ToDictionary(k => k.Key, + v => new StringValues(v.Value))); + } + + public static HeaderDictionary ToHeaderCollection(this IDictionary headers) + { + return new HeaderDictionary(headers.ToDictionary(k => k.Key, v => v.Value)); + } + + + public static Dictionary ToDictionary(this IDictionary headers) + { + return headers.ToDictionary(k => k.Key, v => v.Value.ToArray()); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs index 842e9125..8a4036ee 100644 --- a/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs @@ -25,7 +25,6 @@ public static bool IsValidRegex(this string pattern) public static string GetDescription(this T e) where T : Enum, IConvertible { - var type = typeof(T); var values = Enum.GetValues(type); diff --git a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs index 47036ad1..096fd923 100644 --- a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs +++ b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using JetBrains.Annotations; using Microsoft.AspNetCore.Http; using NinjaTools.FluentMockServer.API.Models; diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs b/src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs index 5169aa7f..97e9c47a 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LogFactory.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Http; using NinjaTools.FluentMockServer.API.Logging.Models; using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.API.Models.ViewModels; namespace NinjaTools.FluentMockServer.API.Logging { @@ -9,7 +10,6 @@ namespace NinjaTools.FluentMockServer.API.Logging public interface ILogFactory { - string GenerateId(); SetupLog SetupCreated(Setup setup); SetupLog SetupDeleted(Setup setup); @@ -31,9 +31,24 @@ public LogFactory(GenerateId idGenerator) _idGenerator = idGenerator; } - public SetupLog SetupCreated(Setup setup) => new SetupLog(_idGenerator(), setup, LogKind.SetupCreated); - public SetupLog SetupDeleted(Setup setup) => new SetupLog(_idGenerator(), setup, LogKind.SetupDeleted); - public RequestMatchedLog RequestMached(HttpContext context, Setup setup) => new RequestMatchedLog(_idGenerator(), (context, setup)); - public RequestUnmatchedLog RequestUnmatched(HttpContext context) => new RequestUnmatchedLog(_idGenerator(), context); + public SetupLog SetupCreated(Setup setup) + { + return new SetupLog(_idGenerator(), setup, LogKind.SetupCreated); + } + + public SetupLog SetupDeleted(Setup setup) + { + return new SetupLog(_idGenerator(), setup, LogKind.SetupDeleted); + } + + public RequestMatchedLog RequestMached(HttpContext context, Setup setup) + { + return new RequestMatchedLog(_idGenerator(), new MatchedRequest(context, setup)); + } + + public RequestUnmatchedLog RequestUnmatched(HttpContext context) + { + return new RequestUnmatchedLog(_idGenerator(), context); + } } } diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs b/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs index 4b7f0eec..4282f3f9 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LogRepository.cs @@ -16,15 +16,15 @@ public class LogRepository : ILogRepository private static readonly string RequestLogDirectory = MockServerPaths.Logs.Requests; private static readonly string SetupLogDirectory = MockServerPaths.Logs.Setups; + public LogRepository() : this(new FileSystem()) + { } + public LogRepository(IFileSystem fileSystem) { _fileSystem = fileSystem; _logItems = new List(); } - public LogRepository() : this(new FileSystem()) - { } - /// public IEnumerable Get() { diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs b/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs index 3b30cef8..8ed3d81a 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs @@ -1,24 +1,9 @@ -using System.IO; using System.IO.Abstractions; using System.Threading.Tasks; using NinjaTools.FluentMockServer.API.Types; namespace NinjaTools.FluentMockServer.API.Logging { - public static class MockServerPaths - { - private const string LogRoot = "/var/log/mock-server"; - - public static class Logs - { - public static readonly string Setups = Path.Combine(LogRoot, "setups"); - public static readonly string Requests = Path.Combine(LogRoot, "requests"); - } - - public static string Configs { get; } = "/etc/mock-server/config/"; - } - - /// public class LoggingInitializer : IInitializer { diff --git a/src/NinjaTools.FluentMockServer.API/Logging/MockServerPaths.cs b/src/NinjaTools.FluentMockServer.API/Logging/MockServerPaths.cs new file mode 100644 index 00000000..c74e3582 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Logging/MockServerPaths.cs @@ -0,0 +1,17 @@ +using System.IO; + +namespace NinjaTools.FluentMockServer.API.Logging +{ + public static class MockServerPaths + { + private const string LogRoot = "/var/log/mock-server"; + + public static class Logs + { + public static readonly string Setups = Path.Combine(LogRoot, "setups"); + public static readonly string Requests = Path.Combine(LogRoot, "requests"); + } + + public static string Configs { get; } = "/etc/mock-server/config/"; + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs index 52a2efe3..437bc8b8 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/ILogItem.cs @@ -2,6 +2,7 @@ namespace NinjaTools.FluentMockServer.API.Logging.Models { + /// public interface ILogItem : IIdentifable { public string ToFormattedString(); diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs index 403057a6..05d3b39a 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Text; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using NinjaTools.FluentMockServer.API.Extensions; namespace NinjaTools.FluentMockServer.API.Logging.Models @@ -35,11 +34,16 @@ protected virtual string FormatContent() protected string Serialize(object obj) { + if (obj is null) + { + return string.Empty; + } + return JsonConvert.SerializeObject(obj, new JsonSerializerSettings { Formatting = Formatting.Indented, - Converters = {new StringEnumConverter()}, - NullValueHandling = NullValueHandling.Ignore + NullValueHandling = NullValueHandling.Ignore, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); } diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs index 6aaccf45..2beb4b22 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogKind.cs @@ -4,6 +4,7 @@ namespace NinjaTools.FluentMockServer.API.Logging.Models { + /// [JsonConverter(typeof(StringEnumConverter))] public enum LogType { @@ -12,6 +13,7 @@ public enum LogType } + /// [JsonConverter(typeof(StringEnumConverter))] public enum LogKind { diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs index da1312c9..2a37ef28 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestMatchedLog.cs @@ -1,5 +1,3 @@ -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Models; using NinjaTools.FluentMockServer.API.Models.ViewModels; namespace NinjaTools.FluentMockServer.API.Logging.Models @@ -10,15 +8,14 @@ public class RequestMatchedLog : LogItem public override LogKind Kind => LogKind.RequestMatched; /// - public RequestMatchedLog(string id, (HttpContext, Setup) content) : base(id, content) - { - } + public RequestMatchedLog(string id, MatchedRequest content) : base(id, content) + { } /// protected override string FormatHeader() { var request = Content.Context.Request; - var requestLiteral = $"{request.Method} {request.Path}"; + var requestLiteral = $"{request.Method} {request.Path.Value}"; return string.Format(base.FormatHeader(), requestLiteral); } } diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs index f3b580d8..2c296ddb 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/RequestUnmatchedLog.cs @@ -1,8 +1,10 @@ +using JetBrains.Annotations; using Microsoft.AspNetCore.Http; using NinjaTools.FluentMockServer.API.Models.ViewModels; namespace NinjaTools.FluentMockServer.API.Logging.Models { + /// public class RequestUnmatchedLog : LogItem { /// @@ -10,10 +12,10 @@ public class RequestUnmatchedLog : LogItem /// public RequestUnmatchedLog(string id, HttpContext content) : base(id,new HttpRequestViewModel(content.Request)) - { - } + { } /// + [NotNull] protected override string FormatHeader() { var request = Content; diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs index 815a706d..979181fc 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/SetupLog.cs @@ -2,6 +2,7 @@ namespace NinjaTools.FluentMockServer.API.Logging.Models { + /// public sealed class SetupLog : LogItem { /// @@ -10,6 +11,7 @@ public SetupLog(string id, Setup content, LogKind kind) : base(id, content) Kind = kind; } + /// public override LogKind Kind { get; } } } diff --git a/src/NinjaTools.FluentMockServer.API/Models/Cookies.cs b/src/NinjaTools.FluentMockServer.API/Models/Cookies.cs new file mode 100644 index 00000000..4ec97a6e --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Cookies.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NinjaTools.FluentMockServer.API.Proxy.Visitors; + +namespace NinjaTools.FluentMockServer.API.Models +{ + + [JsonDictionary] + public class Cookies : IVisitable, IDictionary + { + public IDictionary Cookie { get; } = new Dictionary(); + + public Cookies(params (string key, string value)[] args) + { + foreach (var (k, v) in (args ??= new (string key, string value)[0])) + Cookie.Add(k, v); + } + + public Cookies(IDictionary dict) : this(dict?.Select(kvp => (kvp.Key, kvp.Value))?.ToArray()) + { } + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + + + /// + public IEnumerator> GetEnumerator() + { + return Cookie.GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable) Cookie).GetEnumerator(); + } + + /// + public void Add(KeyValuePair item) + { + Cookie.Add(item); + } + + /// + public void Clear() + { + Cookie.Clear(); + } + + /// + public bool Contains(KeyValuePair item) + { + return Cookie.Contains(item); + } + + /// + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + Cookie.CopyTo(array, arrayIndex); + } + + /// + public bool Remove(KeyValuePair item) + { + return Cookie.Remove(item); + } + + /// + public int Count => Cookie.Count; + + /// + public bool IsReadOnly => Cookie.IsReadOnly; + + /// + public void Add(string key, string value) + { + Cookie.Add(key, value); + } + + /// + public bool ContainsKey(string key) + { + return Cookie.ContainsKey(key); + } + + /// + public bool Remove(string key) + { + return Cookie.Remove(key); + } + + /// + public bool TryGetValue(string key, out string value) + { + return Cookie.TryGetValue(key, out value); + } + + /// + public string this[string key] + { + get => Cookie[key]; + set => Cookie[key] = value; + } + + /// + public ICollection Keys => Cookie.Keys; + + /// + public ICollection Values => Cookie.Values; + } + + /// + public class CookieCollectionConverter : JsonConverter + { + /// + public override void WriteJson(JsonWriter writer, Cookies value, JsonSerializer serializer) + { + var t = JToken.FromObject(value); + if (t.Type == JTokenType.Object) + { + t.WriteTo(writer); + } + } + + /// + public override Cookies ReadJson(JsonReader reader, Type objectType, Cookies existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var jo = JToken.ReadFrom(reader); + var dict = JsonConvert.DeserializeObject>(jo.ToString()); + return new Cookies(dict); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/Headers.cs b/src/NinjaTools.FluentMockServer.API/Models/Headers.cs new file mode 100644 index 00000000..cd8b8b53 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Headers.cs @@ -0,0 +1,131 @@ +using System.Collections; +using System.Collections.Generic; +using Newtonsoft.Json; +using NinjaTools.FluentMockServer.API.Proxy.Visitors; + +namespace NinjaTools.FluentMockServer.API.Models +{ + + [JsonDictionary] + public class Headers : IVisitable, IDictionary + { + public Headers() + { + Header = new Dictionary(); + } + + public IDictionary Header { get; } + + /// + public Headers(IDictionary dictionary) : this() + { + dictionary ??= new Dictionary(); + foreach (var (k, v) in dictionary) + Header.Add(k, v); + } + + /// + public Headers(params (string, string[])[] kvps) : this() + { + kvps ??= new (string, string[])[0]; + foreach (var (k, v) in kvps) + Header.Add(k, v); + } + + + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + + /// + public IEnumerator> GetEnumerator() + { + return Header.GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable) Header).GetEnumerator(); + } + + /// + public void Add(KeyValuePair item) + { + Header.Add(item); + } + + /// + public void Clear() + { + Header.Clear(); + } + + /// + public bool Contains(KeyValuePair item) + { + return Header.Contains(item); + } + + /// + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + Header.CopyTo(array, arrayIndex); + } + + /// + public bool Remove(KeyValuePair item) + { + return Header.Remove(item); + } + + /// + public int Count => Header.Count; + + /// + public bool IsReadOnly => Header.IsReadOnly; + + /// + public void Add(string key, string[] value) + { + Header.Add(key, value); + } + + /// + public bool ContainsKey(string key) + { + return Header.ContainsKey(key); + } + + /// + public bool Remove(string key) + { + return Header.Remove(key); + } + + /// + public bool TryGetValue(string key, out string[] value) + { + return Header.TryGetValue(key, out value); + } + + /// + public string[] this[string key] + { + get => Header[key]; + set => Header[key] = value; + } + + /// + public ICollection Keys => Header.Keys; + + /// + public ICollection Values => Header.Values; + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/Method.cs b/src/NinjaTools.FluentMockServer.API/Models/Method.cs new file mode 100644 index 00000000..0d682785 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Method.cs @@ -0,0 +1,42 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; +using NinjaTools.FluentMockServer.API.Proxy.Visitors; +using NinjaTools.FluentMockServer.API.Serialization.Converters; +using NinjaTools.FluentMockServer.API.Types; +using YamlDotNet.Serialization; + +namespace NinjaTools.FluentMockServer.API.Models +{ + [JsonConverter(typeof(MethodConverter))] + public class Method : IVisitable, IDontRenderWhenEmpty + { + public static implicit operator string(Method wrapper) => wrapper.MethodString; + + /// + public bool IsEnabled() => !string.IsNullOrWhiteSpace(MethodString); + + [UsedImplicitly] + public Method() + { } + + public Method(string methodString) + { + MethodString = methodString; + } + + + + + [YamlMember(SerializeAs = typeof(string), Alias = "Method")] + public string MethodString { get; set; } + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/Path.cs b/src/NinjaTools.FluentMockServer.API/Models/Path.cs new file mode 100644 index 00000000..5c0cbe5a --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Path.cs @@ -0,0 +1,46 @@ +using System; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using NinjaTools.FluentMockServer.API.Proxy.Visitors; +using NinjaTools.FluentMockServer.API.Serialization.Converters; +using NinjaTools.FluentMockServer.API.Types; + +namespace NinjaTools.FluentMockServer.API.Models +{ + /// + [JsonConverter(typeof(PathConverter))] + public class Path : IVisitable, IEquatable, IDontRenderWhenEmpty + { + public static implicit operator string(Path path) => path.PathString.Value; + + /// + public Path([CanBeNull] string path) : this(new PathString($"/{path?.TrimStart('/')}")) + { } + + public Path(PathString path) + { + PathString = path; + } + + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + + /// + public PathString PathString { get; set; } + + public string ToPath() => PathString.Value; + + /// + public bool Equals(PathString other) => PathString.Equals(other); + /// + public bool IsEnabled() => PathString.HasValue; + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/Query.cs b/src/NinjaTools.FluentMockServer.API/Models/Query.cs new file mode 100644 index 00000000..e082e3b0 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/Query.cs @@ -0,0 +1,63 @@ +using System; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NinjaTools.FluentMockServer.API.Proxy.Visitors; +using NinjaTools.FluentMockServer.API.Types; + +namespace NinjaTools.FluentMockServer.API.Models +{ + public class QueryConverter : JsonConverter + { + /// + public override void WriteJson(JsonWriter writer, Query value, JsonSerializer serializer) + { + var t = JToken.FromObject(value); + if (t.Type != JTokenType.Object) + { + t.WriteTo(writer); + } + } + + /// + public override Query ReadJson(JsonReader reader, Type objectType, Query existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var value = serializer.Deserialize(reader); + return new Query(value); + } + } + + /// + [JsonConverter(typeof(QueryConverter))] + public class Query : IVisitable, IDontRenderWhenEmpty + { + public QueryString QueryString { get; } + + public Query() : this(QueryString.Empty) + { + } + + /// + public Query(string value) : this(new QueryString($"?{value.TrimStart('?')}")) + { } + + public Query(QueryString inner) + { + QueryString = inner; + } + + /// + public void Accept(IVisitor visitor) + { + if (visitor is IVisitor typed) + { + typed.Visit(this); + } + } + + /// + public bool IsEnabled() => QueryString.HasValue; + + public string ToQuery() => QueryString.Value; + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestBodyKind.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyKind.cs new file mode 100644 index 00000000..5d062ad0 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyKind.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace NinjaTools.FluentMockServer.API.Models +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum RequestBodyKind + { + Text, + Base64 + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs index fa39a22e..ee884470 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestBodyMatcher.cs @@ -1,45 +1,14 @@ -// using System; -// using System.Collections.Generic; -// using System.Diagnostics; -// using System.IO; -// using JetBrains.Annotations; -// using Microsoft.AspNetCore.Http; -// using Newtonsoft.Json; -// using Newtonsoft.Json.Converters; -// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; -// -// namespace NinjaTools.FluentMockServer.API.Models -// { -// public class RequestBodyMatcher -// { -// public string? Content { get; set; } -// public RequestMatcher.RequestBodyKind Type { get; set; } -// public bool MatchExact { get; set; } -// } -// -// public class RequestMatcher -// { -// private string? _path; -// public string? Path -// { -// get => _path; -// set => _path = value is null -// ? null -// : $"/{value.TrimStart('/')}"; -// } -// -// public RequestBodyMatcher? BodyMatcher { get; set; } -// public string Method { get; set; } -// public Dictionary? Headers { get; set; } -// public Dictionary? Cookies { get; set; } -// public string? Query { get; set; } -// -// -// [JsonConverter(typeof(StringEnumConverter))] -// public enum RequestBodyKind -// { -// Text, -// Base64 -// } -// } -// } +using NinjaTools.FluentMockServer.API.Types; + +namespace NinjaTools.FluentMockServer.API.Models +{ + public class RequestBodyMatcher : IDontRenderWhenEmpty + { + public string? Content { get; set; } + public RequestBodyKind? Kind { get; set; } + public bool? MatchExact { get; set; } + + /// + public bool IsEnabled() => !string.IsNullOrWhiteSpace(Content) || Kind.HasValue || MatchExact.HasValue; + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs index 1dd98ab8..d9c80e63 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/RequestMatcher.cs @@ -1,15 +1,33 @@ -// using System; -// using System.Collections.Generic; -// using System.Diagnostics; -// using System.Linq; -// using System.Text; -// using Microsoft.AspNetCore.Http; -// using Newtonsoft.Json; -// using Newtonsoft.Json.Converters; -// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors; -// -// namespace NinjaTools.FluentMockServer.API.Models -// { -// -// -// } +using System.Collections.Generic; +using Newtonsoft.Json; +using YamlDotNet.Core; +using YamlDotNet.Serialization; + +namespace NinjaTools.FluentMockServer.API.Models +{ + [JsonObject(MemberSerialization.OptOut, ItemNullValueHandling = NullValueHandling.Ignore)] + public class RequestMatcher + { + + [YamlMember(SerializeAs = typeof(Dictionary))] + public RequestBodyMatcher BodyMatcher { get; set; } + + [YamlMember(SerializeAs = typeof(string), ScalarStyle = ScalarStyle.Any)] + public Path Path { get; set; } + + [YamlMember(SerializeAs = typeof(string), ScalarStyle = ScalarStyle.Any)] + public Method Method { get; set; } + + [YamlMember(SerializeAs = typeof(Dictionary))] + public Headers Headers { get; set; } + + [YamlMember(SerializeAs = typeof(Dictionary))] + public Cookies Cookies { get; set; } + + [YamlMember(SerializeAs = typeof(Dictionary))] + public Query Query { get; set; } + } + + + +} diff --git a/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs b/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs index 21709ebe..86115de1 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/ResponseAction.cs @@ -1,21 +1,22 @@ using System.Diagnostics; +using JetBrains.Annotations; namespace NinjaTools.FluentMockServer.API.Models { + /// + /// Defines how the MockServer responds zu a matched requests. + /// [DebuggerDisplay("{DebuggerDisplay()}")] public class ResponseAction { - public string DebuggerDisplay() - { - return $"{Response?.DebuggerDisplay()}"; - } - + private string DebuggerDisplay() => $"{Response?.DebuggerDisplay()}"; public HttpResponse Response { get; set; } } [DebuggerDisplay("{DebuggerDisplay()}")] public class HttpResponse { + [NotNull] public string DebuggerDisplay() => $"Status={StatusCode}; Body={Body}"; public int StatusCode { get; set; } public string Body { get; set; } diff --git a/src/NinjaTools.FluentMockServer.API/Models/Setup.cs b/src/NinjaTools.FluentMockServer.API/Models/Setup.cs index 6d6f5d1e..a1c750c1 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/Setup.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/Setup.cs @@ -1,14 +1,9 @@ -using System.Diagnostics; -using JetBrains.Annotations; - namespace NinjaTools.FluentMockServer.API.Models { public class Setup { - [CanBeNull] public RequestMatcher? Matcher { get; set; } - [CanBeNull] public ResponseAction? Action { get; set; } } } diff --git a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs index 139e8380..c651d318 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/HttpRequestViewModel.cs @@ -7,10 +7,10 @@ namespace NinjaTools.FluentMockServer.API.Models.ViewModels { - [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore, ItemRequired = Required.AllowNull)] public class HttpRequestViewModel { - [JsonIgnore] public HttpRequest Request { get; } + [JsonIgnore] + public HttpRequest Request { get; } public HttpRequestViewModel(HttpRequest request) { diff --git a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/MatchedRequest.cs b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/MatchedRequest.cs index 8aedce53..9c5c3237 100644 --- a/src/NinjaTools.FluentMockServer.API/Models/ViewModels/MatchedRequest.cs +++ b/src/NinjaTools.FluentMockServer.API/Models/ViewModels/MatchedRequest.cs @@ -5,11 +5,14 @@ namespace NinjaTools.FluentMockServer.API.Models.ViewModels { public class MatchedRequest { - [JsonIgnore] public HttpContext Context { get; } + [JsonIgnore] + public HttpContext Context { get; } - [JsonProperty(Order = 0)] public HttpRequestViewModel HttpRequest { get; } + [JsonProperty(Order = 0)] + public HttpRequestViewModel HttpRequest { get; } - [JsonProperty(Order = 1)] public Setup Setup { get; } + [JsonProperty(Order = 1)] + public Setup Setup { get; } public MatchedRequest(HttpContext context, Setup setup) { @@ -17,8 +20,5 @@ public MatchedRequest(HttpContext context, Setup setup) Setup = setup; HttpRequest = new HttpRequestViewModel(context.Request); } - - public static implicit operator (HttpContext, Setup)(MatchedRequest matchedRequest) => (matchedRequest.Context, matchedRequest.Setup); - public static implicit operator MatchedRequest((HttpContext, Setup) matchedRequest) => new MatchedRequest(matchedRequest.Item1, matchedRequest.Item2); } } diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index cb30c996..9ed9dfbc 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -2,19 +2,13 @@ netcoreapp3.1 - enable + Enable CS8600;CS8602;CS8603 - preview + Preview 1.0.0 1.0.0 - - - - - - bin\Release\NinjaTools.FluentMockServer.API.xml @@ -26,39 +20,20 @@ + - - + - - - - + + + + - + - - - - - - - - - - - - - - - - - - - diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs deleted file mode 100644 index 4c4eea43..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationContext.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Diagnostics; -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation -{ - [DebuggerDisplay("{IsMatch} {Ratings} | Messages={Messages}; Matcher={Matcher};", Name = "EvaluatingContext")] - public class EvaluationContext - { - public EvaluationContext(HttpContext httpContext) - { - HttpContext = httpContext; - Messages = new EvaluationMessages(); - Ratings = new EvaluationRatings(); - IsMatch = true; - } - - public HttpContext HttpContext { get; } - public NormalizedMatcher Matcher { get; } - public EvaluationMessages Messages { get; } - public EvaluationRatings Ratings { get; } - public bool IsMatch { get; internal set; } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs deleted file mode 100644 index 0b833f1e..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/EvaluationPipeline.cs +++ /dev/null @@ -1,79 +0,0 @@ -// using AutoMapper; -// using Microsoft.AspNetCore.Http; -// using NinjaTools.FluentMockServer.API.Models; -// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; -// using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; -// -// namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation -// { -// public static class EvaluationPipeline -// { -// private static readonly IEvaluator Pipeline; -// -// static EvaluationPipeline() -// { -// var evaluator = new HttpRequestEvaluator(); -// evaluator.SetNext(new HttpPathEvaluator()) -// .SetNext(new HttpMethodEvaluator()) -// .SetNext(new HttpBodyEvaluator()) -// .SetNext(new HttpCookieEvaluator()) -// .SetNext(new QueryStringEvaluator()) -// .SetNext(new HttpHeaderEvaluator()); -// -// Pipeline = evaluator; -// } -// -// public static IEvaluationResult Evaluate(HttpContext httpContext, RequestMatcher matcher) -// { -// var context = new EvaluationContext(httpContext, matcher.Normalize()); -// var evalResult = Pipeline.Evaluate(context); -// return evalResult; -// } -// -// -// -// -// -// private static IMapper? _mapper; -// public static IMapper Mapper -// { -// get -// { -// if (_mapper is null) -// { -// var config = new MapperConfiguration(cfg => cfg.AddProfile()); -// _mapper = config.CreateMapper(); -// } -// -// return _mapper; -// } -// } -// -// public static NormalizedMatcher Normalize(this RequestMatcher matcher) -// { -// var result = Mapper.Map(matcher); -// return result; -// } -// -// public class RequestMatcherProfile : Profile -// { -// public RequestMatcherProfile() -// { -// ShouldMapField = _ => true; -// ShouldMapProperty = _ => true; -// EnableNullPropagationForQueryMapping = true; -// AllowNullCollections = true; -// AllowNullDestinationValues = true; -// -// CreateMap() -// .ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.BodyMatcher.Content)) -// .ForMember(dest => dest.MatchExact, opt => opt.MapFrom(src => src.BodyMatcher.MatchExact)) -// .ForMember(dest => dest.BodyKind, opt => opt.MapFrom(src => src.BodyMatcher.Type)) -// .ForMember(dest => dest.Query, opt => opt.MapFrom(src => src.Query)) -// .ReverseMap(); -// } -// -// -// } -// } -// } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs deleted file mode 100644 index 56d9715f..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/EvaluatorBase.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Diagnostics; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators -{ - [DebuggerDisplay("{Name}; IsLast={IsLast};", Name = "Evaluator")] - public abstract class EvaluatorBase : IEvaluator - { - public string Name => GetType().Name; - protected abstract void EvaluateMember(EvaluationContext context); - public bool IsLast => _next is null; - - private IEvaluator? _next; - - /// - public IEvaluator SetNext(IEvaluator next) - { - _next = next; - return next; - } - - /// - public virtual IEvaluationResult Evaluate(EvaluationContext context) - { - EvaluateMember(context); - return _next != null ? _next.Evaluate(context) : context switch - { - { IsMatch: true } ctx => new EvaluationSuccessfulResult(ctx), - { } ctx => new EvaluationUnsuccessfulResult(ctx) - }; - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs deleted file mode 100644 index 3a4fc9b2..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpBodyEvaluator.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.IO; -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Extensions; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators -{ - public class HttpBodyEvaluator : EvaluatorBase - { - /// - protected override void EvaluateMember(EvaluationContext context) - { - if (context.EnsureNotNull(context.Matcher, context.HttpContext.Request.Body) is {} m) - { - - var request = context.HttpContext.Request; - - var bodyKind = context.Matcher.BodyKind; - var shouldMatchExact = context.Matcher.MatchExact; - var bodyContent = context.Matcher.Content; - - - request.EnableBuffering(); - request.Body.Position = 0; - using var reader = new StreamReader(request.Body); - var content = reader.ReadToEnd(); - - if (shouldMatchExact && bodyContent == content) - { - context.Match(content, bodyContent); - return; - } - else if (!shouldMatchExact && content.Length > 0 && bodyContent.Contains(content)) - { - context.Match(content, bodyContent); - return; - } - - context.Fail(context.HttpContext.Request.Body, bodyContent); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs deleted file mode 100644 index 4b7f7166..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpCookieEvaluator.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq; -using NinjaTools.FluentMockServer.API.Extensions; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators -{ - public class HttpCookieEvaluator : EvaluatorBase - { - /// - protected override void EvaluateMember(EvaluationContext context) - { - var cookies = context.Matcher.Cookies; - if (context.EnsureNotNull(context.HttpContext.Request.Cookies, cookies) is {} httpCookies) - { - var unsatisfiedCookies = cookies.Except(httpCookies).ToList(); - - if (unsatisfiedCookies.Any() != true) - { - context.Match(httpCookies, cookies); - return; - } - context.Fail(httpCookies, cookies); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs deleted file mode 100644 index c0e054e8..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpHeaderEvaluator.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; -using NinjaTools.FluentMockServer.API.Extensions; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators -{ - public class HttpHeaderEvaluator : EvaluatorBase - { - /// - protected override void EvaluateMember(EvaluationContext context) - { - var header = context.Matcher.Headers; - if (context.EnsureNotNull(context.HttpContext.Request.Headers, header) is {} httpHeader) - { - var unsatisfiedHeaders = header.Except(httpHeader - .ToDictionary(k => k.Key, v => v.Value.ToArray())); - - - if (unsatisfiedHeaders.Any() != true) - { - context.Match(httpHeader, header); - return; - } - - context.Fail(httpHeader, header); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs deleted file mode 100644 index ae2e64b3..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpMethodEvaluator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using NinjaTools.FluentMockServer.API.Extensions; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators -{ - public class HttpMethodEvaluator : EvaluatorBase - { - /// - protected override void EvaluateMember(EvaluationContext context) - { - var method = context.Matcher.Method; - if (context.EnsureNotNull(context.HttpContext.Request.Method, method) is {} httpMethod) - { - if (method is null) - { - context.Match(httpMethod, method); - return; - } - context.Fail(httpMethod, method); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs deleted file mode 100644 index 8e8194bd..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/HttpPathEvaluator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using NinjaTools.FluentMockServer.API.Extensions; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators -{ - public class HttpPathEvaluator : EvaluatorBase - { - - /// - protected override void EvaluateMember(EvaluationContext context) - { - var path = context.Matcher.Path; - if (context.EnsureNotNull(context.HttpContext.Request.Path.Value, path) is {} httpPath) - { - if (path is null) - { - context.Match(httpPath, path); - return; - } - context.Fail(httpPath, path); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs deleted file mode 100644 index 0d4b1326..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Evaluators/QueryStringEvaluator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using NinjaTools.FluentMockServer.API.Extensions; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators -{ - public class QueryStringEvaluator : EvaluatorBase - { - /// - protected override void EvaluateMember(EvaluationContext context) - { - var query = context.Matcher.Query; - if (context.EnsureNotNull(context.HttpContext.Request.QueryString.Value, query) is {} httpQuery) - { - if (query is null) - { - context.Match(httpQuery, query); - return; - } - context.Fail(httpQuery, query); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/HttpRequestEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/HttpRequestEvaluator.cs deleted file mode 100644 index 004aaa3e..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/HttpRequestEvaluator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation -{ - public class HttpRequestEvaluator : IEvaluator - { - private IEvaluator _evaluator; - - /// - public IEvaluator SetNext(IEvaluator next) - { - _evaluator = next; - return next; - } - - /// - public IEvaluationResult Evaluate(EvaluationContext context) - { - return _evaluator.Evaluate(context); - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/IEvaluator.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/IEvaluator.cs deleted file mode 100644 index 8d151600..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/IEvaluator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation -{ - public interface IEvaluator - { - IEvaluator SetNext(IEvaluator next); - - IEvaluationResult Evaluate(EvaluationContext context); - } -} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationMessages.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationMessages.cs deleted file mode 100644 index 557dfc84..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationMessages.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models -{ - [DebuggerDisplay("[M: {MessageCount} E: {ErrorCount} ] | Messages={Messages}; Errors={Exceptions};", Name = "EvaluatingMessages")] - public class EvaluationMessages - { - public int ErrorCount => Exceptions.Count; - public int MessageCount => Messages.Count; - public List Exceptions { get; } - public List Messages { get; } - - public EvaluationMessages() - { - Exceptions = new List(); - Messages = new List(); - } - } -} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationRatings.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationRatings.cs deleted file mode 100644 index ff07c5ab..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationRatings.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models -{ - [DebuggerDisplay("[{Score}] | EvaluationScore={WeightedEvaluationPoints}; BonusPoints={BonusPoints};")] - public class EvaluationRatings : List - { - public long Score => WeightedEvaluationPoints + BonusPoints; - public long WeightedEvaluationPoints => this.Sum(rating => rating.Score); - public int BonusPoints { get; internal set; } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs deleted file mode 100644 index 1308164a..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationResultBase.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models -{ - public abstract class EvaluationResultBase : IEvaluationResult - { - public EvaluationResultBase([NotNull] EvaluationContext context) - { - Messages = context.Messages.Messages; - Errors = context.Messages.Exceptions; - Ratings = context.Ratings; - } - - /// - public abstract bool IsMatch { get; } - - /// - public EvaluationRatings Ratings { get; } - - public long Score => Ratings.Score; - - /// - public IReadOnlyList Messages { get; } - - /// - public IReadOnlyList Errors { get; } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs deleted file mode 100644 index 40bb4413..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationSuccessfulResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Diagnostics; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models -{ - /// - [DebuggerDisplay("IsMatch={IsMatch}; Score={Ratings}; Errors={ErrorCount}; Messages={Messages};")] - public class EvaluationSuccessfulResult : EvaluationResultBase - { - /// - public EvaluationSuccessfulResult(EvaluationContext context) : base(context) - { - } - - /// - public override bool IsMatch => true; - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationUnsuccessfulResult.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationUnsuccessfulResult.cs deleted file mode 100644 index e71f99f3..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationUnsuccessfulResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models -{ - public class EvaluationUnsuccessfulResult : EvaluationResultBase - { - /// - public override bool IsMatch => false; - - /// - public EvaluationUnsuccessfulResult(EvaluationContext context) : base(context) - { - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationWeight.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationWeight.cs deleted file mode 100644 index aa5b5184..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/EvaluationWeight.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators -{ - public enum EvaluationWeight : ushort - { - Non = 0, - Minimal = 1, - Low = 2, - High = 3, - Max = 4, - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs deleted file mode 100644 index 96d0bc8b..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/IEvaluationResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models -{ - public interface IEvaluationResult - { - long Score { get; } - bool IsMatch { get; } - EvaluationRatings Ratings { get; } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/NormalizedMatcher.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/NormalizedMatcher.cs deleted file mode 100644 index e3f3336f..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/NormalizedMatcher.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using NinjaTools.FluentMockServer.API.Models; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models -{ - public class NormalizedMatcher - { - public string? Method { get; private protected set; } - public string? Path { get; private protected set; } - public string? Query { get; private protected set; } - public Dictionary? Headers { get; private protected set; } - public Dictionary? Cookies { get; private protected set; } - - - public bool IsHttps { get; private protected set; } - - public bool HasBody => Content != null; - public string? Content { get; private protected set; } - public RequestBodyKind BodyKind { get; private protected set; } - public bool MatchExact { get; private protected set; } - } - -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs deleted file mode 100644 index e1aa0bc0..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Models/Rating.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Diagnostics; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Evaluators; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models -{ - [DebuggerDisplay("{HttpRequestMember} {Score} | Points={Points}; Weight={Weight};", Name = "Rating")] - public class Rating - { - public Rating(uint points, EvaluationWeight weight) - { - Points = points; - Weight = weight; - } - - public uint Points { get; } - public EvaluationWeight Weight { get; } - public uint Score => Points * (ushort) Weight; - - - public object? HttpRequestMember { get; set; } - public object? Member { get; set; } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Rules/IRule.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Rules/IRule.cs deleted file mode 100644 index c4d351e4..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Rules/IRule.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Rules -{ - public interface IRule - { - - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs deleted file mode 100644 index 402e3f08..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IPartialVisitor.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using NinjaTools.FluentMockServer.API.Models; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors -{ - public interface IPartialVisitor : IVisitor - { - void VisitCookies(IDictionary? cookies); - void VisitHeaders(IDictionary? headers); - void VisitPath(string? path); - void VisitMethod(string? method); - void VisitQuery(string? query); - - void VisitBody(RequestBodyMatcher? bodyMatcher);// => VisitBody(bodyMatcher.Content, bodyMatcher.MatchExact, bodyMatcher.Type); - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequest.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequest.cs deleted file mode 100644 index f10c47d4..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors -{ - public interface IRequest - { - void Accept(Func visitorFactory); - T Accept(Func> visitorFactory); - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestBody.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestBody.cs deleted file mode 100644 index 3fc7cd06..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestBody.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors -{ - public interface IRequestBody - { - void Accept(Func visitorFactory); - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestMatcherEvaluatorVistor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestMatcherEvaluatorVistor.cs deleted file mode 100644 index 0cc2bef0..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IRequestMatcherEvaluatorVistor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using NinjaTools.FluentMockServer.API.Models; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors -{ - public interface IRequestMatcherEvaluatorVistor : IPartialVisitor - { - void Visit(RequestMatcher matcher); - } -} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs deleted file mode 100644 index 98b974ce..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/IVisitor.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors -{ - public interface IRequestMatcherEvaluatorVistor : IRequestMatcherEvaluatorVistor - { - T Evaluate(); - } - - public interface IVisitor - { - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs deleted file mode 100644 index a659cb69..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Evaluation/Visitors/RequestEvaluatorVistor.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Microsoft.AspNetCore.Http; -using NinjaTools.FluentMockServer.API.Extensions; -using NinjaTools.FluentMockServer.API.Models; -using NinjaTools.FluentMockServer.API.Proxy.Evaluation.Models; - -namespace NinjaTools.FluentMockServer.API.Proxy.Evaluation.Visitors -{ - internal class RequestEvaluatorVistor : IRequestMatcherEvaluatorVistor - { - - private readonly EvaluationContext _evaluationContext; - private HttpRequest HttpRequest => _evaluationContext.HttpContext.Request; - - public RequestEvaluatorVistor(EvaluationContext evaluationContext) - { - _evaluationContext = evaluationContext; - } - - /// - public void Visit(RequestMatcher matcher) - { - VisitHeaders(matcher.Headers); - VisitMethod(matcher.Method); - VisitPath(matcher.Path); - VisitQuery(matcher.Query); - VisitCookies(matcher.Cookies); - VisitBody(matcher.BodyMatcher); - matcher.Accept(() => this); - } - - /// - public EvaluationResultBase Evaluate() - { - return _evaluationContext switch - { - { IsMatch: true } ctx => new EvaluationSuccessfulResult(ctx), - { } ctx => new EvaluationUnsuccessfulResult(ctx) - }; - } - - /// - public void VisitCookies(IDictionary? cookies) - { - if (_evaluationContext.EnsureNotNull(HttpRequest.Cookies, cookies) is {} httpCookies) - { - if (cookies.Except(httpCookies).Any()) - { - _evaluationContext.Fail(httpCookies, cookies); - } - else - { - _evaluationContext.Match(httpCookies, cookies); - } - } - } - - - - /// - public void VisitHeaders(IDictionary? headers) - { - if (_evaluationContext.EnsureNotNull(HttpRequest.Headers, headers) is {} httpRequestMember) - { - if (headers.Except(httpRequestMember?.ToDictionary(k => k.Key, v => v.Value.ToArray())).Any()) - { - _evaluationContext.Fail(httpRequestMember, headers); - } - else - { - _evaluationContext.Match(httpRequestMember, headers); - } - } - } - - /// - public void VisitPath(string? path) - { - if (_evaluationContext.EnsureNotNull(HttpRequest.Path.Value, path) is {} httpPath) - { - if(httpPath != path) - { - _evaluationContext.Fail(httpPath, path); - } - else - { - _evaluationContext.Match(httpPath, path); - } - } - } - - /// - public void VisitMethod(string? method) - { - if (_evaluationContext.EnsureNotNull(HttpRequest.Method, method) is {} httpMethod) - { - if (httpMethod != method) - { - _evaluationContext.Fail(httpMethod, method); - } - else - { - _evaluationContext.Match(httpMethod, method); - } - } - } - - /// - public void VisitQuery(string? query) - { - if (_evaluationContext.EnsureNotNull(HttpRequest.QueryString.Value, query) is {} httpQuery) - { - if (httpQuery != query) - { - _evaluationContext.Fail(httpQuery, query); - } - else - { - _evaluationContext.Match(httpQuery, query); - } - } - } - - /// - public void VisitBody(RequestBodyMatcher? bodyMatcher) - { - if (bodyMatcher != null) - { - VisitBody(bodyMatcher.Content, bodyMatcher.MatchExact, bodyMatcher.Type); - } - } - - /// - private void VisitBody(string? requestBody, bool exactMatch, RequestBodyKind kind) - { - HttpRequest.EnableBuffering(); - if (_evaluationContext.EnsureNotNull(HttpRequest.Body, requestBody) is { } httpBodyStream && httpBodyStream.CanSeek) - { - httpBodyStream.Seek(0, SeekOrigin.Begin); - using var reader = new StreamReader(HttpRequest.Body); - var httpBody = reader.ReadToEnd(); - - if (requestBody is { } body && HttpRequest.Body.CanSeek) - { - if (exactMatch && httpBody == body) - { - _evaluationContext.Match(httpBody, body); - } - else - { - _evaluationContext.Fail(httpBody, body); - } - } - } - } - - - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/CookieCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/CookieCollection.cs deleted file mode 100644 index 7296c1df..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/CookieCollection.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; - -namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections -{ - public class CookieCollection : Dictionary, IVisitable - { - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } -} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeaderCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeaderCollection.cs deleted file mode 100644 index e311fcb6..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HeaderCollection.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.AspNetCore.Http; - -namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections -{ - public class HeaderCollection : HeaderDictionary, IVisitable - { - public HeaderCollection() : this(new Dictionary()) - { } - - public HeaderCollection(IDictionary dict) - { - dict ??= new Dictionary(); - foreach (var (key, value) in dict) - { - Add(key, value); - } - } - - public static implicit operator HeaderCollection(Dictionary headers) => From(headers); - public static implicit operator Dictionary(HeaderCollection headers) => - headers.ToDictionary(k => k.Key, - v => v.Value.ToArray()); - - public static HeaderCollection From(IDictionary headers) - { - var headerCollection = new HeaderCollection(); - headers ??= new Dictionary(); - foreach (var header in headers) - { - headerCollection.Add(header.Key, header.Value); - } - - return headerCollection; - } - - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HttpMethodWrapper.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HttpMethodWrapper.cs deleted file mode 100644 index 966e2b24..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/HttpMethodWrapper.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections -{ - public class HttpMethodWrapper : IVisitable - { - public HttpMethodWrapper(string methodString) - { - MethodString = methodString; - } - - public HttpMethodWrapper() - { } - - public string MethodString { get; set; } - public static implicit operator string(HttpMethodWrapper wrapper) => wrapper.MethodString; - public static implicit operator HttpMethodWrapper(string str) => new HttpMethodWrapper {MethodString = str}; - - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } -} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/PathCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/PathCollection.cs deleted file mode 100644 index 4db15725..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/PathCollection.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.AspNetCore.Http; - -namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections -{ - public class PathCollection : IVisitable - { - public static implicit operator string(PathCollection? query) => query?.Path; - public static implicit operator PathCollection(string? str) => new PathCollection {PathString = $"/{str?.TrimStart('/')}"}; - - /// - public PathString PathString { get; set; } - public string Path => PathString.Value; - - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } -} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/QueryCollection.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/QueryCollection.cs deleted file mode 100644 index 307ac15d..00000000 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/Collections/QueryCollection.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.AspNetCore.Http; - -namespace NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections -{ - public class QueryCollection : IVisitable - { - public static implicit operator string(QueryCollection query) => query.Query; - public static implicit operator QueryCollection(QueryString query) => new QueryCollection {QuryString = query}; - public static implicit operator QueryCollection(string str) => new QueryCollection {QuryString = new QueryString($"?{str.TrimStart('?')}")}; - - /// - public QueryString QuryString { get; set; } - public string Query => QuryString.Value; - - /// - public void Accept(IVisitor visitor) - { - if (visitor is IVisitor typed) - { - typed.Visit(this); - } - } - } -} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs index e3867967..203d5666 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs @@ -1,40 +1,34 @@ -using System; using System.IO; using System.Linq; using System.Threading; +using Ardalis.GuardClauses; using Microsoft.AspNetCore.Http; +using NinjaTools.FluentMockServer.API.Extensions; using NinjaTools.FluentMockServer.API.Models; -using NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections; -using QueryCollection = NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections.QueryCollection; -// ReSharper disable PossibleLossOfFraction +using Path = NinjaTools.FluentMockServer.API.Models.Path; namespace NinjaTools.FluentMockServer.API.Proxy.Visitors { - public class ComparasionVisitor : IVisitor, + /// + public sealed class ComparasionVisitor : IVisitor, IVisitor, - IVisitor, - IVisitor, - IVisitor, - IVisitor, - IVisitor, + IVisitor, + IVisitor, + IVisitor, + IVisitor, + IVisitor, IVisitor { - protected HttpContext HttpContext { get; } - protected HttpRequest HttpRequest => HttpContext.Request; + private HttpContext HttpContext { get; } + private HttpRequest HttpRequest => HttpContext.Request; public bool IsSuccess { get; set; } = true; - public double Score { get; set; } = 0; + public double Score { get; set; } + private static double Pass() => 1; - - protected virtual void Fail() + private double Fail() { IsSuccess = false; - //throw new ExecutionEngineException("FAIL"); - } - - protected virtual double? Pass() - { - Score += 1; - return Score; + return 0; } public ComparasionVisitor(HttpContext httpContext) @@ -42,180 +36,135 @@ public ComparasionVisitor(HttpContext httpContext) HttpContext = httpContext; } - /// + /// /// ✅ Entrypoint ✅ - public void Visit(RequestMatcher visitable, CancellationToken token = default) + public double Visit(RequestMatcher visitable, CancellationToken token = default) { - Visit(visitable.Cookies); - Visit(visitable.Headers); - Visit(visitable.Query); - Visit(visitable.Path); - Visit(visitable.Method); - Visit(visitable.BodyMatcher); + Score += Visit(visitable.Cookies); + Score += Visit(visitable.Headers); + Score += Visit(visitable.Query); + Score += Visit(visitable.Path); + Score += Visit(visitable.Method); + Score += Visit(visitable.BodyMatcher); + return Score; } /// - public void Visit(RequestBodyMatcher visitable, CancellationToken token = default) + public double Visit(RequestBodyMatcher? visitable, CancellationToken token = default) { token.ThrowIfCancellationRequested(); + if (!visitable?.IsEnabled() ?? true) + return Pass(); - if (visitable is null || string.IsNullOrWhiteSpace(visitable.Content)) - { - Pass(); - return; - } - + Guard.Against.Null(visitable, nameof(visitable) + nameof(RequestBodyMatcher)); if (HttpRequest.Body is null) - { - Fail(); - return; - } + return Fail(); HttpRequest.EnableBuffering(); HttpRequest.Body.Seek(0, SeekOrigin.Begin); using var reader = new StreamReader(HttpRequest.Body); var httpBody = reader.ReadToEnd(); - if (visitable.MatchExact && httpBody == visitable.Content) - { - Pass(); - return; - } - - Fail(); + if (visitable.MatchExact.HasValue && visitable.MatchExact.Value && httpBody == visitable.Content) + return Pass(); + return Fail(); } /// - public void Visit(HeaderDictionary visitable, CancellationToken token = default) + public double Visit(Headers? visitable, CancellationToken token = default) { token.ThrowIfCancellationRequested(); - if (visitable is null) - { - Pass(); - return; - } + if (!visitable?.Any() ?? true) + return Pass(); - if (HttpRequest.Headers is null && visitable is { } v && v.Any()) - { - Fail(); - return; - } + Guard.Against.NullOrEmpty(visitable.Header, nameof(visitable) + nameof(Headers)); - var rest = visitable?.Except(HttpRequest.Headers); + if (HttpRequest.Headers is null && visitable.Header.EnsureNotNullNotEmpty() is { }) + return Fail(); - if (rest is {} r && r.Any() ) + var success = visitable.Header.Where(v => { - Fail(); - return; - } + if (!HttpContext.Request.Headers.TryGetValue(v.Key, out var value)) + return false; + + return !value.Except(v.Value).Any(); - Pass(); - return; + }).Any(); + return success ? Pass() : Fail(); } + /// - public void Visit(CookieCollection visitable, CancellationToken token = default) + public double Visit(Cookies? visitable, CancellationToken token = default) { token.ThrowIfCancellationRequested(); + if (!visitable?.Any() ?? true) + return Pass(); - if (visitable is null) - { - Pass(); - return; - } - if (HttpRequest.Cookies is null && visitable is { } v && v.Any()) - { + Guard.Against.NullOrEmpty(visitable.Cookie, nameof(visitable) + nameof(Cookies)); + + if (HttpRequest.Cookies is null && visitable.Cookie.EnsureNotNullNotEmpty().Any()) Fail(); - return; - } - var rest = visitable?.Except(HttpRequest.Cookies); + if (visitable.Cookie.Except(HttpContext.Request.Cookies).EnsureNotNullNotEmpty().Any()) + return Fail(); - if (rest is {} r && r.Any() ) - { - Fail(); - return; - } - Pass(); - return; + return Pass(); } /// - public void Visit(QueryCollection visitable, CancellationToken token = default) + public double Visit(Query visitable, CancellationToken token = default) { token.ThrowIfCancellationRequested(); + if (!visitable?.IsEnabled() ?? true) + return Pass(); - if (visitable is null) - { - Pass(); - return; - } + Guard.Against.NullOrEmpty(visitable.QueryString.Value, nameof(visitable) + nameof(Query)); + if (!HttpRequest.QueryString.HasValue && visitable.QueryString.HasValue) + return Fail(); - if (!HttpRequest.QueryString.HasValue && visitable is { } v && v.QuryString.HasValue) - { - Fail(); - return; - } - if (visitable?.Query == HttpRequest.QueryString.Value) - { - Pass(); - return; - } + if (visitable.QueryString.Value == HttpRequest.QueryString.Value) + return Pass(); - Fail(); + return Fail(); } /// - public void Visit(PathCollection visitable, CancellationToken token = default) + public double Visit(Path? visitable, CancellationToken token = default) { token.ThrowIfCancellationRequested(); + if (!visitable?.IsEnabled() ?? true) + return Pass(); - if (visitable is null) - { - Pass(); - return; - } - if (!HttpRequest.Path.HasValue && visitable is { } v && v.PathString.HasValue) - { - Fail(); - return; - } - if (visitable?.Path == HttpRequest.Path.Value) - { - Pass(); - return; - } + Guard.Against.NullOrWhiteSpace(visitable, nameof(visitable) + nameof(Path)); + + if (!HttpRequest.Path.HasValue && visitable is { } v && v.PathString.HasValue) + return Fail(); - Fail(); - return; + if (visitable.ToPath() == HttpRequest.Path.Value) + return Pass(); + return Fail(); } /// - public void Visit(HttpMethodWrapper visitable, CancellationToken token = default) + public double Visit(Method? visitable, CancellationToken token = default) { token.ThrowIfCancellationRequested(); + if (!visitable?.IsEnabled() ?? true) + return Pass(); - if (visitable is null) - { - Pass(); - return; - } - if (string.IsNullOrWhiteSpace(HttpRequest.Method) && visitable is { } v && !string.IsNullOrWhiteSpace(v.MethodString)) - { - Fail(); - return; - } - if (visitable.MethodString == HttpRequest.Method) - { - Pass(); - return; - } + Guard.Against.NullOrEmpty(visitable, nameof(visitable) + nameof(Method)); + if (string.IsNullOrWhiteSpace(HttpRequest.Method) && visitable is { } v && !string.IsNullOrWhiteSpace(v.MethodString)) + return Fail(); - Fail(); + if (visitable.MethodString == HttpRequest.Method) + return Pass(); + + return Fail(); } } } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitable.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitable.cs new file mode 100644 index 00000000..cdc5f604 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitable.cs @@ -0,0 +1,7 @@ +namespace NinjaTools.FluentMockServer.API.Proxy.Visitors +{ + public interface IVisitable + { + void Accept(IVisitor visitor); + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitor.cs index 9468e2f7..9362b3e0 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitor.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/IVisitor.cs @@ -1,37 +1,4 @@ -using System.Collections.Generic; using System.Threading; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections; -using QueryCollection = NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections.QueryCollection; - -namespace NinjaTools.FluentMockServer.API.Models -{ - public class RequestBodyMatcher - { - public string? Content { get; set; } - public RequestBodyKind Kind { get; set; } - public bool MatchExact { get; set; } - } - - [JsonConverter(typeof(StringEnumConverter))] - public enum RequestBodyKind - { - Text, - Base64 - } - public class RequestMatcher - { - public RequestBodyMatcher? BodyMatcher { get; set; } - public HttpMethodWrapper? Method { get; set; } - public HeaderCollection? Headers { get; set; } - public CookieCollection? Cookies { get; set; } - public QueryCollection? Query { get; set; } - public PathCollection? Path { get; set; } - } -} namespace NinjaTools.FluentMockServer.API.Proxy.Visitors { @@ -41,11 +8,6 @@ public interface IVisitor public interface IVisitor { void Visit(TVisitable visitable) => Visit(visitable,default); - void Visit(TVisitable visitable, CancellationToken token); - } - - public interface IVisitable - { - void Accept(IVisitor visitor); + double Visit(TVisitable visitable, CancellationToken token); } } diff --git a/src/NinjaTools.FluentMockServer.API/Serialization/Converters/HeadersConverter.cs b/src/NinjaTools.FluentMockServer.API/Serialization/Converters/HeadersConverter.cs new file mode 100644 index 00000000..02e8910a --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Serialization/Converters/HeadersConverter.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using NinjaTools.FluentMockServer.API.Models; +using YamlDotNet.Core; +using YamlDotNet.Serialization; + +namespace NinjaTools.FluentMockServer.API.Serialization.Converters +{ + /// + public class HeadersConverter : IYamlTypeConverter + { + /// + public bool Accepts(Type type) => typeof(Headers) == type; + + /// + public object? ReadYaml(IParser parser, Type type) + { + var deserializer = new DeserializerBuilder() + .IgnoreUnmatchedProperties() + .Build(); + + var headers = deserializer.Deserialize>(parser); + var headerCollection = new Headers(headers); + return headerCollection; + } + + /// + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var serializer = new SerializerBuilder() + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) + .EnsureRoundtrip() + .Build(); + + var headerCollection = (Headers) value!; + var headers = (IDictionary) headerCollection; + + serializer.Serialize(emitter, headers); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Serialization/Converters/MethodConverter.cs b/src/NinjaTools.FluentMockServer.API/Serialization/Converters/MethodConverter.cs new file mode 100644 index 00000000..e464f856 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Serialization/Converters/MethodConverter.cs @@ -0,0 +1,51 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NinjaTools.FluentMockServer.API.Models; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace NinjaTools.FluentMockServer.API.Serialization.Converters +{ + /// + public class MethodConverter : JsonConverter, IYamlTypeConverter + { + /// + public override void WriteJson(JsonWriter writer, Method value, JsonSerializer serializer) + { + var t = JToken.FromObject(value?.MethodString); + if (t.Type != JTokenType.Object) + { + t.WriteTo(writer); + } + } + + /// + public override Method ReadJson(JsonReader reader, Type objectType, Method existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var value = serializer.Deserialize(reader); + return new Method(value); + } + + + /// + public bool Accepts(Type type) => typeof(Method) == type; + + /// + public object? ReadYaml(IParser parser, Type type) + { + var value = parser.Consume().Value; + var pathCollection = new Method(value); + return pathCollection; + } + + /// + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var methodWrapper = (Method) value!; + var path = methodWrapper.MethodString; + emitter.Emit(new Scalar(null, null, path, ScalarStyle.Any, true, false)); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Serialization/Converters/PathConverter.cs b/src/NinjaTools.FluentMockServer.API/Serialization/Converters/PathConverter.cs new file mode 100644 index 00000000..b54a2c81 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Serialization/Converters/PathConverter.cs @@ -0,0 +1,49 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NinjaTools.FluentMockServer.API.Models; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace NinjaTools.FluentMockServer.API.Serialization.Converters +{ + /// + public class PathConverter : JsonConverter, IYamlTypeConverter + { + /// + public override void WriteJson(JsonWriter writer, Path value, JsonSerializer serializer) + { + var jt = JToken.FromObject(value.ToPath()); + if (jt.Type != JTokenType.Object) + { + jt.WriteTo(writer); + } + } + + /// + public override Path ReadJson(JsonReader reader, Type objectType, Path existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var value = JToken.ReadFrom(reader); + return new Path(value.ToString()); + } + + public bool Accepts(Type type) => typeof(Path) == type; + + /// + public object? ReadYaml(IParser parser, Type type) + { + var value = parser.Consume().Value; + var pathCollection = new Path(value); + return pathCollection; + } + + /// + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var pathCollection = (Path) value!; + var path = pathCollection.ToPath(); + emitter.Emit(new Scalar(null, null, path, ScalarStyle.Any, true, false)); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/Types/IDontRenderWhenEmpty.cs b/src/NinjaTools.FluentMockServer.API/Types/IDontRenderWhenEmpty.cs new file mode 100644 index 00000000..dc64eb86 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Types/IDontRenderWhenEmpty.cs @@ -0,0 +1,11 @@ +namespace NinjaTools.FluentMockServer.API.Types +{ + public interface IDontRenderWhenEmpty + { + /// + /// Displays whether of not that component will be serialized. + /// + /// + bool IsEnabled(); + } +} diff --git a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj index 99b7305d..2a52828a 100644 --- a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj +++ b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj @@ -2,8 +2,6 @@ 36A37D14-D7FC-4E32-8F48-5FB606818757 - 1.0.8 - netstandard2.1 1.1.0 netstandard2.1;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1 8.0 @@ -46,6 +44,7 @@ + diff --git a/src/NinjaTools.FluentMockServer/Serialization/ExpectationConverter.cs b/src/NinjaTools.FluentMockServer/Serialization/ExpectationConverter.cs index f0485563..89d7214d 100644 --- a/src/NinjaTools.FluentMockServer/Serialization/ExpectationConverter.cs +++ b/src/NinjaTools.FluentMockServer/Serialization/ExpectationConverter.cs @@ -78,15 +78,6 @@ public override Expectation ReadJson(JsonReader reader, [NotNull] Type objectTyp Console.WriteLine(e); } } -// if (token.SelectToken("httpRequest", false) is {} httpRequest) -// try -// { -// expectation.HttpRequest = httpRequest.ToObject(); -// } -// catch (Exception e) -// { -// Console.WriteLine(e); -// } return expectation; } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Configuration/ConfigFileProviderTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Configuration/ConfigFileProviderTests.cs index 8360fc71..486199c8 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Configuration/ConfigFileProviderTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Configuration/ConfigFileProviderTests.cs @@ -13,8 +13,7 @@ public class ConfigurationProviderTests : XUnitTestBase public ConfigurationProviderTests(ITestOutputHelper output) : base(output) - { - } + { } [Fact] @@ -66,13 +65,14 @@ public void Returns_ConfigurationFile_When_Only_One_Yaml_File() configurations[1].Configurations.Should().HaveCount(2); var setupB = configurations[1].Configurations.First(); setupB.Action.Response.StatusCode.Should().Be(201); - setupB.Matcher.Path.Should().Be("/some/path"); - setupB.Matcher.Method.Should().Be("POST"); + setupB.Matcher.Path.ToPath().Should().Be("/some/path"); + setupB.Matcher.Method.MethodString.Should().Be("POST"); var setupC = configurations[1].Configurations.ElementAt(1); - setupC.Matcher.Headers.Should().HaveCount(2); - setupC.Matcher.Path.Should().Be("/"); + // Dump(setupC, "Setup - C"); + setupC.Matcher.Headers.Header.Should().HaveCount(2); + setupC.Matcher.Path.ToPath().Should().Be("/"); setupC.Action.Should().BeNull(); } @@ -146,13 +146,13 @@ public void Returns_ConfigurationFile_When_Only_Only_JSON_Files() configurations[1].Configurations.Should().HaveCount(2); var setupB = configurations[1].Configurations.First(); setupB.Action.Response.StatusCode.Should().Be(201); - setupB.Matcher.Path.Should().Be("/some/path"); - setupB.Matcher.Method.Should().Be("POST"); + setupB.Matcher.Path.ToPath().Should().Be("/some/path"); + setupB.Matcher.Method.MethodString.Should().Be("POST"); var setupC = configurations[1].Configurations.ElementAt(1); - setupC.Matcher.Headers.Should().HaveCount(2); - setupC.Matcher.Path.Should().Be("/"); + setupC.Matcher.Headers.Header.Should().HaveCount(2); + setupC.Matcher.Path.ToPath().Should().Be("/"); setupC.Action.Should().BeNull(); } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs b/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs index 0a9a9149..925f31dd 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Extensions/EnumExtensions.cs @@ -48,8 +48,6 @@ public void GetDescription_Should_Throw_When_Enum_Does_Not_Contain_Description_A invocation.Should().ThrowExactly(); } - - [Fact] public void GetDescription_Should_Return_DescriptionValue_When_EnumValue_Contains_Description_Attribute() { diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs index d6ae1252..9e0064b7 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Infrastructure/SetupRepositoryTests.cs @@ -32,7 +32,7 @@ public void Add_Should_Create_A_SetupLogCreated_Log_Entry() { Matcher = new RequestMatcher { - Path = "/some/path" + Path = new Path("/some/path") } }; diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs index d7e59fb3..728e1168 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs @@ -14,7 +14,11 @@ public class LogItemTestData : TheoryData { private static readonly HttpContext Context = new DefaultHttpContext { - Request = { Path = "/request/path",Method = "PUT" } + Request = + { + Path = new PathString("/request/path"), + Method = "PUT" + } }; private static readonly Setup DefaultSetup = new Setup @@ -29,8 +33,8 @@ public class LogItemTestData : TheoryData }, Matcher = new RequestMatcher { - Method = "POST", - Path = "/test/path" + Method = new Method("POST"), + Path = new Path( "/test/path") } }; @@ -114,6 +118,7 @@ public LogItemTestData() } } + public class LogItemTests : XUnitTestBase { @@ -160,7 +165,7 @@ public void ToFormattedString_Should_Return_PrettyFormatted_String(ILogItem log, // Act var formatted = log.ToFormattedString(); - Dump(expected, formatted); + // Dump(expected, formatted); // Assert formatted.Should().Be(expected); diff --git a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj index 559f950d..1125f969 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj @@ -2,7 +2,6 @@ netcoreapp3.1 - false @@ -25,4 +24,6 @@ + + diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/ComparasionVisitorTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/ComparasionVisitorTests.cs index 0243a1d2..339cb6cd 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Proxy/ComparasionVisitorTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Proxy/ComparasionVisitorTests.cs @@ -6,10 +6,10 @@ using Microsoft.AspNetCore.Http; using NinjaTools.FluentMockServer.API.Models; using NinjaTools.FluentMockServer.API.Proxy.Visitors; -using NinjaTools.FluentMockServer.API.Proxy.Visitors.Collections; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; using Xunit.Abstractions; +using Path = NinjaTools.FluentMockServer.API.Models.Path; namespace NinjaTools.FluentMockServer.API.Tests.Proxy { @@ -27,14 +27,14 @@ public ComparasionVisitorTests(ITestOutputHelper output) : base(output) public void When_IsMatch_Returns_True_When_Method_Is_Equal(string method) { // Arrange - var compare = new HttpMethodWrapper(method); + var compare = new Method(method); var context = CreateContext(method); var subject = new ComparasionVisitor(context); // Act& Assert - subject.Visit(compare); + var score = subject.Visit(compare); subject.IsSuccess.Should().BeTrue(); - subject.Score.Should().Be(1); + score.Should().Be(1); } [Theory] @@ -45,14 +45,14 @@ public void When_IsMatch_Returns_True_When_Method_Is_Equal(string method) public void When_IsMatch_Returns_True_When_Path_Is_Match(string path) { // Arrange - var compare = CreateObject(path: path); + var compare = new Path(path); var context = CreateContext(path: path); var subject = new ComparasionVisitor(context); // Act& Assert - subject.Visit(compare); + var score = subject.Visit(compare); subject.IsSuccess.Should().BeTrue(); - subject.Score.Should().BeGreaterThan(1); + score.Should().Be(1); } [Theory] @@ -60,14 +60,14 @@ public void When_IsMatch_Returns_True_When_Path_Is_Match(string path) public void When_IsMatch_Returns_True_When_Headers_Are_Equal(Dictionary request, Dictionary contextHeaders, bool isValid) { // Arrange - var compare = new HeaderCollection(request); + var compare = new Headers(request); var context = CreateContext(headers: contextHeaders); var subject = new ComparasionVisitor(context); // Act& Assert - subject.Visit(compare); + var score = subject.Visit(compare); subject.IsSuccess.Should().Be(isValid); - subject.Score.Should().Be(isValid ? 1 : 0); + score.Should().Be(isValid ? 1 : 0); } [ItemNotNull] @@ -149,31 +149,33 @@ private HttpContext CreateContext(string method = null, string path = null, Dict if (queryString != null) { - request.QueryString = queryString.Value; + request.QueryString = new QueryString(queryString.Value.Value); } - foreach (var header in headers ?? new Dictionary()) + if (headers != null) { - request.Headers.Add(header.Key, header.Value); + foreach (var (key, value) in headers) + { + request.Headers.Add(key, value); + } } - return context; } private RequestMatcher CreateObject( string method = null, string path = null, - Dictionary headers = null, + IDictionary headers = null, RequestBodyMatcher bodyMatcher = null, - QueryString? queryString = null) + QueryString? queryString = null ) { return new RequestMatcher { - Method = method, - Path = path, - Headers = headers, + Method = new Method(method), + Path = new Path(path), + Headers = new Headers(headers), BodyMatcher = bodyMatcher, - Query = queryString + Query = new Query(queryString.HasValue ? queryString.Value : QueryString.Empty) }; } @@ -181,12 +183,13 @@ private RequestMatcher CreateObject( public void IsMatch_Should_Be_False_When_QueryString_Not_Same() { // Arrange - var context = CreateContext(queryString: new QueryString("?id=100")); + var context = CreateContext(queryString: new QueryString()); var sut = new ComparasionVisitor(context); // Act & Assert - sut.Visit(new QueryString("?color=green")); + var score = sut.Visit(new Query(new QueryString("?id=100"))); sut.IsSuccess.Should().BeFalse(); + score.Should().Be(0); } [Fact] @@ -208,9 +211,9 @@ public void IsMatch_Should_Be_False_When_RequestBodyMatcher_IsNotMatch() var subject = new ComparasionVisitor(context); // Act& Assert - subject.Visit(bodyMatcher); + var score = subject.Visit(bodyMatcher); subject.IsSuccess.Should().BeFalse(); - subject.Score.Should().Be(0); + score.Should().Be(0); } @@ -222,9 +225,9 @@ public void IsMatch_Should_Be_True_When_No_RequestBodyMatcher() var subject = new ComparasionVisitor(context); // Act& Assert - subject.Visit((RequestBodyMatcher) null); + var score = subject.Visit((RequestBodyMatcher) null); subject.IsSuccess.Should().BeTrue(); - subject.Score.Should().Be(1); + score.Should().Be(1); } [Fact] @@ -236,9 +239,9 @@ public void IsMatch_Should_Be_True_When_QueryString_IsMatch() var subject = new ComparasionVisitor(context); // Act& Assert - subject.Visit(query); + var score = subject.Visit(new Query(query)); subject.IsSuccess.Should().BeTrue(); - subject.Score.Should().Be(1); + score.Should().Be(1); } [Fact] @@ -249,9 +252,9 @@ public void IsMatch_Should_Be_True_When_QueryString_Not_Set() var subject = new ComparasionVisitor(context); // Act& Assert - subject.Visit((API.Proxy.Visitors.Collections.QueryCollection) null); + var score = subject.Visit((Query) null); subject.IsSuccess.Should().BeTrue(); - subject.Score.Should().Be(1); + score.Should().Be(1); } @@ -274,9 +277,9 @@ public void IsMatch_Should_Be_True_When_RequestBodyMatcher_IsMatch() var subject = new ComparasionVisitor(context); // Act& Assert - subject.Visit(bodyMatcher); + var score = subject.Visit(bodyMatcher); subject.IsSuccess.Should().BeTrue(); - subject.Score.Should().Be(1); + score.Should().Be(1); } } } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Serilization/HeadersTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Serilization/HeadersTests.cs new file mode 100644 index 00000000..7605f24d --- /dev/null +++ b/test/NinjaTools.FluentMockServer.API.Tests/Serilization/HeadersTests.cs @@ -0,0 +1,76 @@ +using FluentAssertions; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NinjaTools.FluentMockServer.API.Models; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.API.Tests.Serilization +{ + public class HeadersTests : XUnitTestBase + { + public HeadersTests(ITestOutputHelper output) : base(output) + { } + + [Fact] + public void Should_Serialize() + { + // Arrange + const string expected = +@"{ + ""a"": [ + ""1"" + ], + ""b"": [ + ""1"" + ], + ""c"": [ + ""1"" + ], + ""d"": [ + ""1"" + ] +}"; + var instance = new Headers(("a", new []{"1"}),("b", new []{"1"}),("c", new []{"1"}),("d", new []{"1"})); + + // Act + var jo = JObject.FromObject(instance); + Dump(jo, "Headers - JObject"); + var json = jo.ToString(Formatting.Indented); + + // Assert + Output.WriteLine(json); + json.Should().Be(expected); + } + + + [Fact] + public void Should_Deserialize() + { + // Arrange + var expected = new Headers(("a", new []{"1"}),("b", new []{"1"}),("c", new []{"1"}),("d", new []{"1"})); + + // Act + var instance = JObject.Parse(@"{ + ""a"": [ + ""1"" + ], + ""b"": [ + ""1"" + ], + ""c"": [ + ""1"" + ], + ""d"": [ + ""1"" + ] +}"); + Dump(instance, "HeaderCollection - JObject"); + var dict = instance.ToObject(); + + // Assert + dict.Should().HaveSameCount(expected.Header); + } + } +} diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs index 82bb5172..df6c8f51 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs @@ -17,8 +17,7 @@ public class LogServiceTests : XUnitTestBase { /// public LogServiceTests(ITestOutputHelper output) : base(output) - { - } + { } private ILogService CreateSubject(out Mock repo, GenerateId idGenerator = null) { @@ -61,16 +60,16 @@ private ILogService CreateSeededLogService() { factory.SetupCreated(new Setup { - Matcher = new RequestMatcher {Path = "/path"} + Matcher = new RequestMatcher {Path = new Path("/path")} }), factory.SetupDeleted(new Setup { - Matcher = new RequestMatcher {Method = "POST"} + Matcher = new RequestMatcher {Method = new Method("POST")} }), factory.RequestUnmatched(context), factory.RequestMached(context, new Setup { - Matcher = new RequestMatcher {Method = "POST"} + Matcher = new RequestMatcher {Method = new Method("POST")} }) }); diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Services/SetupServiceTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Services/SetupServiceTests.cs index 7baca8bf..7f7e26ae 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Services/SetupServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Services/SetupServiceTests.cs @@ -67,7 +67,7 @@ public void TryGetMatchingSetup_Should_Return_True_If_Matching_Setup_In_Repo() { Matcher = new RequestMatcher { - Path = "/some/path" + Path = new Path("/some/path") } }; diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Types/InitializerTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Types/InitializerTests.cs index 35ef0a51..c58a7b61 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Types/InitializerTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Types/InitializerTests.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using Moq; using NinjaTools.FluentMockServer.API.Configuration; -using NinjaTools.FluentMockServer.API.Services; using NinjaTools.FluentMockServer.API.Types; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; diff --git a/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj b/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj index f9922d2b..4b30ab65 100644 --- a/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj @@ -19,7 +19,7 @@ - + diff --git a/test/NinjaTools.FluentMockServer.Tests/TestHelpers/XUnitTestBase.cs b/test/NinjaTools.FluentMockServer.Tests/TestHelpers/XUnitTestBase.cs index c737306f..28676e20 100644 --- a/test/NinjaTools.FluentMockServer.Tests/TestHelpers/XUnitTestBase.cs +++ b/test/NinjaTools.FluentMockServer.Tests/TestHelpers/XUnitTestBase.cs @@ -36,12 +36,17 @@ protected virtual string Dump([CanBeNull] T? value, [CanBeNull] string? heade { return str; } - + + if (value is null) + { + return $"{typeof(T).Name} is null!"; + } + return JToken.FromObject(v).ToString(Formatting.Indented); }; + var sb = new StringBuilder(); - sb.AppendLine(header is null ? $"--- Dump of '{typeof(T).Name}' ---" : $"--- {header} ---"); From 5b3fb1341ae884aee49b3a48671c095753a6574c Mon Sep 17 00:00:00 2001 From: alex held Date: Fri, 31 Jan 2020 20:25:46 +0100 Subject: [PATCH 43/64] chore: set nullable warnings only --- .../NinjaTools.FluentMockServer.API.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 9ed9dfbc..ed943778 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - Enable + warnings CS8600;CS8602;CS8603 Preview 1.0.0 From 7992bde29030c3ca23a6e17b193de63f86f35c3e Mon Sep 17 00:00:00 2001 From: alex held Date: Fri, 31 Jan 2020 20:32:42 +0100 Subject: [PATCH 44/64] chore: unset nullable warnings as error --- .../NinjaTools.FluentMockServer.API.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index ed943778..7c2f5523 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -3,7 +3,6 @@ netcoreapp3.1 warnings - CS8600;CS8602;CS8603 Preview 1.0.0 1.0.0 @@ -16,7 +15,7 @@ bin\Debug\NinjaTools.FluentMockServer.API.xml - NU1605;CS8600 + NU1605; From 964f447f2da80308ba8f7dccc090cf2597189b45 Mon Sep 17 00:00:00 2001 From: alex held Date: Fri, 31 Jan 2020 20:32:42 +0100 Subject: [PATCH 45/64] chore: unset nullable warnings as error --- .../NinjaTools.FluentMockServer.API.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index ed943778..7c2f5523 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -3,7 +3,6 @@ netcoreapp3.1 warnings - CS8600;CS8602;CS8603 Preview 1.0.0 1.0.0 @@ -16,7 +15,7 @@ bin\Debug\NinjaTools.FluentMockServer.API.xml - NU1605;CS8600 + NU1605; From 3b27ac27b650211b25e32b60a0265e18c75cd834 Mon Sep 17 00:00:00 2001 From: alex held Date: Fri, 31 Jan 2020 22:01:01 +0100 Subject: [PATCH 46/64] =?UTF-8?q?chore:=20enable=20nullable=20reference=20?= =?UTF-8?q?types=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NinjaTools.FluentMockServer.API.Tests.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj index 1125f969..9a6fa280 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj @@ -3,6 +3,8 @@ netcoreapp3.1 false + preview + enable From 0f81ae9986619f37ac0e87a19666f2db88e20465 Mon Sep 17 00:00:00 2001 From: alex held Date: Fri, 31 Jan 2020 22:02:06 +0100 Subject: [PATCH 47/64] chore: set target framework depending on Build-Configuration --- .../NinjaTools.FluentMockServer.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj index 2a52828a..a54f6c83 100644 --- a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj +++ b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj @@ -3,8 +3,8 @@ 36A37D14-D7FC-4E32-8F48-5FB606818757 1.1.0 - netstandard2.1;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1 8.0 + netcoreapp3.1; Enabled @@ -39,6 +39,7 @@ true + netstandard2.1;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1 bin\Release\HardCoded.MockServer.xml CS1591; From ef7f6f88b0cc0152739276bbe1cc2b2161a969d0 Mon Sep 17 00:00:00 2001 From: alex held Date: Fri, 31 Jan 2020 23:03:40 +0100 Subject: [PATCH 48/64] refactor(test): use collection fixture for mockserver tests --- .gitattributes | 3 +-- .../.idea/contentModel.xml | 20 +------------------ .../Models/Logging/LogItemTests.cs | 4 ++-- .../Xunit/ManualMockServerSetupTests.cs | 1 - .../Xunit/VerificationTests.cs | 14 ++++++++++++- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/.gitattributes b/.gitattributes index 7cc4aec1..c506dd7b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,5 @@ # Auto detect text files that will always have LF line endings on checkout. -* text=auto - +* text=auto eol=lf # Denote all files that are truly binary and should not be modified. *.png binary diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index 52c276c2..2d0945b1 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -16,6 +16,7 @@ + @@ -88,21 +89,9 @@ - - - - - - - - - - - - @@ -150,7 +139,6 @@ - @@ -225,7 +213,6 @@ - @@ -253,8 +240,6 @@ - - @@ -266,9 +251,6 @@ - - - diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs index 728e1168..4538cd34 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Models/Logging/LogItemTests.cs @@ -165,9 +165,9 @@ public void ToFormattedString_Should_Return_PrettyFormatted_String(ILogItem log, // Act var formatted = log.ToFormattedString(); - // Dump(expected, formatted); - + // Assert + Dump(expected, formatted); formatted.Should().Be(expected); } } diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs index 85b3f86e..f09e42d7 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs @@ -8,7 +8,6 @@ using Xunit; using Xunit.Abstractions; -[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] namespace NinjaTools.FluentMockServer.Tests.Xunit { public class ManualMockServerSetupTests : MockServerTestBase diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs index 6ee9ad37..a6aa1473 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs @@ -1,3 +1,4 @@ +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -47,7 +48,12 @@ public async Task VerifyAsync_Should_Return_False_When_MockServer_Recieved_No_Ma } } - public abstract class MockServerTestBase : XUnitTestBase, IClassFixture + [CollectionDefinition(nameof(MockServerCollectionFixture), DisableParallelization = true)] + public class MockServerCollectionFixture : ICollectionFixture + { } + + [Collection(nameof(MockServerCollectionFixture))] + public abstract class MockServerTestBase : XUnitTestBase, IDisposable { public MockServerFixture Fixture { get; } @@ -60,5 +66,11 @@ protected MockServerTestBase(MockServerFixture fixture, ITestOutputHelper output Fixture = fixture; Thread.Sleep(200); } + + /// + public void Dispose() + { + MockClient.ResetAsync().GetAwaiter().GetResult(); + } } } From 55d1909f37661eb48fef677ee14c21dc1d0622e6 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 25 Jan 2020 19:56:02 +0100 Subject: [PATCH 49/64] !refactor(client): remove unnecessary complexity --- .../Extensions/HttpContextExtensions.cs | 27 + .../NinjaTools.FluentMockServer.API.csproj | 7 +- .../Extensions/JsonExtensions.cs | 18 - .../Builders/FluentExpectationBuilder.cs | 18 +- .../Builders/FluentVerificationBuilder.cs | 1 - .../HttpEntities/FluentHttpRequestBuilder.cs | 7 +- .../ValueObjects/FluentHeaderBuilder.cs | 2 - .../FluentAPI/IFluentExpectationBuilder.cs | 1 - .../MockServerClient.cs | 67 +- .../MockServerSetup.cs | 1 - .../Models/Expectation.Equality.cs | 74 -- .../Models/Expectation.cs | 24 +- .../Models/HttpEntities/HttpError.cs | 32 +- .../Models/HttpEntities/HttpForward.cs | 51 -- .../Models/HttpEntities/HttpRequest.cs | 66 +- .../Models/HttpEntities/HttpResponse.cs | 59 +- .../Models/HttpEntities/HttpTemplate.cs | 44 -- .../Models/IIdentifiable.cs | 25 - .../Models/ValueTypes/Body.cs | 31 +- .../Models/ValueTypes/ConnectionOptions.cs | 35 +- .../Models/ValueTypes/Delay.cs | 28 +- .../Models/ValueTypes/LifeTime.cs | 31 +- .../Models/ValueTypes/Times.cs | 55 +- .../Models/ValueTypes/VerificationTimes.cs | 33 +- .../Models/Verify.cs | 32 +- .../NinjaTools.FluentMockServer.csproj | 6 +- .../Utils/IMockServerLogger.cs | 39 ++ ...njaTools.FluentMockServer.API.Tests.csproj | 1 - .../Builders/FluentExpectationBuilderTests.cs | 44 +- .../FluentVerificationBuilderTests.cs | 21 +- .../HttpEntities/FluentRequestBuilderTests.cs | 8 +- .../FluentResponseBuilderTests.cs | 645 +++--------------- .../Models/Equality/BodyEqualityTests.cs | 42 -- .../ConnectionOptionsEqualityTests.cs | 13 - .../Models/Equality/DelayEqualityTests.cs | 13 - .../Models/Equality/EqualityTestBase.cs | 150 ---- .../Equality/ExpectationEqualityTests.cs | 28 - .../Models/Equality/HttpErrorEqualityTests.cs | 13 - .../Equality/HttpForwardEqualityTests.cs | 13 - .../Equality/HttpRequestEqualityTests.cs | 13 - .../Equality/HttpResponseEqualityTests.cs | 13 - .../Equality/HttpTemplateEqualityTests.cs | 13 - .../Models/Equality/LifeTimeEqualityTests.cs | 13 - .../Models/Equality/TimesEqualityTests.cs | 13 - .../VerificationTimesEqualityTests.cs | 26 - .../Models/Equality/VerifyEqualityTests.cs | 13 - .../NinjaTools.FluentMockServer.Tests.csproj | 21 +- .../ContentSerializationTests.cs | 15 +- .../ContractResolverTests.cs | 3 +- .../ExpectationConverterTests.cs | 14 +- .../ExpectationSerializationTests.cs | 17 +- .../HttpResponseSerializationTests.cs | 11 +- .../TheoryData/EqualityOperatorTestData.cs | 10 +- .../Data/TheoryData/RandomHttpRequestData.cs | 10 +- .../Xunit/SoapServiceTests.cs | 14 +- 55 files changed, 359 insertions(+), 1665 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer.API/Extensions/HttpContextExtensions.cs delete mode 100644 src/NinjaTools.FluentMockServer/Extensions/JsonExtensions.cs delete mode 100644 src/NinjaTools.FluentMockServer/Models/Expectation.Equality.cs delete mode 100644 src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpForward.cs delete mode 100644 src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpTemplate.cs delete mode 100644 src/NinjaTools.FluentMockServer/Models/IIdentifiable.cs create mode 100644 src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/BodyEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/ConnectionOptionsEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/DelayEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/EqualityTestBase.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/ExpectationEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpErrorEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpForwardEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpRequestEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpResponseEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpTemplateEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/LifeTimeEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/TimesEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/VerificationTimesEqualityTests.cs delete mode 100644 test/NinjaTools.FluentMockServer.Tests/Models/Equality/VerifyEqualityTests.cs diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/HttpContextExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/HttpContextExtensions.cs new file mode 100644 index 00000000..f14425b5 --- /dev/null +++ b/src/NinjaTools.FluentMockServer.API/Extensions/HttpContextExtensions.cs @@ -0,0 +1,27 @@ +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json.Linq; + +namespace NinjaTools.FluentMockServer.API.Extensions +{ + public static class HttpContextExtensions + { + /// + /// Reads and deserializes the into . + /// + /// + /// The of which gets deserialized. + /// + /// The type to deserialize to + /// + public static async Task ReadAsync([NotNull] this HttpRequest httpRequest) + { + var reader = new StreamReader(httpRequest.Body); + var json = await reader.ReadToEndAsync(); + return JObject.Parse(json).ToObject(); + } + } +} diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 4f0f780e..44dbd691 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -10,7 +10,8 @@ - bin\Release\NinjaTools.FluentMockServer.API.xml + netstandard2.1;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1 + bin\Release\NinjaTools.FluentMockServer.API.xml @@ -27,6 +28,10 @@ + + + + diff --git a/src/NinjaTools.FluentMockServer/Extensions/JsonExtensions.cs b/src/NinjaTools.FluentMockServer/Extensions/JsonExtensions.cs deleted file mode 100644 index 2a03bd3e..00000000 --- a/src/NinjaTools.FluentMockServer/Extensions/JsonExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Newtonsoft.Json.Linq; -using NinjaTools.FluentMockServer.Serialization; - -namespace NinjaTools.FluentMockServer.Extensions -{ - public static class JsonExtensions - { - public static JObject AsJObject(this T obj) - { - return Serializer.SerializeJObject(obj); - } - - public static string AsJson(this object obj) - { - return Serializer.Serialize(obj); - } - } -} diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentExpectationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentExpectationBuilder.cs index 467a9f8b..f4a85cd3 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentExpectationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentExpectationBuilder.cs @@ -16,18 +16,12 @@ internal sealed class FluentExpectationBuilder : IFluentExpectationBuilder public static Models.Expectation Create( [CanBeNull] HttpRequest httpRequest = null, [CanBeNull] HttpResponse httpResponse = null, - [CanBeNull] HttpTemplate httpResponseTemplate = null, - [CanBeNull] HttpForward httpForward = null, - [CanBeNull] HttpTemplate httpForwardTemplate = null, [CanBeNull] HttpError httpError = null, [CanBeNull] Times times = null, - [CanBeNull] LifeTime timeToLive = null) => new Models.Expectation(httpRequest, httpResponse, httpResponseTemplate, httpForward, httpResponseTemplate, httpError, times, timeToLive); + [CanBeNull] LifeTime timeToLive = null) => new Models.Expectation(httpRequest, httpResponse, httpError, times, timeToLive); [CanBeNull] private HttpRequest HttpRequest { get; set; } [CanBeNull] private HttpResponse HttpResponse { get; set; } - [CanBeNull] private HttpTemplate HttpResponseTemplate { get; set; } - [CanBeNull] private HttpForward HttpForward { get; set; } - [CanBeNull] private HttpTemplate HttpForwardTemplate { get; set; } [CanBeNull] private HttpError HttpError { get; set; } [CanBeNull] private Times Times { get; set; } [CanBeNull] private LifeTime TimeToLive { get; set; } @@ -43,14 +37,6 @@ internal FluentExpectationBuilder(MockServerSetup setup) _setup = setup; } - /// - [NotNull] - public IBlankExpectation WithBaseUrl(string url) - { - _setup.BaseUrl = url; - return this; - } - /// [NotNull] public IWithRequest OnHandling([CanBeNull] HttpMethod method = null, Action requestFactory = null) @@ -155,7 +141,7 @@ public MockServerSetup Setup() [NotNull] public Models.Expectation BuildExpectation() { - return new Models.Expectation(HttpRequest, HttpResponse, HttpResponseTemplate, HttpForward, HttpForwardTemplate, HttpError, Times, TimeToLive); + return new Models.Expectation(HttpRequest, HttpResponse, HttpError, Times, TimeToLive); } diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs index ec6366d9..c8fd18cd 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs @@ -11,7 +11,6 @@ internal class FluentVerificationBuilder : IFluentVerificationBuilder, IWithVeri { private HttpRequest _httpRequest; private VerificationTimes _verificationTimes; - /// [NotNull] diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/HttpEntities/FluentHttpRequestBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/HttpEntities/FluentHttpRequestBuilder.cs index 99709da0..18946b84 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/HttpEntities/FluentHttpRequestBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/HttpEntities/FluentHttpRequestBuilder.cs @@ -17,10 +17,9 @@ internal sealed class FluentHttpRequestBuilder : IFluentHttpRequestBuilder [CanBeNull] private string _path; private bool? _secure; private bool? _keepAlive; - - // TODO: Add Builder Methods - // ReSharper disable once UnusedAutoPropertyAccessor.Local - [CanBeNull] private Dictionary Cookies { get; set; } + + [CanBeNull] + private Dictionary Cookies { get; set; } /// public IFluentHttpRequestBuilder WithMethod(HttpMethod method) diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/ValueObjects/FluentHeaderBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/ValueObjects/FluentHeaderBuilder.cs index 6d4b407f..1e7b7c86 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/ValueObjects/FluentHeaderBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/ValueObjects/FluentHeaderBuilder.cs @@ -20,8 +20,6 @@ public FluentHeaderBuilder([CanBeNull] Dictionary seed = null) } private HttpRequestHeaders RequestHeaders => _requestMessage.Headers; - // ReSharper disable once UnusedMember.Local - // TODO: Add Builder Methods private HttpResponseHeaders ResponseHeaders => _responseMessage.Headers; /// diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs index 5bcadcee..7e1f39a4 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs @@ -12,7 +12,6 @@ namespace NinjaTools.FluentMockServer.FluentAPI [EditorBrowsable(EditorBrowsableState.Never)] public interface IBlankExpectation : IFluentInterface { - IBlankExpectation WithBaseUrl(string url); IWithRequest OnHandling(HttpMethod method = null, [CanBeNull] Action requestFactory = null); IWithRequest OnHandlingAny([CanBeNull] HttpMethod method = null); } diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index 0cc96084..948b9c99 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -1,41 +1,63 @@ using System; +using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using JetBrains.Annotations; +using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI; using NinjaTools.FluentMockServer.FluentAPI.Builders; -using NinjaTools.FluentMockServer.Models; using NinjaTools.FluentMockServer.Models.ValueTypes; +using NinjaTools.FluentMockServer.Utils; +using NinjaTools.FluentMockServer.Xunit; using static NinjaTools.FluentMockServer.Utils.RequestFactory; namespace NinjaTools.FluentMockServer { + [DebuggerDisplay("Context={Context}; Host={HttpClient.BaseAddress.Host}")] public class MockServerClient : IDisposable { + private readonly IMockServerLogger _logger; + private readonly HttpClient _httpClient; + [NotNull] - public HttpClient HttpClient { get; private set; } + public HttpClient HttpClient + { + get + { + if (!string.IsNullOrWhiteSpace(Context)) + { + _httpClient.DefaultRequestHeaders.Add(HttpExtensions.MockContextHeaderKey, Context); + } + + return _httpClient; + } + } + + public string? Context { get; } - public MockServerClient(HttpClient client, string hostname = "http://localhost:9003") + public MockServerClient([NotNull] HttpClient client, [NotNull] string hostname, [NotNull] IMockServerLogger logger, string? context = null) { + _logger = logger; client.BaseAddress = new Uri(hostname); client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Host = null; - HttpClient = client; + _httpClient = client; + Context = context; } - - public MockServerClient(string mockServerEndpoint) : this(new HttpClient(),mockServerEndpoint) + + public MockServerClient([NotNull] string mockServerEndpoint, [NotNull] IMockServerLogger logger, string? context = null) : this(new HttpClient(), mockServerEndpoint, logger, context) { } - /// /// Configures the MockServer Client. /// /// /// - public Task SetupAsync(Func setupFactory) + [NotNull] + public Task SetupAsync([NotNull] Func setupFactory) { var builder = new FluentExpectationBuilder(new MockServerSetup()); var setup = setupFactory(builder); @@ -44,6 +66,20 @@ public Task SetupAsync(Func setupFac } + /// + /// Configures the MockServer Client. + /// + /// + /// + public Task SetupAsync(Action setupFactory) + { + var builder = new FluentExpectationBuilder(new MockServerSetup()); + setupFactory(builder); + var setup = builder.Setup(); + return SetupAsync(setup); + } + + /// /// Configures the MockServer Client using a predefined . /// @@ -58,14 +94,11 @@ public async Task SetupAsync(MockServerSetup setup) } } - public async Task SendAsync(HttpRequestMessage request) - { - var response = await HttpClient.SendAsync(request); - return response; - } + [ItemNotNull] public async Task ResetAsync() { + _logger.WriteLine("Resetting MockServer..."); var request = GetResetMessage(); var response = await HttpClient.SendAsync(request); response.EnsureSuccessStatusCode(); @@ -91,7 +124,8 @@ public async Task ResetAsync() } [NotNull] - public async Task Verify(Action verify) + [ItemNotNull] + public async Task Verify([NotNull] Action verify) { var builder = new FluentVerificationBuilder(); verify(builder); @@ -104,10 +138,7 @@ public async Task Verify(Action /// public void Dispose() { - HttpClient?.Dispose(); - // ReSharper disable once AssignNullToNotNullAttribute - HttpClient = null; + HttpClient.Dispose(); } - } } diff --git a/src/NinjaTools.FluentMockServer/MockServerSetup.cs b/src/NinjaTools.FluentMockServer/MockServerSetup.cs index ce8fd387..53e1bca6 100644 --- a/src/NinjaTools.FluentMockServer/MockServerSetup.cs +++ b/src/NinjaTools.FluentMockServer/MockServerSetup.cs @@ -6,6 +6,5 @@ namespace NinjaTools.FluentMockServer public class MockServerSetup { public List Expectations { get; } = new List(); - public string BaseUrl { get; set; } } } diff --git a/src/NinjaTools.FluentMockServer/Models/Expectation.Equality.cs b/src/NinjaTools.FluentMockServer/Models/Expectation.Equality.cs deleted file mode 100644 index f96e1c12..00000000 --- a/src/NinjaTools.FluentMockServer/Models/Expectation.Equality.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; - -// ReSharper disable NonReadonlyMemberInGetHashCode - -namespace NinjaTools.FluentMockServer.Models -{ - /// - /// Model to set up an Expectation on the MockServer. - /// - public partial class Expectation : IIdentifiable - { - /// - public int Id { get; set; } - - /// - public DateTime CreatedOn { get; set; } = DateTime.UtcNow; - - /// - public DateTime ModifiedOn{ get; set; } - - /// - [Timestamp] - public byte[] Timestamp { get; set; } - - - /// - public bool Equals(Expectation other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Id == other.Id && CreatedOn.Equals(other.CreatedOn) && ModifiedOn.Equals(other.ModifiedOn) && Equals(Timestamp, other.Timestamp) && Equals(HttpRequest, other.HttpRequest) && Equals(HttpResponse, other.HttpResponse) && Equals(HttpResponseTemplate, other.HttpResponseTemplate) && Equals(HttpForward, other.HttpForward) && Equals(HttpForwardTemplate, other.HttpForwardTemplate) && Equals(HttpError, other.HttpError) && Equals(Times, other.Times) && Equals(TimeToLive, other.TimeToLive); - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Expectation) obj); - } - - /// - public override int GetHashCode() - { - var hashCode = new HashCode(); - hashCode.Add(HttpRequest); - hashCode.Add(HttpResponse); - hashCode.Add(HttpResponseTemplate); - hashCode.Add(HttpForward); - hashCode.Add(HttpForwardTemplate); - hashCode.Add(HttpError); - hashCode.Add(Times); - hashCode.Add(TimeToLive); - hashCode.Add(Id); - hashCode.Add(CreatedOn); - hashCode.Add(ModifiedOn); - hashCode.Add(Timestamp); - return hashCode.ToHashCode(); - } - - public static bool operator ==([CanBeNull] Expectation left, [CanBeNull] Expectation right) - { - return Equals(left, right); - } - - public static bool operator !=([CanBeNull] Expectation left, [CanBeNull] Expectation right) - { - return !Equals(left, right); - } - } -} diff --git a/src/NinjaTools.FluentMockServer/Models/Expectation.cs b/src/NinjaTools.FluentMockServer/Models/Expectation.cs index 1e38af28..4203a40e 100644 --- a/src/NinjaTools.FluentMockServer/Models/Expectation.cs +++ b/src/NinjaTools.FluentMockServer/Models/Expectation.cs @@ -10,28 +10,21 @@ namespace NinjaTools.FluentMockServer.Models /// Model to set up an Expectation on the MockServer. /// [JsonConverter(typeof(ExpectationConverter))] - public partial class Expectation + public class Expectation { - private Expectation() - { - } - + [CanBeNull] + public string MockContext { get; set; } + [JsonConstructor] public Expectation( [CanBeNull] HttpRequest httpRequest, [CanBeNull] HttpResponse httpResponse, - [CanBeNull] HttpTemplate httpResponseTemplate, - [CanBeNull] HttpForward httpForward, - [CanBeNull] HttpTemplate httpForwardTemplate, [CanBeNull] HttpError httpError, [CanBeNull] Times times, [CanBeNull] LifeTime timeToLive) { HttpRequest = httpRequest; HttpResponse = httpResponse; - HttpResponseTemplate = httpResponseTemplate; - HttpForward = httpForward; - HttpForwardTemplate = httpForwardTemplate; HttpError = httpError; Times = times; TimeToLive = timeToLive; @@ -47,15 +40,6 @@ public Expectation( /// public HttpResponse HttpResponse { get; private set;} - public HttpTemplate HttpResponseTemplate { get; private set;} - - /// - /// The Target specification to forward the matched to. - /// - public HttpForward HttpForward { get; private set;} - - public HttpTemplate HttpForwardTemplate { get; private set;} - /// /// An to respond with in case the has been matched. /// diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs index 26a81eb0..eefa669a 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs @@ -6,7 +6,7 @@ namespace NinjaTools.FluentMockServer.Models.HttpEntities /// /// Model to configure an Error. /// - public class HttpError : IEquatable + public class HttpError { public HttpError(Delay delay, bool? dropConnection, string responseBytes) { @@ -29,35 +29,5 @@ public HttpError(Delay delay, bool? dropConnection, string responseBytes) /// Base64 encoded byte response. /// public string ResponseBytes { get; } - - - /// - public bool Equals(HttpError other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Equals(Delay, other.Delay) && DropConnection == other.DropConnection && ResponseBytes == other.ResponseBytes; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((HttpError) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = (Delay != null ? Delay.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ DropConnection.GetHashCode(); - hashCode = (hashCode * 397) ^ (ResponseBytes != null ? ResponseBytes.GetHashCode() : 0); - return hashCode; - } - } } } diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpForward.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpForward.cs deleted file mode 100644 index 5d09bf79..00000000 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpForward.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; - -namespace NinjaTools.FluentMockServer.Models.HttpEntities -{ - /// - /// Model to describe to which destination the to forward. - /// - public class HttpForward : IEquatable - { - public HttpForward(string host, int? port) - { - Host = host; - Port = port; - } - /// - /// Gets and sets the Hostname to forward to. - /// - public string Host { get; } - - /// - /// Gets and sets the Port to forward to. - /// - public int? Port { get; } - - /// - public bool Equals(HttpForward other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Host == other.Host && Port == other.Port; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((HttpForward) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - return ((Host != null ? Host.GetHashCode() : 0) * 397) ^ Port.GetHashCode(); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs index 4ba83811..cb9796d3 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs @@ -9,10 +9,9 @@ namespace NinjaTools.FluentMockServer.Models.HttpEntities /// /// Model to describe, which request should be matched. /// - public class HttpRequest : IEquatable + public class HttpRequest { - [UsedImplicitly] - private HttpRequest() + public HttpRequest() { } @@ -30,83 +29,36 @@ public HttpRequest(string method, Dictionary headers, Dictiona /// /// The to be matched. /// - public string Method { get; } + public string Method { get; set; } /// /// Header constraints that need to be fulfilled. /// - public Dictionary Headers { get; } + public Dictionary Headers { get; set; } /// /// Cookie constraints that need to be fulfilled. /// - public Dictionary Cookies { get; } + public Dictionary Cookies { get; set; } /// /// Body constraints that need be fulfilled. /// - public JToken Body { get; } + public JToken Body { get; set; } /// /// Constrains on the path /// - public string Path { get; } + public string Path { get; set; } /// /// Constraint on whether encryption is enabled for this request. /// - public bool? Secure { get; } + public bool? Secure { get; set; } /// /// Constraint on whether to keep the connection alive /// - public bool? KeepAlive { get; } - - [NotNull] - public static HttpRequest Create( - [CanBeNull] string method = null, - [CanBeNull] string path = null, - [CanBeNull] Body body = null, - [CanBeNull] Dictionary headers = null, - [CanBeNull] Dictionary cookies = null, - bool? secure = null, - bool? keepAlive = null) - { - return new HttpRequest(method, headers, cookies, body, path, secure, keepAlive); - } - - - /// - public bool Equals(HttpRequest other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Method == other.Method && Equals(Headers, other.Headers) && Equals(Cookies, other.Cookies) && Equals(Body, other.Body) && Path == other.Path && Secure == other.Secure && KeepAlive == other.KeepAlive; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((HttpRequest) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = (Method != null ? Method.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Headers != null ? Headers.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Cookies != null ? Cookies.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Body != null ? Body.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Path != null ? Path.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ Secure.GetHashCode(); - hashCode = (hashCode * 397) ^ KeepAlive.GetHashCode(); - return hashCode; - } - } + public bool? KeepAlive { get; set; } } } diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs index 1cd33c20..65cac3e2 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs @@ -13,8 +13,10 @@ namespace NinjaTools.FluentMockServer.Models.HttpEntities /// /// Model to describe how to respond to a matching . /// - public class HttpResponse : IEquatable + public class HttpResponse { + public HttpResponse() { } + public HttpResponse(int? statusCode, [CanBeNull] Delay delay, [CanBeNull] ConnectionOptions connectionOptions, [CanBeNull] JToken body, [CanBeNull] Dictionary headers) { StatusCode = statusCode; @@ -24,68 +26,23 @@ public HttpResponse(int? statusCode, [CanBeNull] Delay delay, [CanBeNull] Connec Headers = headers; } - [UsedImplicitly] - private protected HttpResponse() - { - } - /// /// The of the . /// - public int? StatusCode { get; protected set; } + public int? StatusCode { get; set; } /// /// A to wait until the is returned. /// - public Delay Delay { get; protected set; } + public Delay Delay { get; set; } /// /// Some switches regarding the HttpConnection. /// - public ConnectionOptions ConnectionOptions { get; protected set; } - - public JToken Body { get; protected set; } - - public Dictionary Headers { get; protected set; } - - /// - public bool Equals(HttpResponse other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return StatusCode == other.StatusCode && Equals(Delay, other.Delay) && Equals(ConnectionOptions, other.ConnectionOptions) && Equals(Body, other.Body) && Equals(Headers, other.Headers); - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((HttpResponse) obj); - } + public ConnectionOptions ConnectionOptions { get; set; } - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = StatusCode.GetHashCode(); - hashCode = (hashCode * 397) ^ (Delay != null ? Delay.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (ConnectionOptions != null ? ConnectionOptions.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Body != null ? Body.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Headers != null ? Headers.GetHashCode() : 0); - return hashCode; - } - } + public JToken Body { get; set; } - public static HttpResponse Create(int? statusCode = null, - [CanBeNull] Delay delay = null, - [CanBeNull] ConnectionOptions connectionOptions = null, - [CanBeNull] JToken body = null, - [CanBeNull] Dictionary headers = null) - { - return new HttpResponse(statusCode, delay, connectionOptions, body, headers); - } + public Dictionary Headers { get; set; } } } diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpTemplate.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpTemplate.cs deleted file mode 100644 index 055828b8..00000000 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpTemplate.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using NinjaTools.FluentMockServer.Models.ValueTypes; - -namespace NinjaTools.FluentMockServer.Models.HttpEntities -{ - public class HttpTemplate : IEquatable - { - public HttpTemplate(string template, Delay delay) - { - Template = template; - Delay = delay; - } - - public string Template { get; } - - public Delay Delay { get; } - - /// - public bool Equals(HttpTemplate other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Template == other.Template && Equals(Delay, other.Delay); - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((HttpTemplate) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - return ((Template != null ? Template.GetHashCode() : 0) * 397) ^ (Delay != null ? Delay.GetHashCode() : 0); - } - } - } -} diff --git a/src/NinjaTools.FluentMockServer/Models/IIdentifiable.cs b/src/NinjaTools.FluentMockServer/Models/IIdentifiable.cs deleted file mode 100644 index df456101..00000000 --- a/src/NinjaTools.FluentMockServer/Models/IIdentifiable.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using JetBrains.Annotations; -using Newtonsoft.Json; - -namespace NinjaTools.FluentMockServer.Models -{ - public interface IIdentifiable : IEquatable - { - [JsonIgnore, Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)] - int Id { get; } - - [JsonIgnore, Required, DatabaseGenerated(DatabaseGeneratedOption.Computed)] - DateTime CreatedOn { get; set; } - - [JsonIgnore, DatabaseGenerated(DatabaseGeneratedOption.Computed)] - DateTime ModifiedOn { get; set; } - - [JsonIgnore, Timestamp, CanBeNull] - byte[] Timestamp { get; set; } - - - } -} diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs index 9a4cdb32..43626d3c 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs @@ -6,37 +6,8 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes { - public partial class Body + public class Body : JObject { - /// - public override bool Equals(object obj) - { - if (!(obj is Body other)) - { - return false; - } - - if (!Children().Any() && !other.Children().Any()) - { - return true; - } - - var equals = JToken.DeepEquals(this, other); - return equals; - } - - /// - public override int GetHashCode() - { - var hashCode = new JTokenEqualityComparer().GetHashCode(this); - return hashCode; - } - } - [Serializable] - public partial class Body : JObject - { - - /// [JsonConverter(typeof(StringEnumConverter))] public enum BodyType diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs index 0ab28e85..670d1006 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs @@ -6,7 +6,7 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes /// /// Some options regarding a Connection. /// - public class ConnectionOptions : IEquatable + public class ConnectionOptions { public ConnectionOptions(bool? closeSocket, long? contentLengthHeaderOverride, bool? suppressContentLengthHeader, bool? suppressConnectionHeader, bool? keepAliveOverride) { @@ -28,7 +28,7 @@ public ConnectionOptions(bool? closeSocket, long? contentLengthHeaderOverride, b public long? ContentLengthHeaderOverride { get; } /// - /// Disables the ContentLengthHeadeer + /// Disables the ContentLengthHeader /// public bool? SuppressContentLengthHeader { get; } @@ -41,36 +41,5 @@ public ConnectionOptions(bool? closeSocket, long? contentLengthHeaderOverride, b /// Overrides the setting. /// public bool? KeepAliveOverride { get; } - - /// - public bool Equals(ConnectionOptions other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return CloseSocket == other.CloseSocket && ContentLengthHeaderOverride == other.ContentLengthHeaderOverride && SuppressContentLengthHeader == other.SuppressContentLengthHeader && SuppressConnectionHeader == other.SuppressConnectionHeader && KeepAliveOverride == other.KeepAliveOverride; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((ConnectionOptions) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = CloseSocket.GetHashCode(); - hashCode = (hashCode * 397) ^ ContentLengthHeaderOverride.GetHashCode(); - hashCode = (hashCode * 397) ^ SuppressContentLengthHeader.GetHashCode(); - hashCode = (hashCode * 397) ^ SuppressConnectionHeader.GetHashCode(); - hashCode = (hashCode * 397) ^ KeepAliveOverride.GetHashCode(); - return hashCode; - } - } } } diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs index 56836347..ed6bbb68 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs @@ -6,7 +6,7 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes /// /// Model to configure an optional before responding with an action to a matched . /// - public class Delay : IEquatable + public class Delay { public Delay(TimeUnit timeUnit, int value) { @@ -23,31 +23,5 @@ public Delay(TimeUnit timeUnit, int value) /// The value of the . /// public int Value { get; } - - /// - public bool Equals(Delay other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return TimeUnit == other.TimeUnit && Value == other.Value; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Delay) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - return ((int) TimeUnit * 397) ^ Value; - } - } } } diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs index d03133da..42b90365 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs @@ -2,7 +2,7 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes { - public class LifeTime : IEquatable + public class LifeTime { public LifeTime(int? timeToLive = null, TimeUnit timeUnit = ValueTypes.TimeUnit.Milliseconds) { @@ -21,34 +21,5 @@ public LifeTime(int? timeToLive = null, TimeUnit timeUnit = ValueTypes.TimeUnit. public TimeUnit? TimeUnit { get; } public int? TimeToLive { get; } public bool? Unlimited { get; } - - /// - public bool Equals(LifeTime other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return TimeUnit == other.TimeUnit && TimeToLive == other.TimeToLive && Unlimited == other.Unlimited; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((LifeTime) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = TimeUnit.GetHashCode(); - hashCode = (hashCode * 397) ^ TimeToLive.GetHashCode(); - hashCode = (hashCode * 397) ^ Unlimited.GetHashCode(); - return hashCode; - } - } } } diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Times.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Times.cs index fbc55334..c7e5597f 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Times.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Times.cs @@ -5,7 +5,7 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes { [Serializable] - public class Times : ICloneable, IEquatable + public class Times { [JsonConstructor] public Times(int remainingTimes) @@ -19,58 +19,5 @@ public Times(int remainingTimes) [NotNull] public static Times Once => new Times(1); [NotNull] public static Times Always => new Times(0); - - /// - public bool Equals(Times other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return RemainingTimes == other.RemainingTimes; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Times) obj); - } - - /// - public override int GetHashCode() - { - return RemainingTimes; - } - - /// - public object Clone() - { - return new Times(RemainingTimes); - } - - public static bool operator ==(Times left, Times right) - { - // Check for null on left side. - if (ReferenceEquals(left, null)) - { - if (ReferenceEquals(right, null)) - { - // null == null = true. - return true; - } - - // Only the left side is null. - return false; - } - - // Equals handles case of null on right side. - return left.Equals(right); - } - - public static bool operator !=([CanBeNull] Times left, [CanBeNull] Times right) - { - return !(left?.Equals(right) ?? false); - } } } diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs index 64b44b26..3c16a812 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs @@ -1,10 +1,10 @@ using System; +using JetBrains.Annotations; using Newtonsoft.Json; namespace NinjaTools.FluentMockServer.Models.ValueTypes { - [Serializable] - public class VerificationTimes : IEquatable + public class VerificationTimes { [JsonConstructor] public VerificationTimes(int? atLeast, int? atMost) @@ -13,7 +13,10 @@ public VerificationTimes(int? atLeast, int? atMost) AtMost = atMost; } + [NotNull] public static VerificationTimes Once => new VerificationTimes(1, 1); + + [NotNull] public static VerificationTimes Twice => new VerificationTimes(2, 2); public int? AtLeast { get; } @@ -33,31 +36,5 @@ public static VerificationTimes LessThan(int times) { return new VerificationTimes(0, times); } - - /// - public bool Equals(VerificationTimes other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return AtLeast == other.AtLeast && AtMost == other.AtMost; - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((VerificationTimes) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - return (AtLeast.GetHashCode() * 397) ^ AtMost.GetHashCode(); - } - } } } diff --git a/src/NinjaTools.FluentMockServer/Models/Verify.cs b/src/NinjaTools.FluentMockServer/Models/Verify.cs index 7c387f5b..68e98e19 100644 --- a/src/NinjaTools.FluentMockServer/Models/Verify.cs +++ b/src/NinjaTools.FluentMockServer/Models/Verify.cs @@ -7,7 +7,7 @@ namespace NinjaTools.FluentMockServer.Models /// /// Model used to describe what to verify. /// - public class Verify : IEquatable + public class Verify { public Verify(HttpRequest httpRequest, VerificationTimes times) { @@ -29,35 +29,5 @@ public static Verify Once(HttpRequest httpRequest = null) { return new Verify(httpRequest, VerificationTimes.Once); } - - /// - public bool Equals(Verify other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Equals(HttpRequest, other.HttpRequest) && Equals(Times, other.Times) && Equals(HttpRequest, other.HttpRequest) && Equals(Times, other.Times); - } - - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Verify) obj); - } - - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = (HttpRequest != null ? HttpRequest.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Times != null ? Times.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (HttpRequest != null ? HttpRequest.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Times != null ? Times.GetHashCode() : 0); - return hashCode; - } - } } } diff --git a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj index a54f6c83..f7cd93d6 100644 --- a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj +++ b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj @@ -1,11 +1,14 @@ - + + 36A37D14-D7FC-4E32-8F48-5FB606818757 1.1.0 + netcoreapp3.1 8.0 netcoreapp3.1; Enabled + enable @@ -48,7 +51,6 @@ - diff --git a/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs b/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs new file mode 100644 index 00000000..0a5f3051 --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs @@ -0,0 +1,39 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; + +namespace NinjaTools.FluentMockServer.Utils +{ + public interface IMockServerLogger + { + void WriteLine([JetBrains.Annotations.NotNull] string message, params object[] args); + void Error([JetBrains.Annotations.NotNull] string message, params object[] args); + } + + public class MockServerTestLogger : IMockServerLogger + { + /// + /// Gets the default . + /// + public static IMockServerLogger Instance => Factory.Value; + + private MockServerTestLogger(){} + private static readonly Lazy Factory; + static MockServerTestLogger() + { + Factory = new Lazy(() => new MockServerTestLogger()); + } + + /// + public void WriteLine(string message, [CanBeNull] params object[] args) + { + Console.WriteLine(message, args); + } + + /// + public void Error(string message, [CanBeNull] params object[] args) + { + Console.Error.WriteLine(message, args); + } + } +} diff --git a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj index 852adbd9..a02d0ff7 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.API.Tests/NinjaTools.FluentMockServer.API.Tests.csproj @@ -17,7 +17,6 @@ - diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs index 9c204f30..c66b8029 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs @@ -4,14 +4,18 @@ using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; +using Moq; using Newtonsoft.Json; using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI; using NinjaTools.FluentMockServer.FluentAPI.Builders; using NinjaTools.FluentMockServer.Models.ValueTypes; +using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Tests.TestHelpers.Mocks; +using NinjaTools.FluentMockServer.Utils; using Xunit; using Xunit.Abstractions; +using Times = NinjaTools.FluentMockServer.Models.ValueTypes.Times; namespace NinjaTools.FluentMockServer.Tests.FluentAPI.Builders { @@ -36,30 +40,14 @@ public void Should_Set_Times() // Act fac(builder); var expectation = builder.Setup().Expectations.First(); - var result = expectation.AsJson(); + var result = Serializer.Serialize(expectation); // Assert _outputHelper.WriteLine(result); result.Should() .MatchRegex(@"(?m)\s*""times"":\s*\{\s*""remainingTimes"":\s*1,\s*""unlimited"":\s*false\s*}"); } - - [Fact] - public void Should_Set_BaseUrl() - { - // Arrange - const string baseUrl = "http://example.com"; - var builder = new FluentExpectationBuilder(); - builder.WithBaseUrl(baseUrl); - - // Act - var setup = builder.Setup(); - - // Assert - _outputHelper.WriteLine(JsonConvert.SerializeObject(setup, Formatting.Indented)); - setup.BaseUrl.Should().Be(baseUrl); - } - + [Fact] public void Should_Set_Times_Always() { @@ -70,7 +58,7 @@ public void Should_Set_Times_Always() .Setup(); // Act - var result = setup .Expectations.First().AsJson(); + var result = Serializer.Serialize(setup.Expectations.First()); // Assert _outputHelper.WriteLine(result); @@ -88,7 +76,8 @@ public void Should_Set_Times_Limited(int times) var builder = new FluentExpectationBuilder(); // Act - var result = builder.RespondTimes(times, 200).Setup().Expectations.First().AsJson(); + var result = Serializer.Serialize((builder + .RespondTimes(times, 200).Setup().Expectations.First())); // Assert _outputHelper.WriteLine(result); @@ -104,14 +93,13 @@ public void Should_Set_TimeToLive() var builder = new FluentExpectationBuilder(); // Act - var result = builder + var result = Serializer.Serialize(builder .OnHandlingAny() .RespondWith(HttpStatusCode.OK) .WhichIsValidFor(10) .Setup() - .Expectations.First() - .AsJson(); - + .Expectations.First()); + // Assert _outputHelper.WriteLine(result); result.Should().MatchRegex(@"(?m)\s*""timeToLive"":\s*\{\s*""timeUnit"":\s*""SECONDS""\s*,\s*""timeToLive"":\s*10\s*,\s*""unlimited""\s*:\s*false\s*}"); @@ -129,7 +117,7 @@ public void Should_Match_Any_Request() // Act var expectation = builder.Setup().Expectations.First(); - var result = expectation.AsJson(); + var result = Serializer.Serialize(expectation); // Assert _outputHelper.WriteLine(result); @@ -148,7 +136,7 @@ public void Should_Match_Any_Request_With_HttpMethod() // Act var expectation = builder.Setup().Expectations.First(); - var result = expectation.AsJson(); + var result = Serializer.Serialize(expectation); // Assert _outputHelper.WriteLine(result); @@ -168,7 +156,7 @@ public void Should_Match_Any_Request_With_Method(string method) builder.OnHandling(new HttpMethod(method)) .RespondWith(HttpStatusCode.Created); var expectation = builder.Setup().Expectations.First(); - var result = expectation.AsJson(); + var result = Serializer.Serialize(expectation); // Assert _outputHelper.WriteLine(result); @@ -180,7 +168,7 @@ public async Task Should_Build_Expectation() { // Arrange var handler = new MockHandler(_outputHelper); - var mockServerClient = new MockServerClient(new HttpClient(handler)); + var mockServerClient = new MockServerClient(new HttpClient(handler),"http://localhost:9000" , Mock.Of()); // Act await mockServerClient.SetupAsync( diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentVerificationBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentVerificationBuilderTests.cs index d03e10d9..02efb4d5 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentVerificationBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentVerificationBuilderTests.cs @@ -3,6 +3,7 @@ using Newtonsoft.Json.Linq; using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI.Builders; +using NinjaTools.FluentMockServer.Serialization; using Xunit; using Xunit.Abstractions; @@ -22,19 +23,19 @@ public class FluentVerificationBuilderTests public void Should_Verify_HttpRequest() { // Arrange - var expected = new JObject + var expected = Serializer.Serialize(new JObject { ["httpRequest"] = new JObject { ["path"] = "/some/path" } - }.AsJson(); + }); var builder = new FluentVerificationBuilder(); // Act builder.Verify(request => request.WithPath("some/path")); - var result = builder.Build().AsJson(); + var result = Serializer.Serialize(builder.Build()); // Assert _outputHelper.WriteLine(result); @@ -63,7 +64,7 @@ public void Should_Verify_Once() // Act builder.Verify(request => request.WithPath("some/path")).Once(); - var result = builder.Build().AsJson(); + var result = Serializer.Serialize(builder.Build()); // Assert _outputHelper.WriteLine(result); @@ -92,7 +93,7 @@ public void Should_Verify_Twice() // Act builder.Verify(request => request.WithPath("some/path")).Twice(); - var result = builder.Build().AsJson(); + var result = Serializer.Serialize(builder.Build()); // Assert _outputHelper.WriteLine(result); @@ -117,7 +118,7 @@ public void Should_Verify_Between() // Act builder.Verify(request => request.WithPath("some/path")).Between(1, 2); - var result = builder.Build().AsJson(); + var result = Serializer.Serialize(builder.Build()); // Assert _outputHelper.WriteLine(result); @@ -144,7 +145,7 @@ public void Should_Verify_AtMost() // Act builder.Verify(request => request.WithPath("some/path")).AtMost(5); - var result = builder.Build().AsJson(); + var result = Serializer.Serialize(builder.Build()); // Assert _outputHelper.WriteLine(result); @@ -154,7 +155,7 @@ public void Should_Verify_AtMost() [Fact] public void Should_Verify_AtLeast() { - var expected = new JObject + var expected = Serializer.Serialize(new JObject { ["httpRequest"] = new JObject { @@ -164,12 +165,12 @@ public void Should_Verify_AtLeast() { ["atLeast"] = 5 } - }.AsJson(); + }); var builder = new FluentVerificationBuilder(); // Act builder.Verify(request => request.WithPath("some/path")).AtLeast(5); - var result = builder.Build().AsJson(); + var result = Serializer.Serialize(builder.Build()); // Assert _outputHelper.WriteLine(result); diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentRequestBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentRequestBuilderTests.cs index f4f732d5..0a7febfe 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentRequestBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentRequestBuilderTests.cs @@ -22,7 +22,7 @@ public void Should_Use_Encryption() var builder = new FluentHttpRequestBuilder(); // Act - var result = builder.EnableEncryption().Build().AsJson(); + var result = Serializer.Serialize(builder.EnableEncryption().Build()); Logger.LogResult("JSON", result); // Assert @@ -41,7 +41,7 @@ public void Should_Set_Content_Type_Header() .AddContentType(CommonContentType.Soap12) .Build(); - Logger.LogResult("JSON ", result.AsJson()); + Logger.LogResult("JSON ", Serializer.Serialize(result)); // Assert // TODO:reactive @@ -55,7 +55,7 @@ public void Should_Keep_Alive() var builder = new FluentHttpRequestBuilder(); // Act - var result = builder.KeepConnectionAlive().Build().AsJson(); + var result = Serializer.Serialize(builder.KeepConnectionAlive().Build()); Logger.LogResult("JSON", result); // Assert @@ -72,7 +72,7 @@ public void Should_Set_Path(string inputPath, string expectedPath) var builder = new FluentHttpRequestBuilder(); // Act - var result = builder.WithPath(inputPath).Build().AsJson(); + var result = Serializer.Serialize(builder.WithPath(inputPath).Build()); Logger.LogResult("JSON", result); // Assert diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentResponseBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentResponseBuilderTests.cs index 220edc2f..e0f64a18 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentResponseBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentResponseBuilderTests.cs @@ -1,542 +1,103 @@ -//using System; -//using System.Collections.Generic; -//using System.Net; -//using System.Text; -//using System.Text.RegularExpressions; -//using Divergic.Logging.Xunit; -//using FluentAssertions; -//using FluentAssertions.Execution; -//using FluentAssertions.Formatting; -//using FluentAssertions.Json; -//using FluentAssertions.Primitives; -//using Microsoft.Extensions.Logging; -//using Microsoft.Extensions.Logging.Abstractions; -//using Newtonsoft.Json; -//using Newtonsoft.Json.Linq; -//using Newtonsoft.Json.Schema; -//using NinjaTools.FluentMockServer.Builders.Response; -//using NinjaTools.FluentMockServer.Domain.Models.HttpEntities; -//using NinjaTools.FluentMockServer.Domain.Models.ValueTypes; -//using Xunit; -//using Xunit.Abstractions; -//using static NinjaTools.FluentMockServer.Tests.Test.FileSystem; -// -//namespace NinjaTools.FluentMockServer.Tests.Builders -//{ -// -// /// -// /// The -// /// interface defines the members for formatting log messages. -// /// -// public class TestOutputFormatter : ILogFormatter -// { -// private TestOutputFormatter() -// { -// } -// -// private static readonly Lazy _lazy = new Lazy(() => new TestOutputFormatter()); -// public static TestOutputFormatter Instance = _lazy.Value; -// -// -// /// -// public string Format(int scopeLevel, string name, LogLevel logLevel, EventId eventId, string message, Exception exception) -// { -// var builder = new StringBuilder(); -// -// if (scopeLevel > 0) -// { -// builder.Append(' ', scopeLevel * 2); -// } -// -// if (logLevel != LogLevel.Debug && logLevel != LogLevel.Trace && logLevel != LogLevel.Information ) -// { -// builder.Append($"{logLevel} "); -// -// if (!string.IsNullOrEmpty(name)) -// { -// builder.Append($"{name} "); -// } -// -// if (eventId.Id != 0) -// { -// builder.Append($"[{eventId.Id}]: "); -// } -// } -// -// if (!string.IsNullOrEmpty(message)) -// { -// builder.Append("\n" + message); -// } -// -// if (exception != null) -// { -// builder.Append($"\n{exception}"); -// } -// -// return builder.ToString(); -// } -// } -// -// -// -// public static class BuildableBaseExtensions -// { -// public static HttpResponseAssertions Should(this HttpResponse response, ILogger logger = null) -// { -// return new HttpResponseAssertions(response, logger); -// } -// -// public static HttpRequestAssertions Should(this HttpRequest response, ILogger logger = null) -// { -// return new HttpRequestAssertions(response, logger); -// } -// } -// -// public class HttpRequestAssertions : BuildableBaseAssertions -// { -// public HttpRequestAssertions(HttpRequest subject, ILogger logger = null) : base(subject, logger) -// { -// } -// } -// -// -// public class HttpResponseAssertions : BuildableBaseAssertions -// { -// -// /// -// public HttpResponseAssertions(HttpResponse subject, ILogger logger = null) : base(subject, logger) -// { -// } -// -// -// -// public AndWhichConstraint ContainsBody(JToken body, string because = null, params object[] becauseArgs) -// { -// var jObject = Subject.SerializeJObject(); -// -// Execute.Assertion -// .ForCondition(!(jObject is null)) -// .UsingLineBreaks -// .BecauseOf(because, becauseArgs) -// .FailWith("Expected {context:JObject} to contain body {0}{reason}, but it was .", body.ToString(Formatting.Indented)); -// -// var bodyValue = jObject?.SelectToken("body")?.Value(); -// -// if (bodyValue is null) -// { -// Execute.Assertion -// .UsingLineBreaks -// .BecauseOf(because, becauseArgs) -// .FailWith("Expected {{context:JObject}} to contain body {0}{reason}, but is was .", body.ToString(Formatting.Indented)); -// } -// -// -// var jTokenAssertions = new JTokenAssertions(bodyValue); -// var andWhich = new AndWhichConstraint(this, jTokenAssertions.BeEquivalentTo(body).And); -// return andWhich; -// } -// } -// -// public class BuildableBaseAssertions : ReferenceTypeAssertions> where TBuildable : BuildableBase, -// { -// private readonly ILogger _logger; -// -// /// -// protected override string Identifier => typeof(TBuildable).Name; -// -// protected bool LoggedJson { get; set; } -// protected bool LoggedExpectedJObject { get; set; } -// protected bool LoggedActualJObject { get; set; } -// -// static BuildableBaseAssertions() -// { -// Formatter.AddFormatter(new JTokenFormatter()); -// } -// -// public BuildableBaseAssertions(TBuildable subject, ILogger logger = null) -// { -// _logger = logger ?? LogFactory.Create("Default"); -// Subject = subject; -// } -// -// private void Log(JToken expected = null) -// { -// -// try -// { -// if (Subject != null) -// { -// if (!LoggedJson) -// { -// _logger.LogResult("Serialize()", Subject?.Serialize() ?? ""); -// LoggedJson = true; -// } -// -// if (!LoggedActualJObject) -// { -// _logger.LogResult("SerializeJObject()", Subject?.SerializeJObject()?.ToString(Formatting.Indented) ?? ""); -// LoggedActualJObject = true; -// } -// } -// -// if (expected != null) -// { -// if (!LoggedExpectedJObject) -// { -// _logger.LogExpected("SerializeJObject()",expected?.ToString(Formatting.Indented) ?? ""); -// LoggedExpectedJObject = true; -// } -// } -// } -// catch (Exception e) -// { -// // ignore -// } -// } -// -// public AndConstraint> BeValid(JSchema schema, string because = null, params object[] becauseArgs) -// { -// Log(); -// _logger.LogExpected("Schema", schema.ToString(SchemaVersion.Draft7)); -// var jObject = Subject?.SerializeJObject(); -// -// Execute.Assertion -// .ForCondition(!(jObject is null)) -// .UsingLineBreaks -// .BecauseOf(because, becauseArgs) -// .FailWith("Expected {context:string} to match JsonSchema {0}{reason}, but it was .", schema.ToString(SchemaVersion.Draft7)); -// -// -// -// if (!jObject.IsValid(schema, out IList errorMessages)) -// { -// -// var errorMessage = $"JsonSchema Validation Error!\n\n{string.Join('\n', errorMessages)}"; -// _logger.LogCritical(errorMessage); -// -// Execute.Assertion -// .UsingLineBreaks -// .BecauseOf(because, becauseArgs) -// .AddPreFormattedFailure(errorMessage); -// } -// -// -// var and = new AndConstraint>(this); -// return and; -// } -// -// -// public AndConstraint> MatchingRegex(string regularExpression, string because = null, params object[] becauseArgs) -// { -// Log(); -// -// var json = Subject?.ToString() -// -// Execute.Assertion -// .ForCondition(!(json is null)) -// .UsingLineBreaks -// .BecauseOf(because, becauseArgs) -// .FailWith("Expected {context:string} to match regex {0}{reason}, but it was .", regularExpression); -// -// bool isMatch = false; -// try -// { -// isMatch = Regex.IsMatch(json, regularExpression); -// } -// catch (ArgumentException) -// { -// Execute.Assertion -// .FailWith("Cannot match {context:string} against {0} because it is not a valid regular expression.", regularExpression); -// } -// -// Execute.Assertion -// .ForCondition(isMatch) -// .BecauseOf(because, becauseArgs) -// .UsingLineBreaks -// .FailWith("Expected {context:string} to match regex {0}{reason}, but {1} does not match.", regularExpression, json); -// -// -// return new AndConstraint>(this); -// } -// -// public AndWhichConstraint, JTokenAssertions> HasValue(string jsonPath, JToken expected, string because = null, params object[] becauseArgs) -// { -// Log(expected); -// -// var jObject = Subject?.SerializeJObject(); -// -// Execute.Assertion -// .ForCondition(!(jObject is null)) -// .UsingLineBreaks -// .BecauseOf(because, becauseArgs) -// .FailWith("Expected {context:string} to contain JToken {0}{reason} at Path {1}, but it was .", expected, jsonPath); -// -// var jTokenAtPath = jObject?.SelectToken(jsonPath); -// -// if (jTokenAtPath is null) -// { -// Execute.Assertion -// .UsingLineBreaks -// .BecauseOf(because, becauseArgs) -// .FailWith("Expected {context:JObject} to contain JToken at JsonPath {0}, but it does not.", jsonPath); -// } -// -// var andJTokenAssertions = new JTokenAssertions(jTokenAtPath) -// .BeEquivalentTo(expected, because, becauseArgs); -// -// var andWhich = new AndWhichConstraint, JTokenAssertions>(this, andJTokenAssertions.And); -// -// return andWhich; -// } -// -// -// private AndConstraint Contains(JTokenAssertions subject, string path, TValue expected) where TValue : JToken -// { -// Log(expected); -// -// return subject.Subject -// .SelectToken(path) -// .Should().BeAssignableTo() -// .And.Subject -// .Should().BeEquivalentTo(expected); -// } -// } -// -// public static class LogFactory -// { -// private static readonly Lazy _loggerFactory = new Lazy(() => _loggerFactoryFactory()); -// private static Func _loggerFactoryFactory = () => new NullLoggerFactory(); -// -// -// public static T Dump(this T instance) where T : class -// { -// var factory = GetLoggerFactory(); -// var log = factory.CreateLogger("Default"); -// var json = JsonConvert.SerializeObject(instance, Formatting.Indented); -// log.LogWarning(json); -// return instance; -// } -// -// public static void LogExpected(this ILogger logger, string topic, string message, params object[] args) -// { -// var factory = GetLoggerFactory(); -// var log = factory.CreateLogger("Expected"); -// var sb = new StringBuilder($"=== {topic} ==="); -// sb.AppendLine(message); -// log.Log(LogLevel.Information, sb.ToString(), args); -// } -// -// public static void LogResult(this ILogger logger, string topic, string message, params object[] args) -// { -// var factory = GetLoggerFactory(); -// var log = factory.CreateLogger("Result"); -// var sb = new StringBuilder($"=== {topic} ==="); -// sb.AppendLine(message); -// log.Log(LogLevel.Information, sb.ToString(), args); -// } -// -// private class Config : LoggingConfig -// { -// public static Config Instance => new Config(); -// -// public Config() -// { -// Formatter = TestOutputFormatter.Instance; -// } -// } -// -// public static ILogger Create() -// { -// var loggerFactory = GetLoggerFactory(); -// return loggerFactory.CreateLogger(); -// } -// -// public static ILogger Create(string name) -// { -// var loggerFactory = GetLoggerFactory(); -// return loggerFactory.CreateLogger(string.IsNullOrWhiteSpace(name) ? "Default" : name); -// } -// -// private static ILoggerFactory GetLoggerFactory(ITestOutputHelper output = null) -// { -// if (_loggerFactory.IsValueCreated) -// { -// return _loggerFactory.Value; -// } -// -// if (output is null) -// { -// return new NullLoggerFactory(); -// } -// -// _loggerFactoryFactory = () => LoggerFactory.Create(builder => builder.AddXunit(output, Config.Instance)); -// return _loggerFactory.Value; -// } -// -// public static ILogger AsLogger(this ITestOutputHelper testOutputHelper) -// { -// var factory = GetLoggerFactory(testOutputHelper); -// return factory.CreateLogger("Default"); -// } -// -// public static ILogger AsLogger(this ITestOutputHelper testOutputHelper) -// { -// var factory = GetLoggerFactory(testOutputHelper); -// return factory.CreateLogger(); -// } -// } -// -// -// public class FluentResponseBuilderTests -// { -// private readonly ILogger _logger; -// -// public FluentResponseBuilderTests(ITestOutputHelper outputHelper) -// { -// _logger = outputHelper.AsLogger(); -// } -// -// -// [Fact] -// public void Should_Add_Header() -// { -// // Arrange -// var builder = new FluentHttpResponseBuilder(); -// -// // Act -// var response = builder -// .ConfigureHeaders(opt => opt -// .AddContentType("text/xml charset=UTF-8;")) -// .Build() -// .ToString() -// -// // Assert -// response.Should().MatchRegex(@"(?mis){.*""Content-Type"":.*\[.*""text/xml charset=UTF-8;"".*\].*}.*"); -// -// } -// -// [Fact] -// public void Should_Add_Body_Literal() -// { -// // Arrange -// var builder = new FluentHttpResponseBuilder(); -// -// // Act -// var response = builder -// .WithBody("Hello World!") -// .Build() -// .SerializeJObject(); -// -// // Assert -// response.Value("body").Should().Be("Hello World!"); -// } -// -// -// -// [Fact] -// public void WithLiteralResponse_Creates_LiteralBody() -// { -// // Arrange -// var builder = new FluentHttpResponseBuilder(); -// -// // Act -// var response = builder -// .WithBody("Hello World!") -// .Build() -// .SerializeJObject(); -// -// // Assert -// response.Value("body") -// .Should().Be("Hello World!"); -// } -// -// -// -// -// -// [Fact] -// public void Build_With_Complex_Setup_Matches_JsonSchema1() -// { -// // Arrange -// var builder = new FluentHttpResponseBuilder(); -// const string contentType = "xml/plain; charset=utf-8"; -// var schema = Load("FluentResponseBuilder_Schema1"); -// var responseBody = LoadXml("WithOrderId"); -// -// // Act -// var response = builder -// .WithStatusCode(HttpStatusCode.OK) -// .WithDelay(50, TimeUnit.Milliseconds) -// .AddContentType(contentType) -// .WithBody(responseBody) -// .Build() -// .SerializeJObject(); -// -// -// // Assert -// response.SelectToken("statusCode").Value().Should().Be(200); -// response.Value("body").Should().BeEquivalentTo(responseBody); -// response.Value("delay").Should().BeEquivalentTo(new JObject -// { -// ["timeUnit"] = TimeUnit.Milliseconds.ToString().ToUpper(), -// ["value"] = 50 -// }); -// response.Value("headers").Value("Content-Type")[0].Value().Should().Be(contentType); -// -// var sb = new StringBuilder("ValidationErrors:\n"); -// -// if (!response.IsValid(schema, out IList errors)) -// { -// int i = 0; -// foreach (var error in errors) -// { -// sb.AppendLine($"Error [{i}]"); -// sb.AppendLine(error); -// sb.AppendLine(); -// sb.AppendLine(); -// } -// } -// -// _logger.LogInformation(sb.ToString()); -// -//// response.Should().val -//// .ContainsBody(new JProperty("body", responseBody)) -//// .And.BeValid(schema) -//// .And.Match(r => r.StatusCode == 200) -//// .And.HasValue("statusCode", new JValue(200)) -//// .And.HasValue("delay", new JObject -//// { -//// ["timeUnit"] = TimeUnit.Milliseconds.ToString().ToUpper(), -//// ["value"] = 50 -//// }) -//// .And.HasValue("headers", new JObject -//// { -//// ["Content-Type"] = new JArray(contentType) -//// }); -// } -// -// -// -// [Fact] -// public void Should_Add_Multiple_Header_Values() -// { -// // Arrange -// IFluentHttpResponseBuilder builder = new FluentHttpResponseBuilder(); -// -// // Act -// var response = builder -// .AddContentType("text/xml charset=UTF-8;") -// .WithHeader("Header-name 2", "true") -// .Build() -// .SerializeJObject(); -// -// -// _logger.LogResult("JSON", response.ToString(Formatting.Indented)); -// -// -// // Assert -// response["headers"]["Content-Type"][0].Value().Should().Be("text/xml charset=UTF-8;"); -// response["headers"]["Header-name 2"][0].Value().Should().Be("true"); -// } -// } -//} +using System.Collections.Generic; +using System.Net; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Text; +using FluentAssertions; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using NinjaTools.FluentMockServer.FluentAPI; +using NinjaTools.FluentMockServer.FluentAPI.Builders.HttpEntities; +using NinjaTools.FluentMockServer.Models.ValueTypes; +using NinjaTools.FluentMockServer.Serialization; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using Xunit; +using Xunit.Abstractions; +using static NinjaTools.FluentMockServer.Tests.TestHelpers.FileSystem; + +namespace NinjaTools.FluentMockServer.Tests.FluentAPI.Builders.HttpEntities +{ + public class FluentResponseBuilderTests : XUnitTestBase + { + + /// + public FluentResponseBuilderTests(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Should_Add_Header() + { + // Arrange + var builder = new FluentHttpResponseBuilder(); + + // Act + var response = builder + .ConfigureHeaders(opt => opt + .AddContentType("text/xml charset=UTF-8;")) + .Build(); + + // Assert + response.Headers + .GetValueOrDefault("Content-Type") + .Should().ContainSingle("text/xml charset=UTF-8;") + .And.HaveCount(1); + } + + [Fact] + public void Should_Add_Body_Literal() + { + // Arrange + var builder = new FluentHttpResponseBuilder(); + + // Act + var response = Serializer.SerializeJObject(builder + .WithBody("Hello World!") + .Build()); + + // Assert + response.Value("body").Should().Be("Hello World!"); + } + + + + [Fact] + public void WithLiteralResponse_Creates_LiteralBody() + { + // Arrange + var builder = new FluentHttpResponseBuilder(); + + // Act + var response = Serializer.SerializeJObject(builder + .WithBody("Hello World!") + .Build()); + + // Assert + response.Value("body").Should().Be("Hello World!"); + } + + [Fact] + public void Should_Add_Multiple_Header_Values() + { + // Arrange + IFluentHttpResponseBuilder builder = new FluentHttpResponseBuilder(); + + // Act + var response = Serializer.SerializeJObject(builder + .AddContentType("text/xml charset=UTF-8;") + .WithHeader("Header-name 2", "true") + .Build()); + + + Output.WriteLine("JSON", response.ToString(Formatting.Indented)); + + + // Assert + response["headers"]["Content-Type"][0].Value().Should().Be("text/xml charset=UTF-8;"); + response["headers"]["Header-name 2"][0].Value().Should().Be("true"); + } + + + } +} diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/BodyEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/BodyEqualityTests.cs deleted file mode 100644 index b3340a46..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/BodyEqualityTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using NinjaTools.FluentMockServer.FluentAPI; -using NinjaTools.FluentMockServer.FluentAPI.Builders.ValueObjects; -using NinjaTools.FluentMockServer.Models.ValueTypes; -using Xunit; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class BodyEqualityTests : EqualityTestBase - { - static BodyEqualityTests() - { - TestDataOverride = new List() - { - new object[] {Create(b => b.ContainingJson("some json value")), Create(b => b.NotContainingJson("not containing this string"))}, - new object[] {new Body(), Create(b => b.WithXmlContent("xml content"))}, - new[] {Create(b => b.WithXmlContent("xml content")), (object) null}, - new object[] {Create(b => b.MatchingXmlSchema("xml schema")), new FactAttribute()} - }; - } - - private static Body Create(Action factory) - { - var type = typeof(FluentBodyBuilder); - var builder = new FluentBodyBuilder(); - factory(builder); - - var bodyProperty = type.GetProperty("Body", BindingFlags.NonPublic | BindingFlags.Instance); - var body = bodyProperty.GetValue(builder) as Body; - - return body; - } - - /// - public BodyEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/ConnectionOptionsEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/ConnectionOptionsEqualityTests.cs deleted file mode 100644 index 23eaecb5..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/ConnectionOptionsEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models.ValueTypes; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class ConnectionOptionsEqualityTests : EqualityTestBase - { - /// - public ConnectionOptionsEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/DelayEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/DelayEqualityTests.cs deleted file mode 100644 index 1bbd0a63..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/DelayEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models.ValueTypes; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class DelayEqualityTests : EqualityTestBase - { - /// - public DelayEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/EqualityTestBase.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/EqualityTestBase.cs deleted file mode 100644 index d2549ed8..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/EqualityTestBase.cs +++ /dev/null @@ -1,150 +0,0 @@ -#nullable enable -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using FluentAssertions; -using Force.DeepCloner; -using JetBrains.Annotations; -using Newtonsoft.Json; -using NinjaTools.FluentMockServer.Tests.TestHelpers; -using NinjaTools.FluentMockServer.Tests.TestHelpers.Data.Random; -using NinjaTools.FluentMockServer.Tests.TestHelpers.Data.TheoryData; -using Xunit; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public abstract class EqualityTestBase : XUnitTestBase where T : class - { - // ReSharper disable once StaticMemberInGenericType - protected static List TestDataOverride { get; set; } - protected EqualityTestBase(ITestOutputHelper outputHelper) : base(outputHelper) - { } - - [Theory] - [MemberData(nameof(GetEqualityOperatorTestData))] - public void Equals_Should_Compare_For_Equality_By_Properties([NotNull] T a, [NotNull] T b, bool areEqual) - { - // Assert - switch (areEqual) - { - case true: - a.Should().Be(b); - break; - default: - a.Should().NotBe(b); - break; - } - } - - [Fact] - public void Equals_Should_Be_False_When_Instances_Have_Different_Types() - { - // Arrange - var a = InstanceFactoryCreator.CreateDefault(); - var b = new Regex(""); - - // Assert - a.Equals(b).Should().BeFalse(); - } - - [Fact] - public void Equals_Should_Be_True_When_Instances_Are_Not_The_Same_Reference_But_Have_Same_Properties() - { - // Arrange - var a = InstanceFactoryCreator.CreateDefault(); - var b = a.DeepClone(); - - // Assert - a.Equals(b).Should().BeTrue(); - } - - - [Theory] - [MemberData(nameof(GetTestData))] - public void Equals_Should_Return_False_When_Instances_Are_Not_Equal([NotNull] T a, [CanBeNull] object? b) - { - Output.WriteLine($"{nameof(a)}: {JsonConvert.SerializeObject(a, Formatting.Indented)}; Type={typeof(T).Name};\n{nameof(b)}: {b}; Type={b?.GetType().Name ?? ""};\n"); - - // Assert - a.Should().NotBe(b); - } - - [Theory] - [MemberData(nameof(GetTestData))] - public void Equals_Should_Return_True_When_Instances_Are_Equal([NotNull] T a, [CanBeNull] object? b) - { - Output.WriteLine($"{nameof(a)}: {a}; Type={typeof(T).Name};\n{nameof(b)}: {b}; Type={b?.GetType().Name ?? ""};\n"); - - // Assert - // ReSharper disable once EqualExpressionComparison - a.Equals(a).Should().BeTrue(); - } - - - [Theory] - [MemberData(nameof(GetEqualityOperatorTestData))] - public void EqualityOperators_Should_Compare_By_Equality_And_Not_By_Reference([NotNull] T a, [NotNull] T b, bool areEqual) - { - Output.WriteLine($"{nameof(a)}: {JsonConvert.SerializeObject(a, Formatting.Indented)}; Type={a.GetType().Name};\n{nameof(b)}: {JsonConvert.SerializeObject(b, Formatting.Indented)}; Type={b.GetType().Name};\n"); - - // Act - var equalResult = a == b; - var unEqualResult = a != b; - - // Assert - equalResult.Should().Be(areEqual); - unEqualResult.Should().Be(!areEqual); - } - - [Theory] - [MemberData(nameof(GetEqualityOperatorTestData))] - public void GetHashCode_Should_Be_Correct([NotNull] T a, [NotNull] T b, bool areEqual) - { - var hashA = a.GetHashCode(); - var hashB = b.GetHashCode(); - - Output.WriteLine($"hash of {nameof(a)}: {hashA}; Type={typeof(T).Name};\nhash of {nameof(b)}: {hashB}; Type={b.GetType().Name};\n"); - - // Assert - if (areEqual) - { - hashA.Should().Be(hashB); - } - else - { - hashA.GetHashCode().Should().NotBe(hashB); - } - } - - [NotNull] - public static IEnumerable GetEqualityOperatorTestData() - { - var equalityOperatorDataType = typeof(EqualityOperatorTestData<>); - var dataType = equalityOperatorDataType.MakeGenericType(typeof(T)); - var dataTypeInstance = Activator.CreateInstance(dataType) as EqualityOperatorTestData; - return dataTypeInstance ?? throw new ArgumentNullException(nameof(dataTypeInstance)); - } - - - [NotNull] - public static IEnumerable GetTestData() - { - if (TestDataOverride != null) - { - return TestDataOverride; - } - var data = GetTheoryData(); - return data; - } - - [NotNull] - private static NotEqualData GetTheoryData() - { - var notEqualDataType = typeof(NotEqualData<>); - var dataType = notEqualDataType.MakeGenericType(typeof(T)); - var dataTypeInstance = Activator.CreateInstance(dataType) as NotEqualData; - return dataTypeInstance ?? throw new ArgumentNullException(nameof(dataTypeInstance)); - } - } -} diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/ExpectationEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/ExpectationEqualityTests.cs deleted file mode 100644 index e818825a..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/ExpectationEqualityTests.cs +++ /dev/null @@ -1,28 +0,0 @@ -using FluentAssertions; -using NinjaTools.FluentMockServer.FluentAPI.Builders; -using NinjaTools.FluentMockServer.Models; -using NinjaTools.FluentMockServer.Models.ValueTypes; -using Xunit; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class ExpectationEqualityTests : EqualityTestBase - { - [Fact] - public void Equals_Should_Be_False_When_Instances_Are_Not_Equal() - { - // Arrange - var a = FluentExpectationBuilder.Create(times: Times.Once); - var b = FluentExpectationBuilder.Create(); - - // Assert - a.Should().NotBe(b); - } - - /// - public ExpectationEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpErrorEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpErrorEqualityTests.cs deleted file mode 100644 index 14fef206..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpErrorEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models.HttpEntities; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class HttpErrorEqualityTests : EqualityTestBase - { - /// - public HttpErrorEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpForwardEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpForwardEqualityTests.cs deleted file mode 100644 index 3ee0eaf1..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpForwardEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models.HttpEntities; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class HttpForwardEqualityTests : EqualityTestBase - { - /// - public HttpForwardEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpRequestEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpRequestEqualityTests.cs deleted file mode 100644 index e64843cc..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpRequestEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models.HttpEntities; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class HttpRequestEqualityTests : EqualityTestBase - { - /// - public HttpRequestEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpResponseEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpResponseEqualityTests.cs deleted file mode 100644 index 71b41833..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpResponseEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models.HttpEntities; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class HttpResponseEqualityTests : EqualityTestBase - { - /// - public HttpResponseEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpTemplateEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpTemplateEqualityTests.cs deleted file mode 100644 index 3d11f671..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/HttpTemplateEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models.HttpEntities; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class HttpTemplateEqualityTests : EqualityTestBase - { - /// - public HttpTemplateEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/LifeTimeEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/LifeTimeEqualityTests.cs deleted file mode 100644 index 1044e95e..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/LifeTimeEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models.ValueTypes; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class LifeTimeEqualityTests : EqualityTestBase - { - /// - public LifeTimeEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/TimesEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/TimesEqualityTests.cs deleted file mode 100644 index 433279f0..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/TimesEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models.ValueTypes; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class TimesEqualityTests : EqualityTestBase - { - /// - public TimesEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/VerificationTimesEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/VerificationTimesEqualityTests.cs deleted file mode 100644 index 56cdfa9d..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/VerificationTimesEqualityTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; -using NinjaTools.FluentMockServer.Models.ValueTypes; -using Xunit; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class VerificationTimesEqualityTests : EqualityTestBase - { - static VerificationTimesEqualityTests() - { - TestDataOverride = new List - { - new object[]{ VerificationTimes.Once, VerificationTimes.Twice}, - new []{ VerificationTimes.Once, (object) null}, - new object[]{ VerificationTimes.Once, new TheoryAttribute() }, - new object[]{ new VerificationTimes(0, 0), VerificationTimes.Twice}, - }; - } - - /// - public VerificationTimesEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/VerifyEqualityTests.cs b/test/NinjaTools.FluentMockServer.Tests/Models/Equality/VerifyEqualityTests.cs deleted file mode 100644 index 91ced32b..00000000 --- a/test/NinjaTools.FluentMockServer.Tests/Models/Equality/VerifyEqualityTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NinjaTools.FluentMockServer.Models; -using Xunit.Abstractions; - -namespace NinjaTools.FluentMockServer.Tests.Models.Equality -{ - public class VerifyEqualityTests : EqualityTestBase - { - /// - public VerifyEqualityTests(ITestOutputHelper outputHelper) : base(outputHelper) - { - } - } -} \ No newline at end of file diff --git a/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj b/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj index be36b42d..bea2b691 100644 --- a/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj +++ b/test/NinjaTools.FluentMockServer.Tests/NinjaTools.FluentMockServer.Tests.csproj @@ -6,26 +6,23 @@ Preview false enable + Enable + netcoreapp3.1 - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + - @@ -43,14 +40,4 @@ - - - - - - - ..\..\..\..\..\..\..\usr\local\share\dotnet\packs\Microsoft.AspNetCore.App.Ref\3.1.0\ref\netcoreapp3.1\Microsoft.AspNetCore.Server.Kestrel.Core.dll - - - diff --git a/test/NinjaTools.FluentMockServer.Tests/Serialization/ContentSerializationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Serialization/ContentSerializationTests.cs index 9ab3072e..60b408ae 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Serialization/ContentSerializationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Serialization/ContentSerializationTests.cs @@ -7,6 +7,7 @@ using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.Models.HttpEntities; using NinjaTools.FluentMockServer.Models.ValueTypes; +using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; using Xunit.Abstractions; @@ -23,13 +24,15 @@ public ContentSerializationTests(ITestOutputHelper outputHelper) : base(outputHe public void Should_Serialize_Binary_Content() { // Arrange - var httpResponse = HttpResponse.Create( - body: new BinaryContent("iVBORw0KGgoAAAANSUhEUgAAAqwAAAApCAIAAAB"), - delay: new Delay(TimeUnit.Milliseconds, 50)); - + var httpResponse = new HttpResponse + { + Body = new BinaryContent("iVBORw0KGgoAAAANSUhEUgAAAqwAAAApCAIAAAB"), + Delay = new Delay(TimeUnit.Milliseconds, 50) + }; + // Act - httpResponse.AsJObject(); - var json = httpResponse.AsJson(); + Serializer.SerializeJObject(httpResponse); + var json = Serializer.Serialize(httpResponse); Output.WriteLine(json); diff --git a/test/NinjaTools.FluentMockServer.Tests/Serialization/ContractResolverTests/ContractResolverTests.cs b/test/NinjaTools.FluentMockServer.Tests/Serialization/ContractResolverTests/ContractResolverTests.cs index 83ea80e0..016d5c20 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Serialization/ContractResolverTests/ContractResolverTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Serialization/ContractResolverTests/ContractResolverTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using Newtonsoft.Json.Serialization; using NinjaTools.FluentMockServer.Extensions; +using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; using Xunit.Abstractions; @@ -48,7 +49,7 @@ public void Should_SetValueProvider_For_Each_BuildableBaseMember() // Act - var jo = sut.AsJObject(); + var jo = Serializer.SerializeJObject(sut); Output.Dump(jo); TraceWriter.GetTraceMessages().ToList().ForEach(m => Output.Dump(m)); diff --git a/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationConverterTests.cs b/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationConverterTests.cs index 306c4a3c..9e0038e1 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationConverterTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationConverterTests.cs @@ -64,14 +64,14 @@ public void Should_Convert_To_Expectation_When_Converting_From_String() }"; var builder = new FluentHttpRequestBuilder() .WithPath("/some/path"); - + var expected = FluentExpectationBuilder.Create( - httpRequest: builder.Build(), - httpResponse: HttpResponse.Create( - statusCode:201, - delay: new Delay(TimeUnit.Milliseconds, 1)), - times: Times.Once - ); + builder.Build(), + new HttpResponse + { + StatusCode = 201, Delay = new Delay(TimeUnit.Milliseconds, 1), + }, + times: Times.Once); // Act var jsonReader = new JsonTextReader(new StringReader(json)); diff --git a/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationSerializationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationSerializationTests.cs index be081d0a..8f5e2490 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationSerializationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationSerializationTests.cs @@ -30,21 +30,22 @@ public void SerializeJObject_Should_Use_SerializeJObject_Of_Properties() .WithPath("") .WithMethod(HttpMethod.Post) .WithContent(body => body.WithoutXmlContent("")); - - var expectation = FluentExpectationBuilder.Create( - httpRequest: builder.Build(), - httpResponse: HttpResponse.Create(headers: new Dictionary + + var expectation = FluentExpectationBuilder.Create(builder.Build(), new HttpResponse + { + Headers = new Dictionary { {"Content-Type", new[] {"xml"}} }, - body: new JValue("some xml response"), - delay: new Delay(TimeUnit.Milliseconds, 50), - statusCode: 200) + Body = new JValue("some xml response"), + Delay = new Delay(TimeUnit.Milliseconds, 50), + StatusCode = 200 + } ); // Act - var jo = expectation.AsJObject(); + var jo = Serializer.SerializeJObject(expectation); var json = Serializer.Serialize(expectation); Logger.LogInformation("JSON", json); diff --git a/test/NinjaTools.FluentMockServer.Tests/Serialization/HttpResponseSerializationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Serialization/HttpResponseSerializationTests.cs index 07304e59..15029992 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Serialization/HttpResponseSerializationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Serialization/HttpResponseSerializationTests.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json.Linq; using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.Models.HttpEntities; +using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; using Xunit.Abstractions; @@ -19,7 +20,7 @@ public HttpResponseSerializationTests(ITestOutputHelper output) : base(output) public void Get_Body_Returns_Null_If_Content_Is_Null() { // Arrange - var response = HttpResponse.Create(); + var response = new HttpResponse(); // Act & Assert< response.Body.Should().BeNull($"{nameof(HttpResponse.Body)} is null."); @@ -33,10 +34,14 @@ public void Get_Body_Returns_JProperty_When_Set_To_Literal() var expected = new JObject(new JProperty("body", "Hello World!")); // Act - var response = HttpResponse.Create(body: new JValue("Hello World!")); + var response = new HttpResponse + { + Body = new JValue("Hello World!") + }; // Assert - var jo = response.AsJObject(); + + var jo = Serializer.SerializeJObject(response); Output.Dump(jo); jo.Should().BeEquivalentTo(expected); } diff --git a/test/NinjaTools.FluentMockServer.Tests/TestHelpers/Data/TheoryData/EqualityOperatorTestData.cs b/test/NinjaTools.FluentMockServer.Tests/TestHelpers/Data/TheoryData/EqualityOperatorTestData.cs index b5664551..c6fa21f1 100644 --- a/test/NinjaTools.FluentMockServer.Tests/TestHelpers/Data/TheoryData/EqualityOperatorTestData.cs +++ b/test/NinjaTools.FluentMockServer.Tests/TestHelpers/Data/TheoryData/EqualityOperatorTestData.cs @@ -38,8 +38,14 @@ public EqualityOperatorTestData() else if (typeof(Expectation) == dataType) { AddTestData( - FluentExpectationBuilder.Create(times: Times.Once, httpResponse: HttpResponse.Create(statusCode: 200)), - FluentExpectationBuilder.Create(HttpRequest.Create(method: "GET", path: "/test"), httpResponse: HttpResponse.Create(statusCode: 200))); + FluentExpectationBuilder.Create(times: Times.Once, httpResponse: new HttpResponse {StatusCode = 200}), + FluentExpectationBuilder.Create(new HttpRequest + { + Method = "GET", + Path = "/test" + }, + new HttpResponse {StatusCode = 200} + )); } else AddTestData(InstanceFactoryCreator.CreateDefault(), InstanceFactoryCreator.CreateRandom()); diff --git a/test/NinjaTools.FluentMockServer.Tests/TestHelpers/Data/TheoryData/RandomHttpRequestData.cs b/test/NinjaTools.FluentMockServer.Tests/TestHelpers/Data/TheoryData/RandomHttpRequestData.cs index 5eb2aad6..87dffbef 100644 --- a/test/NinjaTools.FluentMockServer.Tests/TestHelpers/Data/TheoryData/RandomHttpRequestData.cs +++ b/test/NinjaTools.FluentMockServer.Tests/TestHelpers/Data/TheoryData/RandomHttpRequestData.cs @@ -93,10 +93,12 @@ public static HttpRequest GetRandomHttpRequest() // 2) The RequestBody var faker = new Faker() - .CustomInstantiator(f => HttpRequest.Create( - method: f.Internet.RandomHttpMethod().ToString(), - path: f.Internet.UrlWithPath(), - body: GetRandomRequestBody())); + .CustomInstantiator(f => new HttpRequest + { + Method = f.Internet.RandomHttpMethod().ToString(), + Path = f.Internet.UrlWithPath(), + Body = GetRandomRequestBody() + }); return faker.Generate(); } diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs index d6ec7f6c..84074adf 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs @@ -33,14 +33,16 @@ public async Task Should_Setup_Expectation_With_Xml_Body_When_Setup_Using_Predef .Replace("{Id1}", id1) .Replace("{Id2}", id2); - var httpResponse = HttpResponse.Create( - body: new JValue(response), - delay: new Delay(TimeUnit.Milliseconds, 50), - headers: new Dictionary + var httpResponse = new HttpResponse + { + Body = new JValue(response), + Delay = new Delay(TimeUnit.Milliseconds, 50), + Headers = new Dictionary { {"Content-Type", new[] {$"text/xml; charset=utf-8"}} - }); - + } + }; + var expectation = FluentExpectationBuilder.Create(httpResponse: httpResponse); var responseJson = JsonConvert.SerializeObject(httpResponse); From 8632326e8665df8e8bc7da265e5d080412998e3d Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 00:02:20 +0100 Subject: [PATCH 50/64] chore: ignore volatile .idea files --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a1f967b3..0b324481 100644 --- a/.gitignore +++ b/.gitignore @@ -264,6 +264,8 @@ paket-files/ # JetBrains Rider .idea/ +**/.idea/contentModel.xml +**/.idea/workspaceModel.xml *.sln.iml # CodeRush @@ -285,4 +287,5 @@ stryker-config.json **/Stryker*/*.* **/.DS_Store -!.idea/ \ No newline at end of file +!.idea/ +/.idea/ From 22715b8262752d5b5486edc6dbc8a73177bb8db9 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 01:13:31 +0100 Subject: [PATCH 51/64] !refactor(verify): throw MockServerVerificationException.cs when verification fails --- .../.idea/contentModel.xml | 29 +--- .../MockServerVerificationException.cs | 33 +++++ .../Extensions/HttpResponseExtensions.cs | 21 +++ .../Builders/FluentVerificationBuilder.cs | 5 + .../FluentAPI/IFluentVerificationBuilder.cs | 1 + .../MockServerClient.cs | 124 +++++++++++------- .../MockServerSetup.cs | 7 + .../Models/Verify.cs | 4 +- .../Xunit/MockServerFixture.cs | 3 +- .../Xunit/ManualMockServerSetupTests.cs | 11 +- .../Xunit/MockServerFixtureTests.cs | 65 ++++----- .../Xunit/VerificationTests.cs | 16 +-- 12 files changed, 187 insertions(+), 132 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer/Exceptions/MockServerVerificationException.cs create mode 100644 src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index 2d0945b1..6d05f868 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -30,8 +30,11 @@ + + + - + @@ -64,15 +67,11 @@ - - - - @@ -104,6 +103,7 @@ + @@ -139,6 +139,7 @@ + @@ -280,24 +281,6 @@ - - - - - - - - - - - - - - - - - - diff --git a/src/NinjaTools.FluentMockServer/Exceptions/MockServerVerificationException.cs b/src/NinjaTools.FluentMockServer/Exceptions/MockServerVerificationException.cs new file mode 100644 index 00000000..4b0c4ce7 --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Exceptions/MockServerVerificationException.cs @@ -0,0 +1,33 @@ +using System; +using NinjaTools.FluentMockServer.Models.HttpEntities; + +namespace NinjaTools.FluentMockServer.Exceptions +{ + public class MockServerException : Exception + { + } + + public class MockServerOperationFailedException : Exception + { + public string Operation { get; } + + /// + public MockServerOperationFailedException(string operation) + { + Operation = operation; + } + } + + public class MockServerVerificationException : MockServerException + { + public string ResponseMessage { get; } + public HttpRequest Expected { get; } + + /// + public MockServerVerificationException(string responseMessage, HttpRequest expectedHttpRequest) + { + ResponseMessage = responseMessage; + Expected = expectedHttpRequest; + } + } +} diff --git a/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs b/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs new file mode 100644 index 00000000..22892366 --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs @@ -0,0 +1,21 @@ +using System.Net.Http; +using System.Runtime.CompilerServices; +using NinjaTools.FluentMockServer.Exceptions; + +namespace NinjaTools.FluentMockServer.Extensions +{ + public static class HttpResponseExtensions + { + /// + /// Ensures that the operation on the MockServer attempted, was successful. + /// + /// + public static void EnsureSuccessfulMockServerOperation(this HttpResponseMessage responseMessage, [CallerMemberName] string caller = null) + { + if (!responseMessage.IsSuccessStatusCode) + { + throw new MockServerOperationFailedException(caller); + } + } + } +} diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs index c8fd18cd..487bb4d5 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs @@ -30,6 +30,11 @@ public IWithVerify Verify([NotNull] Action request) return this; } + /// + public void AtLeastOnce() + { + _verificationTimes = VerificationTimes.MoreThan(1); + } /// public void AtLeast(int value) diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs index 543c69b6..7c8d6d0e 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs @@ -13,6 +13,7 @@ public interface IFluentVerificationBuilder : IFluentBuilder public interface IWithVerify { void AtLeast(int value); + void AtLeastOnce(); void AtMost(int value); void Between(int min, int max); void Once(); diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index 948b9c99..704b989c 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -5,37 +5,36 @@ using System.Net.Http; using System.Threading.Tasks; using JetBrains.Annotations; +using NinjaTools.FluentMockServer.Exceptions; using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI; using NinjaTools.FluentMockServer.FluentAPI.Builders; +using NinjaTools.FluentMockServer.Models; +using NinjaTools.FluentMockServer.Models.HttpEntities; using NinjaTools.FluentMockServer.Models.ValueTypes; using NinjaTools.FluentMockServer.Utils; -using NinjaTools.FluentMockServer.Xunit; using static NinjaTools.FluentMockServer.Utils.RequestFactory; namespace NinjaTools.FluentMockServer { + /// + /// + /// Provides a base class for interacting with a MockServer. + /// Following operations are supported: + /// + /// + /// Create an . + /// + /// + /// Verify whether the MockServer received a matching . + /// + /// + /// setting up HTTP requests and receiving HTTP responses from a resource identified by a URI. + /// [DebuggerDisplay("Context={Context}; Host={HttpClient.BaseAddress.Host}")] public class MockServerClient : IDisposable { private readonly IMockServerLogger _logger; - private readonly HttpClient _httpClient; - - [NotNull] - public HttpClient HttpClient - { - get - { - if (!string.IsNullOrWhiteSpace(Context)) - { - _httpClient.DefaultRequestHeaders.Add(HttpExtensions.MockContextHeaderKey, Context); - } - - return _httpClient; - } - } - - public string? Context { get; } public MockServerClient([NotNull] HttpClient client, [NotNull] string hostname, [NotNull] IMockServerLogger logger, string? context = null) { @@ -43,20 +42,29 @@ public MockServerClient([NotNull] HttpClient client, [NotNull] string hostname, client.BaseAddress = new Uri(hostname); client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Host = null; - _httpClient = client; + HttpClient = client; Context = context; } - public MockServerClient([NotNull] string mockServerEndpoint, [NotNull] IMockServerLogger logger, string? context = null) : this(new HttpClient(), mockServerEndpoint, logger, context) + /// + public MockServerClient([NotNull] string mockServerEndpoint, [NotNull] IMockServerLogger logger, string? context = null) + : this(new HttpClient(), mockServerEndpoint, logger, context) { } + [PublicAPI] + public HttpClient HttpClient { get; } + + public string? Context { get; } + + /// /// Configures the MockServer Client. /// - /// - /// + /// + /// [NotNull] + [PublicAPI] public Task SetupAsync([NotNull] Func setupFactory) { var builder = new FluentExpectationBuilder(new MockServerSetup()); @@ -69,8 +77,9 @@ public Task SetupAsync([NotNull] Func /// Configures the MockServer Client. /// - /// - /// + /// + /// + [PublicAPI] public Task SetupAsync(Action setupFactory) { var builder = new FluentExpectationBuilder(new MockServerSetup()); @@ -83,56 +92,72 @@ public Task SetupAsync(Action setupFactory) /// /// Configures the MockServer Client using a predefined . /// - /// - /// + /// + [PublicAPI] public async Task SetupAsync(MockServerSetup setup) { foreach (var request in setup.Expectations.Select(GetExpectationMessage)) { var response = await HttpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); + response.EnsureSuccessfulMockServerOperation(); } } - [ItemNotNull] - public async Task ResetAsync() + /// + /// Resets all on the MockServer. + /// + /// + [PublicAPI] + public async Task ResetAsync() { _logger.WriteLine("Resetting MockServer..."); var request = GetResetMessage(); var response = await HttpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - return response; + response.EnsureSuccessfulMockServerOperation(); } + /// + /// Verifies that a the MockServer received a matching . + /// + /// Configure the matcher for the . + /// Configure how many the matched request should have occured. + /// [NotNull] - public async Task<(bool isValid, string responseMessage)> VerifyAsync([NotNull] Action verify, [CanBeNull] VerificationTimes times = null) + [PublicAPI] + public Task VerifyAsync([NotNull] Action verify, [CanBeNull] VerificationTimes times = null) { - var response = await Verify(v => - { - if (times != null) - { - v.Verify(verify).Times(times); - } - else - { - v.Verify(verify); - } - }); - var responseMessage = await response.Content.ReadAsStringAsync(); - return (response.StatusCode == HttpStatusCode.Accepted, responseMessage); + if (times is null) + return VerifyAsync(v => v.Verify(verify)); + else + return VerifyAsync(v => v.Verify(verify).Times(times)); } + /// + /// Verifies that a the MockServer received a matching . + /// + /// Configure the matcher for the . + /// [NotNull] - [ItemNotNull] - public async Task Verify([NotNull] Action verify) + [PublicAPI] + public async Task VerifyAsync([NotNull] Action verify) { var builder = new FluentVerificationBuilder(); verify(builder); var verification = builder.Build(); - var request = GetVerifyRequest(verification); + await VerifyInternal(verification); + } + + private async Task VerifyInternal(Verify verify) + { + var request = GetVerifyRequest(verify); var response = await HttpClient.SendAsync(request); - return response; + + if (response.StatusCode != HttpStatusCode.Accepted) + { + var responseMessage = await response.Content.ReadAsStringAsync(); + throw new MockServerVerificationException(responseMessage, verify.HttpRequest); + } } /// @@ -140,5 +165,6 @@ public void Dispose() { HttpClient.Dispose(); } + } } diff --git a/src/NinjaTools.FluentMockServer/MockServerSetup.cs b/src/NinjaTools.FluentMockServer/MockServerSetup.cs index 53e1bca6..3ae27408 100644 --- a/src/NinjaTools.FluentMockServer/MockServerSetup.cs +++ b/src/NinjaTools.FluentMockServer/MockServerSetup.cs @@ -1,10 +1,17 @@ using System.Collections.Generic; +using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models; namespace NinjaTools.FluentMockServer { + /// + /// Represents a collection of to be send to the MockServer. + /// public class MockServerSetup { + /// + /// The collection of to be send to the MockServer. + /// public List Expectations { get; } = new List(); } } diff --git a/src/NinjaTools.FluentMockServer/Models/Verify.cs b/src/NinjaTools.FluentMockServer/Models/Verify.cs index 68e98e19..c6cfd3ba 100644 --- a/src/NinjaTools.FluentMockServer/Models/Verify.cs +++ b/src/NinjaTools.FluentMockServer/Models/Verify.cs @@ -9,7 +9,7 @@ namespace NinjaTools.FluentMockServer.Models /// public class Verify { - public Verify(HttpRequest httpRequest, VerificationTimes times) + public Verify(HttpRequest httpRequest, VerificationTimes? times) { Times = times; HttpRequest = httpRequest; @@ -23,7 +23,7 @@ public Verify(HttpRequest httpRequest, VerificationTimes times) /// /// How many the request is expected to have occured. /// - public VerificationTimes Times { get; } + public VerificationTimes? Times { get; } public static Verify Once(HttpRequest httpRequest = null) { diff --git a/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs b/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs index 8913695a..02cfbdf3 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Threading.Tasks; using JetBrains.Annotations; +using NinjaTools.FluentMockServer.Utils; using Xunit; namespace NinjaTools.FluentMockServer.Xunit @@ -45,7 +46,7 @@ public class MockServerFixture : IDisposable, IAsyncLifetime public MockServerFixture() { Container = new MockServerContainer(); - MockClient = new MockServerClient(Container.MockServerBaseUrl); + MockClient = new MockServerClient(Container.MockServerBaseUrl, MockServerTestLogger.Instance); } /// diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs index f09e42d7..8a7dc72a 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs @@ -27,8 +27,7 @@ await MockClient.SetupAsync(exp => .Setup()); // Assert - var request = new HttpRequestMessage(HttpMethod.Get, new Uri("test", UriKind.Relative)); - var response = await MockClient.SendAsync(request); + var response = await HttpClient.GetAsync("test"); response.StatusCode.Should().Be(HttpStatusCode.Created); } @@ -45,8 +44,7 @@ await MockClient.SetupAsync(exp => await MockClient.ResetAsync(); // Assert - var request = new HttpRequestMessage(HttpMethod.Get, new Uri("test", UriKind.Relative)); - var response = await MockClient.SendAsync(request); + var response = await HttpClient.GetAsync("test"); response.StatusCode.Should().Be(HttpStatusCode.NotFound); } @@ -58,12 +56,9 @@ public async Task Should_Verify_Expecation_Was_Met_On_MockServer() await HttpClient.SendAsync(request); // Act - var (isValid, responseMessage) = await MockClient.VerifyAsync(v => v + await MockClient.VerifyAsync(v => v .WithMethod(HttpMethod.Get) .WithPath("test"), VerificationTimes.MoreThan(1)); - - // Assert - isValid.Should().BeTrue(); } } diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs index dc9e15c4..2b56b4c3 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs @@ -15,7 +15,7 @@ namespace NinjaTools.FluentMockServer.Tests.Xunit { - public class MockServerFixtureTests : MockServerTestBase + public class MockServerFixtureTests : MockServerTestBase { /// public MockServerFixtureTests(MockServerFixture fixture, ITestOutputHelper output) : base(fixture, output) @@ -25,35 +25,31 @@ public MockServerFixtureTests(MockServerFixture fixture, ITestOutputHelper outpu [Fact] public async Task Should_Reset_Expectation_On_MockServer() { - // Arrange - await MockClient.SetupAsync(exp => - exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) - .Setup()); + await MockClient.SetupAsync(exp => + exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) + .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .Setup()); // Act await MockClient.ResetAsync(); - // Assert - var request = new HttpRequestMessage(HttpMethod.Get, new Uri("test", UriKind.Relative)); - var response = await MockClient.SendAsync(request); + // Assert + var response = await HttpClient.GetAsync("test"); - response.StatusCode.Should().Be(HttpStatusCode.NotFound); - + response.StatusCode.Should().Be(HttpStatusCode.NotFound); } [Fact] public async Task Should_Successfully_Setup_Up_Expectation_On_MockServer() { // Arrange - - await MockClient.SetupAsync(exp => - exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) - .Setup()); - - var request = new HttpRequestMessage(HttpMethod.Get, new Uri("test", UriKind.Relative)); - var response = await MockClient.SendAsync(request); + + await MockClient.SetupAsync(exp => + exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) + .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .Setup()); + + var response = await HttpClient.GetAsync("test"); response.StatusCode.Should().Be(HttpStatusCode.Created); } @@ -75,14 +71,13 @@ public async Task Should_Successfully_Setup_Up_Expectation_On_MockServer2() } // Act - + var expectation = factory(builder).Expectations.First(); Output.WriteLine(Serializer.Serialize(expectation)); await MockClient.SetupAsync(factory); - var request = new HttpRequestMessage(HttpMethod.Get, new Uri("/test", UriKind.Relative)); - var response = await MockClient.SendAsync(request); + var response = await HttpClient.GetAsync("test"); response.StatusCode.Should().Be(HttpStatusCode.Created); } @@ -91,21 +86,17 @@ public async Task Should_Verify_Expectation_Was_Met_On_MockServer() { // Arrange await MockClient.SetupAsync(exp => - exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) - .Setup()); - - var request = new HttpRequestMessage(HttpMethod.Get, new Uri("test", UriKind.Relative)); - await MockClient.SendAsync(request); - var builder = new FluentHttpRequestBuilder(); - builder.WithMethod(HttpMethod.Get).WithPath("/test"); - - // Act - var (isValid, response) = await MockClient.VerifyAsync( v => v - .WithPath("/test").WithMethod(HttpMethod.Get), VerificationTimes.Once); - - // Assert - isValid.Should().BeTrue(); + exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) + .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .Setup()); + + await HttpClient.GetAsync("test"); + + var builder = new FluentHttpRequestBuilder(); + builder.WithMethod(HttpMethod.Get).WithPath("/test"); + + // Act + await MockClient.VerifyAsync(v => v.WithPath("/test").WithMethod(HttpMethod.Get), VerificationTimes.Once); } } } diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs index a6aa1473..f611ddc4 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; +using NinjaTools.FluentMockServer.Exceptions; using NinjaTools.FluentMockServer.Models.ValueTypes; using NinjaTools.FluentMockServer.Tests.TestHelpers; using NinjaTools.FluentMockServer.Xunit; @@ -25,26 +26,17 @@ public async Task VerifyAsync_Should_Return_True_When_MockServer_Recieved_Matchi await HttpClient.GetAsync("test"); // Act - var (isValid, responseMessage) = await MockClient.VerifyAsync(v => v - .WithPath("test"), VerificationTimes.Once); - - // Assert - Output.WriteLine(responseMessage); - isValid.Should().BeTrue(); - responseMessage.Should().BeEmpty(); + await MockClient.VerifyAsync(v => v.WithPath("test"), VerificationTimes.Once); } [Fact] public async Task VerifyAsync_Should_Return_False_When_MockServer_Recieved_No_Matching_Setup() { // Act - var (isValid, responseMessage) = await MockClient.VerifyAsync(v => v - .WithPath("test"), VerificationTimes.Once); + Func action = async () => await MockClient.VerifyAsync(v => v.WithPath("test"), VerificationTimes.Once); // Assert - Output.WriteLine(responseMessage); - isValid.Should().BeFalse(); - responseMessage.Should().NotBeNullOrWhiteSpace(); + action.Should().ThrowExactly(); } } From 6fd48353da86dd79a62a521255f58c67a46ff003 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 01:17:16 +0100 Subject: [PATCH 52/64] refactor(client): make non public-api types internal --- .../Extensions/HttpResponseExtensions.cs | 2 +- .../Extensions/StringExtensions.cs | 2 +- .../Serialization/ContractResolver.cs | 2 +- .../Serialization/ExpectationConverter.cs | 2 +- src/NinjaTools.FluentMockServer/Serialization/JsonConfig.cs | 2 +- src/NinjaTools.FluentMockServer/Serialization/JsonContent.cs | 2 +- src/NinjaTools.FluentMockServer/Serialization/Serializer.cs | 2 +- src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs | 2 +- src/NinjaTools.FluentMockServer/Utils/RequestFactory.cs | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs b/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs index 22892366..983953c8 100644 --- a/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs +++ b/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs @@ -4,7 +4,7 @@ namespace NinjaTools.FluentMockServer.Extensions { - public static class HttpResponseExtensions + internal static class HttpResponseExtensions { /// /// Ensures that the operation on the MockServer attempted, was successful. diff --git a/src/NinjaTools.FluentMockServer/Extensions/StringExtensions.cs b/src/NinjaTools.FluentMockServer/Extensions/StringExtensions.cs index 6d7cfe7d..b1d67801 100644 --- a/src/NinjaTools.FluentMockServer/Extensions/StringExtensions.cs +++ b/src/NinjaTools.FluentMockServer/Extensions/StringExtensions.cs @@ -1,6 +1,6 @@ namespace NinjaTools.FluentMockServer.Extensions { - public static class StringExtensions + internal static class StringExtensions { public static bool StartsWithEither(this string @string, params char[] chars) { diff --git a/src/NinjaTools.FluentMockServer/Serialization/ContractResolver.cs b/src/NinjaTools.FluentMockServer/Serialization/ContractResolver.cs index da7cc19a..47fbf63b 100644 --- a/src/NinjaTools.FluentMockServer/Serialization/ContractResolver.cs +++ b/src/NinjaTools.FluentMockServer/Serialization/ContractResolver.cs @@ -6,7 +6,7 @@ namespace NinjaTools.FluentMockServer.Serialization { - public class ContractResolver : CamelCasePropertyNamesContractResolver + internal class ContractResolver : CamelCasePropertyNamesContractResolver { private static readonly Lazy Lazy = new Lazy(() => new ContractResolver()); diff --git a/src/NinjaTools.FluentMockServer/Serialization/ExpectationConverter.cs b/src/NinjaTools.FluentMockServer/Serialization/ExpectationConverter.cs index 89d7214d..9ffa6777 100644 --- a/src/NinjaTools.FluentMockServer/Serialization/ExpectationConverter.cs +++ b/src/NinjaTools.FluentMockServer/Serialization/ExpectationConverter.cs @@ -10,7 +10,7 @@ namespace NinjaTools.FluentMockServer.Serialization { - public class ExpectationConverter : JsonConverter + internal class ExpectationConverter : JsonConverter { private static readonly object Sync = new object(); diff --git a/src/NinjaTools.FluentMockServer/Serialization/JsonConfig.cs b/src/NinjaTools.FluentMockServer/Serialization/JsonConfig.cs index 3d725bd3..d6a3633c 100644 --- a/src/NinjaTools.FluentMockServer/Serialization/JsonConfig.cs +++ b/src/NinjaTools.FluentMockServer/Serialization/JsonConfig.cs @@ -3,7 +3,7 @@ namespace NinjaTools.FluentMockServer.Serialization { /// - public class JsonConfig : JsonSerializerSettings + internal class JsonConfig : JsonSerializerSettings { public static readonly JsonConfig Instance = new JsonConfig(); diff --git a/src/NinjaTools.FluentMockServer/Serialization/JsonContent.cs b/src/NinjaTools.FluentMockServer/Serialization/JsonContent.cs index 51a190dc..159dbdaa 100644 --- a/src/NinjaTools.FluentMockServer/Serialization/JsonContent.cs +++ b/src/NinjaTools.FluentMockServer/Serialization/JsonContent.cs @@ -4,7 +4,7 @@ namespace NinjaTools.FluentMockServer.Serialization { - public class JsonContent : StringContent + internal class JsonContent : StringContent { /// public JsonContent([NotNull] object content) : base(Serializer.Serialize(content), Encoding.UTF8, "application/json") diff --git a/src/NinjaTools.FluentMockServer/Serialization/Serializer.cs b/src/NinjaTools.FluentMockServer/Serialization/Serializer.cs index 05e9d014..035815e9 100644 --- a/src/NinjaTools.FluentMockServer/Serialization/Serializer.cs +++ b/src/NinjaTools.FluentMockServer/Serialization/Serializer.cs @@ -6,7 +6,7 @@ namespace NinjaTools.FluentMockServer.Serialization { - public static class Serializer + internal static class Serializer { private static Lazy _lazy = new Lazy(() => new ProxySerializer()); diff --git a/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs b/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs index 0a5f3051..e9c8d677 100644 --- a/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs +++ b/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs @@ -10,7 +10,7 @@ public interface IMockServerLogger void Error([JetBrains.Annotations.NotNull] string message, params object[] args); } - public class MockServerTestLogger : IMockServerLogger + internal class MockServerTestLogger : IMockServerLogger { /// /// Gets the default . diff --git a/src/NinjaTools.FluentMockServer/Utils/RequestFactory.cs b/src/NinjaTools.FluentMockServer/Utils/RequestFactory.cs index f25d2fa9..bc7a1b26 100644 --- a/src/NinjaTools.FluentMockServer/Utils/RequestFactory.cs +++ b/src/NinjaTools.FluentMockServer/Utils/RequestFactory.cs @@ -5,14 +5,14 @@ namespace NinjaTools.FluentMockServer.Utils { - public static class MockServerEndpoints + internal static class MockServerEndpoints { public const string Expectations = "mockserver/expectation"; public const string Reset = "mockserver/reset"; public const string Verify = "mockserver/verify"; } - public static class RequestFactory + internal static class RequestFactory { [NotNull] From 9daaad68ea024a118f23c50d5a8e4c54eff786a8 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 01:30:33 +0100 Subject: [PATCH 53/64] refactor(client): mark public api with [PublicAPI] --- .../.idea/contentModel.xml | 2 ++ .../Exceptions/MockServerException.cs | 9 ++++++ .../MockServerOperationFailedException.cs | 23 ++++++++++++++ .../MockServerVerificationException.cs | 30 +++++++++---------- .../FluentAPI/IFluentBodyBuilder.cs | 2 ++ .../FluentAPI/IFluentBuilder.cs | 3 ++ .../FluentAPI/IFluentExpectationBuilder.cs | 4 +++ .../FluentAPI/IFluentHttpRequestBuilder.cs | 1 + .../FluentAPI/IFluentHttpResponseBuilder.cs | 1 + .../FluentAPI/IFluentVerificationBuilder.cs | 3 ++ .../MockServerClient.cs | 9 +----- .../MockServerSetup.cs | 1 + .../Models/Expectation.cs | 1 + .../Models/HttpEntities/HttpError.cs | 2 ++ .../Models/HttpEntities/HttpRequest.cs | 1 + .../Models/HttpEntities/HttpResponse.cs | 1 + .../Models/ValueTypes/Body.cs | 2 ++ .../Models/ValueTypes/ConnectionOptions.cs | 2 ++ .../Models/ValueTypes/Content.cs | 2 +- .../Models/ValueTypes/Delay.cs | 2 ++ .../Models/ValueTypes/LifeTime.cs | 2 ++ .../Models/ValueTypes/TimeUnit.cs | 3 +- .../Models/ValueTypes/Times.cs | 3 +- .../Models/ValueTypes/VerificationTimes.cs | 1 + .../Models/Verify.cs | 7 ++--- .../Xunit/MockServerContainer.cs | 2 +- .../Xunit/MockServerFixture.cs | 6 ++-- .../ExpectationConverterTests.cs | 2 +- 28 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer/Exceptions/MockServerException.cs create mode 100644 src/NinjaTools.FluentMockServer/Exceptions/MockServerOperationFailedException.cs diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index 6d05f868..1d9109b6 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -31,6 +31,8 @@ + + diff --git a/src/NinjaTools.FluentMockServer/Exceptions/MockServerException.cs b/src/NinjaTools.FluentMockServer/Exceptions/MockServerException.cs new file mode 100644 index 00000000..f258e498 --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Exceptions/MockServerException.cs @@ -0,0 +1,9 @@ +using System; + +namespace NinjaTools.FluentMockServer.Exceptions +{ + /// + public abstract class MockServerException : Exception + { + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer/Exceptions/MockServerOperationFailedException.cs b/src/NinjaTools.FluentMockServer/Exceptions/MockServerOperationFailedException.cs new file mode 100644 index 00000000..1af280e5 --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Exceptions/MockServerOperationFailedException.cs @@ -0,0 +1,23 @@ +using JetBrains.Annotations; + +namespace NinjaTools.FluentMockServer.Exceptions +{ + /// + /// + /// Indicates that a connection to the MockServer could not be established or an operation failed. + /// + [PublicAPI] + public sealed class MockServerOperationFailedException : MockServerException + { + /// + /// The failed operation. + /// + public string Operation { get; } + + /// + public MockServerOperationFailedException(string operation) + { + Operation = operation; + } + } +} \ No newline at end of file diff --git a/src/NinjaTools.FluentMockServer/Exceptions/MockServerVerificationException.cs b/src/NinjaTools.FluentMockServer/Exceptions/MockServerVerificationException.cs index 4b0c4ce7..d35dfa6a 100644 --- a/src/NinjaTools.FluentMockServer/Exceptions/MockServerVerificationException.cs +++ b/src/NinjaTools.FluentMockServer/Exceptions/MockServerVerificationException.cs @@ -1,26 +1,24 @@ -using System; +using System.Net.Http; +using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models.HttpEntities; namespace NinjaTools.FluentMockServer.Exceptions { - public class MockServerException : Exception - { - } - - public class MockServerOperationFailedException : Exception - { - public string Operation { get; } - - /// - public MockServerOperationFailedException(string operation) - { - Operation = operation; - } - } - + /// + /// + /// Indicates that the MockServer did not receive an expected . + /// + [PublicAPI] public class MockServerVerificationException : MockServerException { + /// + /// The content of the of the MockServer. + /// public string ResponseMessage { get; } + + /// + /// The expected . + /// public HttpRequest Expected { get; } /// diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentBodyBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentBodyBuilder.cs index b61339e7..afcce340 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentBodyBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentBodyBuilder.cs @@ -1,8 +1,10 @@ +using JetBrains.Annotations; using Newtonsoft.Json.Linq; using NinjaTools.FluentMockServer.Serialization; namespace NinjaTools.FluentMockServer.FluentAPI { + [PublicAPI] public interface IFluentBodyBuilder : IFluentBuilder { void WithBinary(string base64); diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentBuilder.cs index 6894d9c8..d5cd724a 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentBuilder.cs @@ -1,5 +1,8 @@ +using JetBrains.Annotations; + namespace NinjaTools.FluentMockServer.FluentAPI { + [PublicAPI] public interface IFluentBuilder : IFluentInterface where TBuildable : class { TBuildable Build(); diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs index 7e1f39a4..3aed5f41 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs @@ -10,6 +10,7 @@ namespace NinjaTools.FluentMockServer.FluentAPI /// /// [EditorBrowsable(EditorBrowsableState.Never)] + [PublicAPI] public interface IBlankExpectation : IFluentInterface { IWithRequest OnHandling(HttpMethod method = null, [CanBeNull] Action requestFactory = null); @@ -19,6 +20,7 @@ public interface IBlankExpectation : IFluentInterface /// /// [EditorBrowsable(EditorBrowsableState.Never)] + [PublicAPI] public interface IFluentExpectationBuilder : IBlankExpectation , IWithRequest, IWithResponse { @@ -26,6 +28,7 @@ public interface IFluentExpectationBuilder : IBlankExpectation , IWithRequest, I /// /// [EditorBrowsable(EditorBrowsableState.Never)] + [PublicAPI] public interface IWithResponse : IFluentInterface { IFluentExpectationBuilder And { get; } @@ -36,6 +39,7 @@ public interface IWithResponse : IFluentInterface /// /// [EditorBrowsable(EditorBrowsableState.Never)] + [PublicAPI] public interface IWithRequest : IFluentInterface { IWithResponse RespondWith(int statusCode, Action responseFactory = null); diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentHttpRequestBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentHttpRequestBuilder.cs index 812837ce..fb922172 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentHttpRequestBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentHttpRequestBuilder.cs @@ -6,6 +6,7 @@ namespace NinjaTools.FluentMockServer.FluentAPI { + [PublicAPI] public interface IFluentHttpRequestBuilder : IFluentBuilder { [NotNull] IFluentHttpRequestBuilder WithMethod([NotNull] HttpMethod method); diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentHttpResponseBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentHttpResponseBuilder.cs index e875bd34..9d2848ed 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentHttpResponseBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentHttpResponseBuilder.cs @@ -8,6 +8,7 @@ namespace NinjaTools.FluentMockServer.FluentAPI { /// /// + [PublicAPI] public interface IFluentHttpResponseBuilder : IFluentInterface { [NotNull] diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs index 7c8d6d0e..d5c6eda6 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs @@ -1,15 +1,18 @@ using System; +using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models; using NinjaTools.FluentMockServer.Models.ValueTypes; namespace NinjaTools.FluentMockServer.FluentAPI { + [PublicAPI] public interface IFluentVerificationBuilder : IFluentBuilder { IWithVerify Verify(Action request); } + [PublicAPI] public interface IWithVerify { void AtLeast(int value); diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index 704b989c..1389e75d 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -32,6 +32,7 @@ namespace NinjaTools.FluentMockServer /// setting up HTTP requests and receiving HTTP responses from a resource identified by a URI. /// [DebuggerDisplay("Context={Context}; Host={HttpClient.BaseAddress.Host}")] + [PublicAPI] public class MockServerClient : IDisposable { private readonly IMockServerLogger _logger; @@ -52,7 +53,6 @@ public MockServerClient([NotNull] string mockServerEndpoint, [NotNull] IMockServ { } - [PublicAPI] public HttpClient HttpClient { get; } public string? Context { get; } @@ -64,7 +64,6 @@ public MockServerClient([NotNull] string mockServerEndpoint, [NotNull] IMockServ /// /// [NotNull] - [PublicAPI] public Task SetupAsync([NotNull] Func setupFactory) { var builder = new FluentExpectationBuilder(new MockServerSetup()); @@ -78,8 +77,6 @@ public Task SetupAsync([NotNull] Func /// - /// - [PublicAPI] public Task SetupAsync(Action setupFactory) { var builder = new FluentExpectationBuilder(new MockServerSetup()); @@ -93,7 +90,6 @@ public Task SetupAsync(Action setupFactory) /// Configures the MockServer Client using a predefined . /// /// - [PublicAPI] public async Task SetupAsync(MockServerSetup setup) { foreach (var request in setup.Expectations.Select(GetExpectationMessage)) @@ -108,7 +104,6 @@ public async Task SetupAsync(MockServerSetup setup) /// Resets all on the MockServer. /// /// - [PublicAPI] public async Task ResetAsync() { _logger.WriteLine("Resetting MockServer..."); @@ -124,7 +119,6 @@ public async Task ResetAsync() /// Configure how many the matched request should have occured. /// [NotNull] - [PublicAPI] public Task VerifyAsync([NotNull] Action verify, [CanBeNull] VerificationTimes times = null) { if (times is null) @@ -139,7 +133,6 @@ public Task VerifyAsync([NotNull] Action verify, [Can /// Configure the matcher for the . /// [NotNull] - [PublicAPI] public async Task VerifyAsync([NotNull] Action verify) { var builder = new FluentVerificationBuilder(); diff --git a/src/NinjaTools.FluentMockServer/MockServerSetup.cs b/src/NinjaTools.FluentMockServer/MockServerSetup.cs index 3ae27408..4bad6d07 100644 --- a/src/NinjaTools.FluentMockServer/MockServerSetup.cs +++ b/src/NinjaTools.FluentMockServer/MockServerSetup.cs @@ -7,6 +7,7 @@ namespace NinjaTools.FluentMockServer /// /// Represents a collection of to be send to the MockServer. /// + [PublicAPI] public class MockServerSetup { /// diff --git a/src/NinjaTools.FluentMockServer/Models/Expectation.cs b/src/NinjaTools.FluentMockServer/Models/Expectation.cs index 4203a40e..be3f5587 100644 --- a/src/NinjaTools.FluentMockServer/Models/Expectation.cs +++ b/src/NinjaTools.FluentMockServer/Models/Expectation.cs @@ -10,6 +10,7 @@ namespace NinjaTools.FluentMockServer.Models /// Model to set up an Expectation on the MockServer. /// [JsonConverter(typeof(ExpectationConverter))] + [PublicAPI] public class Expectation { [CanBeNull] diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs index eefa669a..9c358266 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs @@ -1,4 +1,5 @@ using System; +using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models.ValueTypes; namespace NinjaTools.FluentMockServer.Models.HttpEntities @@ -6,6 +7,7 @@ namespace NinjaTools.FluentMockServer.Models.HttpEntities /// /// Model to configure an Error. /// + [PublicAPI] public class HttpError { public HttpError(Delay delay, bool? dropConnection, string responseBytes) diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs index cb9796d3..65f9b0e3 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs @@ -9,6 +9,7 @@ namespace NinjaTools.FluentMockServer.Models.HttpEntities /// /// Model to describe, which request should be matched. /// + [PublicAPI] public class HttpRequest { public HttpRequest() diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs index 65cac3e2..b29677d1 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs @@ -13,6 +13,7 @@ namespace NinjaTools.FluentMockServer.Models.HttpEntities /// /// Model to describe how to respond to a matching . /// + [PublicAPI] public class HttpResponse { public HttpResponse() { } diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs index 43626d3c..309da3f8 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs @@ -1,11 +1,13 @@ using System; using System.Linq; +using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; namespace NinjaTools.FluentMockServer.Models.ValueTypes { + [PublicAPI] public class Body : JObject { /// diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs index 670d1006..df733a81 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs @@ -1,4 +1,5 @@ using System; +using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models.HttpEntities; namespace NinjaTools.FluentMockServer.Models.ValueTypes @@ -6,6 +7,7 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes /// /// Some options regarding a Connection. /// + [PublicAPI] public class ConnectionOptions { public ConnectionOptions(bool? closeSocket, long? contentLengthHeaderOverride, bool? suppressContentLengthHeader, bool? suppressConnectionHeader, bool? keepAliveOverride) diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Content.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Content.cs index 3ca40ac5..d13eb5c5 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Content.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Content.cs @@ -3,7 +3,7 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes { - public class BinaryContent + internal class BinaryContent { public const string Type = "BINARY"; diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs index ed6bbb68..270db77d 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs @@ -1,4 +1,5 @@ using System; +using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models.HttpEntities; namespace NinjaTools.FluentMockServer.Models.ValueTypes @@ -6,6 +7,7 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes /// /// Model to configure an optional before responding with an action to a matched . /// + [PublicAPI] public class Delay { public Delay(TimeUnit timeUnit, int value) diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs index 42b90365..e7a53bd0 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs @@ -1,7 +1,9 @@ using System; +using JetBrains.Annotations; namespace NinjaTools.FluentMockServer.Models.ValueTypes { + [PublicAPI] public class LifeTime { public LifeTime(int? timeToLive = null, TimeUnit timeUnit = ValueTypes.TimeUnit.Milliseconds) diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/TimeUnit.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/TimeUnit.cs index 2ea0193c..11447498 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/TimeUnit.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/TimeUnit.cs @@ -1,4 +1,5 @@ using System; +using JetBrains.Annotations; using Newtonsoft.Json; using NinjaTools.FluentMockServer.Serialization; @@ -8,7 +9,7 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes /// Enumerate the available time unit. /// [JsonConverter(typeof(ContractResolver.UpperCaseEnumConverter))] - [Serializable] + [PublicAPI] public enum TimeUnit { /// diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Times.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Times.cs index c7e5597f..c7f410b8 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Times.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Times.cs @@ -1,10 +1,9 @@ -using System; using JetBrains.Annotations; using Newtonsoft.Json; namespace NinjaTools.FluentMockServer.Models.ValueTypes { - [Serializable] + [PublicAPI] public class Times { [JsonConstructor] diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs index 3c16a812..fb3b2464 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs @@ -4,6 +4,7 @@ namespace NinjaTools.FluentMockServer.Models.ValueTypes { + [PublicAPI] public class VerificationTimes { [JsonConstructor] diff --git a/src/NinjaTools.FluentMockServer/Models/Verify.cs b/src/NinjaTools.FluentMockServer/Models/Verify.cs index c6cfd3ba..81a9b4aa 100644 --- a/src/NinjaTools.FluentMockServer/Models/Verify.cs +++ b/src/NinjaTools.FluentMockServer/Models/Verify.cs @@ -1,4 +1,5 @@ using System; +using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models.HttpEntities; using NinjaTools.FluentMockServer.Models.ValueTypes; @@ -7,6 +8,7 @@ namespace NinjaTools.FluentMockServer.Models /// /// Model used to describe what to verify. /// + [PublicAPI] public class Verify { public Verify(HttpRequest httpRequest, VerificationTimes? times) @@ -24,10 +26,5 @@ public Verify(HttpRequest httpRequest, VerificationTimes? times) /// How many the request is expected to have occured. /// public VerificationTimes? Times { get; } - - public static Verify Once(HttpRequest httpRequest = null) - { - return new Verify(httpRequest, VerificationTimes.Once); - } } } diff --git a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs index 4866139f..7a05608b 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs @@ -12,7 +12,7 @@ namespace NinjaTools.FluentMockServer.Xunit /// /// The InMemory handle to the MockServer Docker Container /// - public class MockServerContainer : IDisposable + internal class MockServerContainer : IDisposable { /// /// Gets the Port exposed to the Host. diff --git a/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs b/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs index 02cfbdf3..a741a818 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs @@ -7,9 +7,11 @@ namespace NinjaTools.FluentMockServer.Xunit { + /// /// - /// An exposing a . + /// An exposing a . /// + [PublicAPI] public class MockServerFixture : IDisposable, IAsyncLifetime { /// @@ -38,7 +40,7 @@ public class MockServerFixture : IDisposable, IAsyncLifetime /// Handle to the . /// [NotNull] - protected MockServerContainer Container { get; } + internal MockServerContainer Container { get; } /// /// diff --git a/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationConverterTests.cs b/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationConverterTests.cs index 9e0038e1..dfe3069f 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationConverterTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationConverterTests.cs @@ -28,7 +28,7 @@ public void Should_Use_ExpectationConverter_When_Using_Standard_Deserializer() var builder = new FluentHttpRequestBuilder().WithPath("/some/path"); // Act - var unused =Verify.Once(builder.Build()); + var unused = new Verify(builder.Build(), VerificationTimes.Once); var expected = FluentExpectationBuilder.Create(httpRequest:builder.Build()); // Act From 39fdf7f59dcb8fdb0e01441829efe5003571661b Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 01:44:24 +0100 Subject: [PATCH 54/64] =?UTF-8?q?style:=20apply=20code=20styles=20?= =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Configuration/ConfigFile.cs | 1 - .../Configuration/ConfigurationFileType.cs | 2 +- .../Controllers/SetupController.cs | 2 +- .../ApplicationBuilderExtensions.cs | 5 +---- .../DependencyInjection/MockServerBuilder.cs | 2 +- .../MockServerBuilderExtensions.cs | 2 +- .../Extensions/StringExtensions.cs | 1 + .../Infrastructure/SetupRepository.cs | 1 - .../Logging/LogService.cs | 3 +-- .../Logging/LoggingInitializer.cs | 10 ++++++++-- .../Logging/Models/LogItem.cs | 2 +- .../NinjaTools.FluentMockServer.API.csproj | 3 +-- src/NinjaTools.FluentMockServer.API/Program.cs | 2 +- .../Proxy/Visitors/ComparasionVisitor.cs | 2 ++ .../Types/IStartupInitializer.cs | 4 ++-- .../Types/StartupInitializer.cs | 3 +-- .../Extensions/HttpResponseExtensions.cs | 2 -- .../Extensions/StringExtensions.cs | 14 ++++---------- .../HttpEntities/FluentHttpRequestBuilder.cs | 2 +- .../FluentAPI/IFluentExpectationBuilder.cs | 6 +++++- .../MockServerClient.cs | 5 +---- .../Models/HttpEntities/HttpError.cs | 1 - .../Models/HttpEntities/HttpRequest.cs | 2 -- .../Models/HttpEntities/HttpResponse.cs | 1 - .../Models/ValueTypes/Body.cs | 2 -- .../Models/ValueTypes/ConnectionOptions.cs | 1 - .../Models/ValueTypes/Delay.cs | 1 - .../Models/ValueTypes/LifeTime.cs | 1 - .../Models/ValueTypes/TimeUnit.cs | 1 - .../Models/ValueTypes/VerificationTimes.cs | 1 - src/NinjaTools.FluentMockServer/Models/Verify.cs | 1 - .../Serialization/CommonContentType.cs | 2 +- .../Utils/IMockServerLogger.cs | 5 ++--- .../Xunit/MockServerContainer.cs | 2 +- .../DependencyInjection/MockServerBuilderTests.cs | 1 + .../Services/LogServiceTests.cs | 7 +------ .../Builders/FluentExpectationBuilderTests.cs | 2 -- .../Builders/FluentVerificationBuilderTests.cs | 1 - .../HttpEntities/FluentRequestBuilderTests.cs | 1 - .../HttpEntities/FluentResponseBuilderTests.cs | 9 +-------- .../ContractResolverTests/ContractResolverTests.cs | 1 - .../Serialization/ExpectationSerializationTests.cs | 1 - .../HttpResponseSerializationTests.cs | 1 - .../TestHelpers/XUnitTestBase.cs | 7 +------ .../Xunit/VerificationTests.cs | 2 +- 45 files changed, 43 insertions(+), 85 deletions(-) diff --git a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFile.cs b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFile.cs index e5bc6e48..e273fa10 100644 --- a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFile.cs +++ b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigFile.cs @@ -34,7 +34,6 @@ public ConfigFile([NotNull] string path, [NotNull] params Setup[] setups) [YamlIgnore] public ConfigurationFileType? FileType { get; } - [NotNull] [Required] [JsonRequired] public ConfigurationCollection Configurations { get; } diff --git a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationFileType.cs b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationFileType.cs index 7cef5772..380feed0 100644 --- a/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationFileType.cs +++ b/src/NinjaTools.FluentMockServer.API/Configuration/ConfigurationFileType.cs @@ -4,7 +4,7 @@ namespace NinjaTools.FluentMockServer.API.Configuration { [JsonConverter(typeof(StringEnumConverter))] - public enum ConfigurationFileType : int + public enum ConfigurationFileType { yaml = 1, yml = 1, diff --git a/src/NinjaTools.FluentMockServer.API/Controllers/SetupController.cs b/src/NinjaTools.FluentMockServer.API/Controllers/SetupController.cs index 1f130327..d951cae5 100644 --- a/src/NinjaTools.FluentMockServer.API/Controllers/SetupController.cs +++ b/src/NinjaTools.FluentMockServer.API/Controllers/SetupController.cs @@ -37,7 +37,7 @@ public IEnumerable List() /// Configures a new on the Mock-Server. /// /// - /// The configured + /// The configured [HttpPost("create")] public Task Create([NotNull] Setup setup) { diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ApplicationBuilderExtensions.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ApplicationBuilderExtensions.cs index b3dd0ec9..1877b368 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/ApplicationBuilderExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/ApplicationBuilderExtensions.cs @@ -30,10 +30,7 @@ private static void UseMockServerMiddleware(this IApplicationBuilder app) private static void UseMockServerRouting(this IApplicationBuilder app) { app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); + app.UseEndpoints(endpoints => endpoints.MapControllers()); } private static void UseMockServerAdminBranch(this IApplicationBuilder app) diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs index e46ac3ba..855f14bf 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilder.cs @@ -29,7 +29,7 @@ public MockServerBuilder([NotNull] IServiceCollection services, [NotNull] IConfi Services.TryAddSingleton(); MvcCoreBuilder = Services - .AddMvcCore(options => { options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true; }) + .AddMvcCore(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true) .AddControllersAsServices() .AddAuthorization() .AddNewtonsoftJson(opt => diff --git a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilderExtensions.cs b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilderExtensions.cs index fd4f9139..6ee760a6 100644 --- a/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilderExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/DependencyInjection/MockServerBuilderExtensions.cs @@ -9,7 +9,7 @@ public static class MockServerBuilderExtensions [NotNull] public static IMockServerBuilder AddAdminPath([NotNull] this IMockServerBuilder builder, int port = 1080) { - builder.Services.Configure(opt => { opt.Port = port; }); + builder.Services.Configure(opt => opt.Port = port); return builder; } diff --git a/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs b/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs index 8a4036ee..4391dbe0 100644 --- a/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs +++ b/src/NinjaTools.FluentMockServer.API/Extensions/StringExtensions.cs @@ -13,6 +13,7 @@ public static bool IsValidRegex(this string pattern) try { + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed Regex.Match("", pattern); } catch (ArgumentException) diff --git a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs index 55355ef4..13c3dc19 100644 --- a/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs +++ b/src/NinjaTools.FluentMockServer.API/Infrastructure/SetupRepository.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs b/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs index 450808b7..f15945ac 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LogService.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; using Microsoft.Extensions.Logging; using NinjaTools.FluentMockServer.API.Infrastructure; using NinjaTools.FluentMockServer.API.Logging.Models; @@ -26,7 +25,7 @@ public LogService(ILogger logger, ILogRepository logRepository, LogF } /// - public void Log([NotNull] Func> logFactory) + public void Log(Func> logFactory) { var logItem = logFactory(_factory); _logger.LogInformation(logItem.ToFormattedString()); diff --git a/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs b/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs index 8ed3d81a..078906a7 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/LoggingInitializer.cs @@ -9,13 +9,17 @@ public class LoggingInitializer : IInitializer { private readonly IFileSystem _fileSystem; - public LoggingInitializer(IFileSystem fileSystem) + /// + /// Initializes a new instance of . + /// + /// + internal LoggingInitializer(IFileSystem fileSystem) { _fileSystem = fileSystem; } /// - public async Task InitializeAsync() + public Task InitializeAsync() { var setupLogsPath = MockServerPaths.Logs.Setups; var requestLogsPath = MockServerPaths.Logs.Requests; @@ -32,6 +36,8 @@ public async Task InitializeAsync() _fileSystem.Directory.Delete(requestLogsPath, true); } _fileSystem.Directory.CreateDirectory(requestLogsPath); + + return Task.CompletedTask; } } } diff --git a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs index 05d3b39a..d13ffe42 100644 --- a/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs +++ b/src/NinjaTools.FluentMockServer.API/Logging/Models/LogItem.cs @@ -67,7 +67,7 @@ private string SafeFormatHeader() var header = FormatHeader(); return header; } - catch (Exception e) + catch (Exception) { return GetType().Name; } diff --git a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj index 44dbd691..a4531794 100644 --- a/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj +++ b/src/NinjaTools.FluentMockServer.API/NinjaTools.FluentMockServer.API.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - warnings + enable Preview 1.0.0 1.0.0 @@ -10,7 +10,6 @@ - netstandard2.1;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1 bin\Release\NinjaTools.FluentMockServer.API.xml diff --git a/src/NinjaTools.FluentMockServer.API/Program.cs b/src/NinjaTools.FluentMockServer.API/Program.cs index 22ab3516..4d81aec4 100644 --- a/src/NinjaTools.FluentMockServer.API/Program.cs +++ b/src/NinjaTools.FluentMockServer.API/Program.cs @@ -12,6 +12,6 @@ public static void Main(string[] args) public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); + .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()); } } diff --git a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs index 1c97446a..312853fd 100644 --- a/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs +++ b/src/NinjaTools.FluentMockServer.API/Proxy/Visitors/ComparasionVisitor.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading; @@ -10,6 +11,7 @@ namespace NinjaTools.FluentMockServer.API.Proxy.Visitors { /// + [SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")] public sealed class ComparasionVisitor : IVisitor, IVisitor, IVisitor, diff --git a/src/NinjaTools.FluentMockServer.API/Types/IStartupInitializer.cs b/src/NinjaTools.FluentMockServer.API/Types/IStartupInitializer.cs index b48469cb..9f7086fe 100644 --- a/src/NinjaTools.FluentMockServer.API/Types/IStartupInitializer.cs +++ b/src/NinjaTools.FluentMockServer.API/Types/IStartupInitializer.cs @@ -3,9 +3,9 @@ namespace NinjaTools.FluentMockServer.API.Types { /// - /// Initializes registered at startup. + /// Initializes registered at startup. /// - public interface IStartupInitializer : IInitializer + internal interface IStartupInitializer : IInitializer { /// /// Registers a diff --git a/src/NinjaTools.FluentMockServer.API/Types/StartupInitializer.cs b/src/NinjaTools.FluentMockServer.API/Types/StartupInitializer.cs index a1e839d7..97d172ec 100644 --- a/src/NinjaTools.FluentMockServer.API/Types/StartupInitializer.cs +++ b/src/NinjaTools.FluentMockServer.API/Types/StartupInitializer.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Threading.Tasks; -using JetBrains.Annotations; using Microsoft.Extensions.Logging; namespace NinjaTools.FluentMockServer.API.Types @@ -27,7 +26,7 @@ public async Task InitializeAsync() } /// - public void AddInitializer([NotNull] TInitializer initializer) where TInitializer : class, IInitializer + public void AddInitializer(TInitializer initializer) where TInitializer : class, IInitializer { if (_initializers.Contains(initializer)) { diff --git a/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs b/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs index 983953c8..e952f20e 100644 --- a/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs +++ b/src/NinjaTools.FluentMockServer/Extensions/HttpResponseExtensions.cs @@ -13,9 +13,7 @@ internal static class HttpResponseExtensions public static void EnsureSuccessfulMockServerOperation(this HttpResponseMessage responseMessage, [CallerMemberName] string caller = null) { if (!responseMessage.IsSuccessStatusCode) - { throw new MockServerOperationFailedException(caller); - } } } } diff --git a/src/NinjaTools.FluentMockServer/Extensions/StringExtensions.cs b/src/NinjaTools.FluentMockServer/Extensions/StringExtensions.cs index b1d67801..9bc2328d 100644 --- a/src/NinjaTools.FluentMockServer/Extensions/StringExtensions.cs +++ b/src/NinjaTools.FluentMockServer/Extensions/StringExtensions.cs @@ -1,23 +1,17 @@ +using System.Linq; + namespace NinjaTools.FluentMockServer.Extensions { internal static class StringExtensions { public static bool StartsWithEither(this string @string, params char[] chars) { - foreach (var @char in chars) - if (@string.StartsWith(@char.ToString())) - return true; - - return false; + return chars.Any(@char => @string.StartsWith(@char.ToString())); } public static bool EndsWithEither(this string @string, params char[] chars) { - foreach (var @char in chars) - if (@string.EndsWith(@char.ToString())) - return true; - - return false; + return chars.Any(@char => @string.EndsWith(@char.ToString())); } public static bool IsContainerByEither(this string @string, params char[] chars) diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/HttpEntities/FluentHttpRequestBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/HttpEntities/FluentHttpRequestBuilder.cs index 18946b84..f30c5710 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/HttpEntities/FluentHttpRequestBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/HttpEntities/FluentHttpRequestBuilder.cs @@ -19,7 +19,7 @@ internal sealed class FluentHttpRequestBuilder : IFluentHttpRequestBuilder private bool? _keepAlive; [CanBeNull] - private Dictionary Cookies { get; set; } + private Dictionary Cookies { get; } /// public IFluentHttpRequestBuilder WithMethod(HttpMethod method) diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs index 3aed5f41..36d0e576 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs @@ -7,6 +7,7 @@ namespace NinjaTools.FluentMockServer.FluentAPI { + /// /// /// [EditorBrowsable(EditorBrowsableState.Never)] @@ -16,7 +17,8 @@ public interface IBlankExpectation : IFluentInterface IWithRequest OnHandling(HttpMethod method = null, [CanBeNull] Action requestFactory = null); IWithRequest OnHandlingAny([CanBeNull] HttpMethod method = null); } - + + /// /// /// [EditorBrowsable(EditorBrowsableState.Never)] @@ -25,6 +27,7 @@ public interface IFluentExpectationBuilder : IBlankExpectation , IWithRequest, I { } + /// /// /// [EditorBrowsable(EditorBrowsableState.Never)] @@ -36,6 +39,7 @@ public interface IWithResponse : IFluentInterface MockServerSetup Setup(); } + /// /// /// [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index 1389e75d..7a544236 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -121,10 +121,7 @@ public async Task ResetAsync() [NotNull] public Task VerifyAsync([NotNull] Action verify, [CanBeNull] VerificationTimes times = null) { - if (times is null) - return VerifyAsync(v => v.Verify(verify)); - else - return VerifyAsync(v => v.Verify(verify).Times(times)); + return times is null ? VerifyAsync(v => v.Verify(verify)) : VerifyAsync(v => v.Verify(verify).Times(times)); } /// diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs index 9c358266..10a1e4db 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpError.cs @@ -1,4 +1,3 @@ -using System; using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models.ValueTypes; diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs index 65f9b0e3..8eca3bf0 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs @@ -1,8 +1,6 @@ -using System; using System.Collections.Generic; using JetBrains.Annotations; using Newtonsoft.Json.Linq; -using NinjaTools.FluentMockServer.Models.ValueTypes; namespace NinjaTools.FluentMockServer.Models.HttpEntities { diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs index b29677d1..3cb8b5d2 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpResponse.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Net; using JetBrains.Annotations; diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs index 309da3f8..d67f8e3f 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Body.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs index df733a81..bd011265 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/ConnectionOptions.cs @@ -1,4 +1,3 @@ -using System; using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models.HttpEntities; diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs index 270db77d..6016cbda 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/Delay.cs @@ -1,4 +1,3 @@ -using System; using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models.HttpEntities; diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs index e7a53bd0..e35b7546 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/LifeTime.cs @@ -1,4 +1,3 @@ -using System; using JetBrains.Annotations; namespace NinjaTools.FluentMockServer.Models.ValueTypes diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/TimeUnit.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/TimeUnit.cs index 11447498..0502e3ab 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/TimeUnit.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/TimeUnit.cs @@ -1,4 +1,3 @@ -using System; using JetBrains.Annotations; using Newtonsoft.Json; using NinjaTools.FluentMockServer.Serialization; diff --git a/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs b/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs index fb3b2464..17cb5a9c 100644 --- a/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs +++ b/src/NinjaTools.FluentMockServer/Models/ValueTypes/VerificationTimes.cs @@ -1,4 +1,3 @@ -using System; using JetBrains.Annotations; using Newtonsoft.Json; diff --git a/src/NinjaTools.FluentMockServer/Models/Verify.cs b/src/NinjaTools.FluentMockServer/Models/Verify.cs index 81a9b4aa..1fe97ceb 100644 --- a/src/NinjaTools.FluentMockServer/Models/Verify.cs +++ b/src/NinjaTools.FluentMockServer/Models/Verify.cs @@ -1,4 +1,3 @@ -using System; using JetBrains.Annotations; using NinjaTools.FluentMockServer.Models.HttpEntities; using NinjaTools.FluentMockServer.Models.ValueTypes; diff --git a/src/NinjaTools.FluentMockServer/Serialization/CommonContentType.cs b/src/NinjaTools.FluentMockServer/Serialization/CommonContentType.cs index e5f33147..0c85c50c 100644 --- a/src/NinjaTools.FluentMockServer/Serialization/CommonContentType.cs +++ b/src/NinjaTools.FluentMockServer/Serialization/CommonContentType.cs @@ -81,7 +81,7 @@ public static bool TryParseContentType([NotNull] string input, [CanBeNull] out C contentType = null; var list = ToList(); - if (!list.Any(ct => ct.Name == input)) return false; + if (list.All(ct => ct.Name != input)) return false; contentType = list.First(ct => ct.Name == input); return true; } diff --git a/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs b/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs index e9c8d677..7711ed01 100644 --- a/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs +++ b/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs @@ -1,13 +1,12 @@ using System; -using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; namespace NinjaTools.FluentMockServer.Utils { public interface IMockServerLogger { - void WriteLine([JetBrains.Annotations.NotNull] string message, params object[] args); - void Error([JetBrains.Annotations.NotNull] string message, params object[] args); + void WriteLine([NotNull] string message, params object[] args); + void Error([NotNull] string message, params object[] args); } internal class MockServerTestLogger : IMockServerLogger diff --git a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs index 7a05608b..aa29da9b 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs @@ -104,7 +104,7 @@ private async Task WaitUntilContainerStarted() return; } } - catch (Exception e) + catch (Exception) { await Task.Delay(TimeSpan.FromMilliseconds(100)); } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs index 4950bc3f..de4a4023 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/DependencyInjection/MockServerBuilderTests.cs @@ -34,6 +34,7 @@ private IServiceCollection GetServiceCollection(out IConfiguration config) config = configBuilder.Build(); var services = new ServiceCollection(); services.AddSingleton(config); + // ReSharper disable once RedundantTypeArgumentsOfMethod services.AddSingleton(config); return services; } diff --git a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs index df6c8f51..a91cd823 100644 --- a/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.API.Tests/Services/LogServiceTests.cs @@ -23,12 +23,7 @@ private ILogService CreateSubject(out Mock repo, GenerateId idGe { repo = new Mock(); - if (idGenerator != null) - { - return new LogService(CreateLogger(),repo.Object ,new LogFactory(idGenerator)); - } - - return new LogService(CreateLogger(), repo.Object); + return idGenerator != null ? new LogService(CreateLogger(),repo.Object ,new LogFactory(idGenerator)) : new LogService(CreateLogger(), repo.Object); } public const string Id = "1234"; diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs index c66b8029..52782e7a 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs @@ -5,8 +5,6 @@ using System.Threading.Tasks; using FluentAssertions; using Moq; -using Newtonsoft.Json; -using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI; using NinjaTools.FluentMockServer.FluentAPI.Builders; using NinjaTools.FluentMockServer.Models.ValueTypes; diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentVerificationBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentVerificationBuilderTests.cs index 02efb4d5..a968220c 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentVerificationBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentVerificationBuilderTests.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI.Builders; using NinjaTools.FluentMockServer.Serialization; using Xunit; diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentRequestBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentRequestBuilderTests.cs index 0a7febfe..64cbf760 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentRequestBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentRequestBuilderTests.cs @@ -1,5 +1,4 @@ using FluentAssertions; -using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI.Builders.HttpEntities; using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Tests.TestHelpers; diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentResponseBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentResponseBuilderTests.cs index e0f64a18..c0acf83d 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentResponseBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/HttpEntities/FluentResponseBuilderTests.cs @@ -1,20 +1,13 @@ using System.Collections.Generic; -using System.Net; -using System.Net.Http.Headers; -using System.Net.Mime; -using System.Text; using FluentAssertions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Schema; using NinjaTools.FluentMockServer.FluentAPI; using NinjaTools.FluentMockServer.FluentAPI.Builders.HttpEntities; -using NinjaTools.FluentMockServer.Models.ValueTypes; using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; using Xunit.Abstractions; -using static NinjaTools.FluentMockServer.Tests.TestHelpers.FileSystem; namespace NinjaTools.FluentMockServer.Tests.FluentAPI.Builders.HttpEntities { @@ -90,7 +83,7 @@ public void Should_Add_Multiple_Header_Values() .Build()); - Output.WriteLine("JSON", response.ToString(Formatting.Indented)); + Dump("JSON", response.ToString(Formatting.Indented)); // Assert diff --git a/test/NinjaTools.FluentMockServer.Tests/Serialization/ContractResolverTests/ContractResolverTests.cs b/test/NinjaTools.FluentMockServer.Tests/Serialization/ContractResolverTests/ContractResolverTests.cs index 016d5c20..7c3e09b2 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Serialization/ContractResolverTests/ContractResolverTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Serialization/ContractResolverTests/ContractResolverTests.cs @@ -2,7 +2,6 @@ using System.Linq; using FluentAssertions; using Newtonsoft.Json.Serialization; -using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Tests.TestHelpers; using Xunit; diff --git a/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationSerializationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationSerializationTests.cs index 8f5e2490..94afd40f 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationSerializationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Serialization/ExpectationSerializationTests.cs @@ -3,7 +3,6 @@ using FluentAssertions; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; -using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI.Builders; using NinjaTools.FluentMockServer.FluentAPI.Builders.HttpEntities; using NinjaTools.FluentMockServer.Models.HttpEntities; diff --git a/test/NinjaTools.FluentMockServer.Tests/Serialization/HttpResponseSerializationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Serialization/HttpResponseSerializationTests.cs index 15029992..e7f0009a 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Serialization/HttpResponseSerializationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Serialization/HttpResponseSerializationTests.cs @@ -1,6 +1,5 @@ using FluentAssertions; using Newtonsoft.Json.Linq; -using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.Models.HttpEntities; using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Tests.TestHelpers; diff --git a/test/NinjaTools.FluentMockServer.Tests/TestHelpers/XUnitTestBase.cs b/test/NinjaTools.FluentMockServer.Tests/TestHelpers/XUnitTestBase.cs index 28676e20..e2339b1f 100644 --- a/test/NinjaTools.FluentMockServer.Tests/TestHelpers/XUnitTestBase.cs +++ b/test/NinjaTools.FluentMockServer.Tests/TestHelpers/XUnitTestBase.cs @@ -37,12 +37,7 @@ protected virtual string Dump([CanBeNull] T? value, [CanBeNull] string? heade return str; } - if (value is null) - { - return $"{typeof(T).Name} is null!"; - } - - return JToken.FromObject(v).ToString(Formatting.Indented); + return value is null ? $"{typeof(T).Name} is null!" : JToken.FromObject(v).ToString(Formatting.Indented); }; diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs index f611ddc4..b755adbe 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs @@ -30,7 +30,7 @@ public async Task VerifyAsync_Should_Return_True_When_MockServer_Recieved_Matchi } [Fact] - public async Task VerifyAsync_Should_Return_False_When_MockServer_Recieved_No_Matching_Setup() + public void VerifyAsync_Should_Return_False_When_MockServer_Recieved_No_Matching_Setup() { // Act Func action = async () => await MockClient.VerifyAsync(v => v.WithPath("test"), VerificationTimes.Once); From 106c1a33c4a94412098da33a6ad22119ba34954b Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 02:37:16 +0100 Subject: [PATCH 55/64] =?UTF-8?q?feat(client):=20add=20concurrency=20proof?= =?UTF-8?q?=20mock=20context=20isolation=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/HttpExtensions.cs | 20 +++++ .../MockServerClient.cs | 24 ++++-- .../NinjaTools.FluentMockServer.csproj | 1 + .../Utils/IMockServerLogger.cs | 31 ++++--- .../IsolatedMockContextAttribute.cs | 20 +++++ .../MockServerCollectionAttribute.cs | 17 ++++ .../Xunit/ContextRegistry.cs | 43 ++++++++++ .../Xunit/GlobalSetup.cs | 78 ++++++++++++++++++ .../Xunit/MockServerContainer.cs | 37 +++++++-- .../Xunit/MockServerContext.cs | 67 +++++++++++++++ .../Xunit/MockServerFixture.cs | 55 ++++--------- .../Builders/FluentExpectationBuilderTests.cs | 4 +- .../Xunit/LoggerTests.cs | 39 +++++++++ .../Xunit/MockServerContextTests.cs | 70 ++++++++++++++++ .../Xunit/MockServerFixtureTests.cs | 48 ++++++----- .../Xunit/MockTestFrameworkBase.cs | 82 +++++++++++++++++++ .../Xunit/SoapServiceTests.cs | 2 +- .../Xunit/VerificationTests.cs | 19 ++--- 18 files changed, 558 insertions(+), 99 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer/Extensions/HttpExtensions.cs create mode 100644 src/NinjaTools.FluentMockServer/Xunit/Attributes/IsolatedMockContextAttribute.cs create mode 100644 src/NinjaTools.FluentMockServer/Xunit/Attributes/MockServerCollectionAttribute.cs create mode 100644 src/NinjaTools.FluentMockServer/Xunit/ContextRegistry.cs create mode 100644 src/NinjaTools.FluentMockServer/Xunit/GlobalSetup.cs create mode 100644 src/NinjaTools.FluentMockServer/Xunit/MockServerContext.cs create mode 100644 test/NinjaTools.FluentMockServer.Tests/Xunit/LoggerTests.cs create mode 100644 test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs create mode 100644 test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs diff --git a/src/NinjaTools.FluentMockServer/Extensions/HttpExtensions.cs b/src/NinjaTools.FluentMockServer/Extensions/HttpExtensions.cs new file mode 100644 index 00000000..4a575b60 --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Extensions/HttpExtensions.cs @@ -0,0 +1,20 @@ +using System.Linq; +using System.Net.Http; +using JetBrains.Annotations; + +public static class HttpExtensions +{ + public const string MockContextHeaderKey = "X-MockServer-Context"; + + public static bool TryGetMockContextHeader([NotNull] this HttpRequestMessage message, out string value) + { + if (message.Headers.TryGetValues(MockContextHeaderKey, out var values)) + { + value = values.First(); + return true; + } + + value = null; + return false; + } +} diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index 7a544236..4f8f2671 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -37,13 +37,31 @@ public class MockServerClient : IDisposable { private readonly IMockServerLogger _logger; + [NotNull] + public HttpClient HttpClient + { + get + { + if (!string.IsNullOrWhiteSpace(Context)) + { + _httpClient.DefaultRequestHeaders.Add(HttpExtensions.MockContextHeaderKey, Context); + } + + return _httpClient; + } + } + + private readonly HttpClient _httpClient; + + public string? Context { get; internal set; } + public MockServerClient([NotNull] HttpClient client, [NotNull] string hostname, [NotNull] IMockServerLogger logger, string? context = null) { _logger = logger; client.BaseAddress = new Uri(hostname); client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Host = null; - HttpClient = client; + _httpClient = client; Context = context; } @@ -53,10 +71,6 @@ public MockServerClient([NotNull] string mockServerEndpoint, [NotNull] IMockServ { } - public HttpClient HttpClient { get; } - - public string? Context { get; } - /// /// Configures the MockServer Client. diff --git a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj index f7cd93d6..2f6cd755 100644 --- a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj +++ b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj @@ -53,6 +53,7 @@ + diff --git a/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs b/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs index 7711ed01..59318494 100644 --- a/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs +++ b/src/NinjaTools.FluentMockServer/Utils/IMockServerLogger.cs @@ -9,20 +9,31 @@ public interface IMockServerLogger void Error([NotNull] string message, params object[] args); } - internal class MockServerTestLogger : IMockServerLogger + public class NullMockServerLogger : IMockServerLogger { - /// - /// Gets the default . - /// - public static IMockServerLogger Instance => Factory.Value; - - private MockServerTestLogger(){} - private static readonly Lazy Factory; - static MockServerTestLogger() + private static readonly Lazy _factory = new Lazy(() => new NullMockServerLogger()); + + public static IMockServerLogger Instance => _factory.Value; + + /// + public void WriteLine(string message, params object[] args) { - Factory = new Lazy(() => new MockServerTestLogger()); + // Null implementation } + /// + public void Error(string message, params object[] args) + { + // Null implementation + } + } + + public class MockServerTestLogger : IMockServerLogger + { + private static readonly Lazy _factory = new Lazy(() => new MockServerTestLogger()); + + public static IMockServerLogger Instance => _factory.Value; + /// public void WriteLine(string message, [CanBeNull] params object[] args) { diff --git a/src/NinjaTools.FluentMockServer/Xunit/Attributes/IsolatedMockContextAttribute.cs b/src/NinjaTools.FluentMockServer/Xunit/Attributes/IsolatedMockContextAttribute.cs new file mode 100644 index 00000000..c2b2a66f --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Xunit/Attributes/IsolatedMockContextAttribute.cs @@ -0,0 +1,20 @@ +using System; +using NinjaTools.FluentMockServer.Models; +using Xunit.Sdk; + +namespace NinjaTools.FluentMockServer.Xunit.Attributes +{ + /// + /// Unit tests marked with this will create inside an isolated + /// Context. + /// + [AttributeUsage(AttributeTargets.Method)] + public class IsolatedMockServerSetupAttribute : BeforeAfterTestAttribute + { + } + + + namespace NinjaTools.FluentMockServer.Xunit.Attributes + { + } +} diff --git a/src/NinjaTools.FluentMockServer/Xunit/Attributes/MockServerCollectionAttribute.cs b/src/NinjaTools.FluentMockServer/Xunit/Attributes/MockServerCollectionAttribute.cs new file mode 100644 index 00000000..46cddf4b --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Xunit/Attributes/MockServerCollectionAttribute.cs @@ -0,0 +1,17 @@ +using System; +using Xunit.Sdk; + +namespace NinjaTools.FluentMockServer.Xunit.Attributes.NinjaTools.FluentMockServer.Xunit.Attributes +{ + [AttributeUsage(AttributeTargets.Method)] + public class MockServerCollectionAttribute : BeforeAfterTestAttribute + { + public string Id { get; } + + /// + public MockServerCollectionAttribute(string collection) : base() + { + Id = collection; + } + } +} diff --git a/src/NinjaTools.FluentMockServer/Xunit/ContextRegistry.cs b/src/NinjaTools.FluentMockServer/Xunit/ContextRegistry.cs new file mode 100644 index 00000000..b9898928 --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Xunit/ContextRegistry.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Reflection; +using JetBrains.Annotations; +using Xunit; + +namespace NinjaTools.FluentMockServer.Xunit +{ + public class ContextRegistry + { + private static readonly Lazy _factory = new Lazy(() => new ContextRegistry()); + public static ContextRegistry Instance => _factory.Value; + private ContextRegistry(){} + + public ConcurrentDictionary Collections { get; } = new ConcurrentDictionary(); + public ConcurrentBag Isolated { get; } = new ConcurrentBag(); + + internal bool TryGetIdentifier(MethodInfo mi, out string id) + { + if (!Isolated.Contains(mi)) + return Collections.TryGetValue(mi, out id); + id = XunitContext.Context.UniqueTestName; + return true; + } + + internal void RegisterIsolated([NotNull] ConcurrentBag isolated ) + { + foreach (var methodInfo in isolated) + { + Isolated.Add(methodInfo); + } + } + + internal void RegisterCollections([NotNull] ConcurrentDictionary collections) + { + foreach (var kvp in collections) + { + Collections.TryAdd(kvp.Key, kvp.Value); + } + } + } +} diff --git a/src/NinjaTools.FluentMockServer/Xunit/GlobalSetup.cs b/src/NinjaTools.FluentMockServer/Xunit/GlobalSetup.cs new file mode 100644 index 00000000..3ee0d6ae --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Xunit/GlobalSetup.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Reflection; +using JetBrains.Annotations; +using NinjaTools.FluentMockServer.Xunit.Attributes; +using NinjaTools.FluentMockServer.Xunit.Attributes.NinjaTools.FluentMockServer.Xunit.Attributes; +using Xunit; + +namespace NinjaTools.FluentMockServer.Xunit +{ + [GlobalSetUp] + public static class GlobalSetup + { + [UsedImplicitly] + public static void Setup() + { + Called = true; + ContextRegistry.Instance.RegisterCollections(ResolveCollections()); + ContextRegistry.Instance.RegisterIsolated(ResolveIsolated()); + } + + [NotNull] + private static ConcurrentDictionary ResolveCollections() + { + var dict = new ConcurrentDictionary(); + + var types = AppDomain.CurrentDomain + .GetAssemblies() + .Where(a => a.FullName.StartsWith("Ninja")) + .SelectMany(a => a.GetExportedTypes()) + .ToList(); + + var isolated = types + .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Instance)) + .Where(t => t.IsDefined(typeof(MockServerCollectionAttribute))) + .ToList(); + + foreach (var method in isolated) + { + var attribute = method.GetCustomAttribute(); + dict.TryAdd(method, attribute.Id); + } + + return dict; + } + + + [NotNull] + private static ConcurrentBag ResolveIsolated() + { + var dict = new ConcurrentBag(); + + var types = AppDomain.CurrentDomain + .GetAssemblies() + .Where(a => a.FullName.StartsWith("Ninja")) + .SelectMany(a => a.GetExportedTypes()) + .ToList(); + + var isolated = types + .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Instance)) + .Where(t => t.IsDefined(typeof(IsolatedMockServerSetupAttribute))) + .ToList(); + + foreach (var method in isolated) + { + dict.Add(method); + } + + return dict; + } + + /// + /// Whether the has already been executed. + /// + public static bool Called; + } +} diff --git a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs index aa29da9b..d87210e7 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs @@ -14,6 +14,10 @@ namespace NinjaTools.FluentMockServer.Xunit /// internal class MockServerContainer : IDisposable { + public const string Prefix = "NinjaTools-FluentMockServer"; + public const string Suffix = "netcore-xunit-background-runner"; + public static string ContainerName = $"{Prefix}-{Suffix}"; + /// /// Gets the Port exposed to the Host. /// @@ -55,13 +59,29 @@ private static int GetAvailablePort(int startingPort) public MockServerContainer() { - HostPort = GetAvailablePort(3000); + var hosts = new Hosts().Discover(true); + var docker = hosts.FirstOrDefault(x => x.IsNative) ?? hosts.FirstOrDefault(x => x.Name == "default"); - ContainerService = new Builder() - .UseContainer() - .UseImage(ContainerImage) - .ExposePort(HostPort, ContainerPort) - .Build(); + if (docker?.GetRunningContainers().FirstOrDefault(c => c.Name == ContainerName) is {} container) + { + ContainerService = container; + if (ContainerService.GetConfiguration().NetworkSettings.Ports.TryGetValue("1080/tcp", out var endpoints)) + { + HostPort = int.Parse(endpoints.First().HostPort); + } + } + else + { + HostPort = GetAvailablePort(3000); + + ContainerService = new Builder() + .UseContainer() + .ReuseIfExists() + .WithName(ContainerName) + .UseImage(ContainerImage) + .ExposePort(HostPort, ContainerPort) + .Build(); + } } @@ -117,10 +137,10 @@ private async Task WaitUntilContainerStarted() /// /// Starts the MockServer. /// - public async Task StartAsync() + public Task StartAsync() { ContainerService.Start(); - await WaitUntilContainerStarted(); + return WaitUntilContainerStarted(); } /// @@ -135,7 +155,6 @@ public Task StopAsync() /// public void Dispose() { - ContainerService.Dispose(); } } } diff --git a/src/NinjaTools.FluentMockServer/Xunit/MockServerContext.cs b/src/NinjaTools.FluentMockServer/Xunit/MockServerContext.cs new file mode 100644 index 00000000..53d5faca --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Xunit/MockServerContext.cs @@ -0,0 +1,67 @@ +using System; +using System.Net.Http; +using System.Reflection; +using JetBrains.Annotations; +using NinjaTools.FluentMockServer.Utils; +using Xunit; + +namespace NinjaTools.FluentMockServer.Xunit +{ + public class MockServerContext + { + [NotNull] + public static MockServerContext Establish() + { + var context = new MockServerContext(); + + if (ContextRegistry.Instance.TryGetIdentifier(context.TestMethod, out var id)) + { + context.MockClient = new MockServerClient(Container.MockServerBaseUrl, MockServerTestLogger.Instance, id); + return context; + } + + context.MockClient = new MockServerClient(Container.MockServerBaseUrl, MockServerTestLogger.Instance); + return context; + } + + /// + /// Gets the . + /// + [NotNull] + public MockServerClient MockClient { get; private set; } + + [CanBeNull] + public string? Id => MockClient.Context; + + [NotNull] + public string Name => XunitContext.Context.UniqueTestName; + + [NotNull] + public MethodInfo TestMethod => XunitContext.Context.MethodInfo; + + /// + /// Handle to the . + /// + [NotNull] + internal static MockServerContainer Container => _container.Value; + + /// + /// Gets the of the . + /// + [NotNull] + public HttpClient HttpClient => MockClient.HttpClient; + + /// + /// The port used to connect the . + /// + public int MockServerPort => Container.HostPort; + + /// + /// The host used to connect the . + /// + [NotNull] + public string MockServerBaseUrl => Container.MockServerBaseUrl; + + private static readonly Lazy _container = new Lazy(() => new MockServerContainer()); + } +} diff --git a/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs b/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs index a741a818..210175c6 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/MockServerFixture.cs @@ -1,9 +1,9 @@ using System; -using System.Net.Http; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using JetBrains.Annotations; -using NinjaTools.FluentMockServer.Utils; using Xunit; +using Xunit.Abstractions; namespace NinjaTools.FluentMockServer.Xunit { @@ -14,60 +14,37 @@ namespace NinjaTools.FluentMockServer.Xunit [PublicAPI] public class MockServerFixture : IDisposable, IAsyncLifetime { - /// - /// Gets the . - /// [NotNull] - public MockServerClient MockClient { get; } + public MockServerContext Context { get; private set; } - /// - /// Gets the of the . - /// - - public HttpClient HttpClient => MockClient.HttpClient; - - /// - /// The port used to connect the . - /// - public int MockServerPort => Container.HostPort; - - /// - /// The host used to connect the . - /// - public string MockServerBaseUrl => Container.MockServerBaseUrl; - - /// - /// Handle to the . - /// [NotNull] - internal MockServerContainer Container { get; } - - /// - /// - /// - public MockServerFixture() + public MockServerContext Register(ITestOutputHelper testOutputHelper, [CallerFilePath] string? sources = null) { - Container = new MockServerContainer(); - MockClient = new MockServerClient(Container.MockServerBaseUrl, MockServerTestLogger.Instance); + XunitContext.Register(testOutputHelper); + Context = MockServerContext.Establish(); + return Context; } - + /// public void Dispose() { - DisposeAsync().GetAwaiter().GetResult(); + Context.MockClient.Dispose(); + MockServerContext.Container.Dispose(); } /// + [NotNull] public Task InitializeAsync() { - return Container.StartAsync(); + return MockServerContext.Container.StartAsync(); } /// - public Task DisposeAsync() + public async Task DisposeAsync() { - MockClient.Dispose(); - return Container.StopAsync(); + await Context.MockClient.ResetAsync(); + MockServerContext.Container.Dispose(); } + } } diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs index 52782e7a..73d054f5 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs @@ -166,8 +166,8 @@ public async Task Should_Build_Expectation() { // Arrange var handler = new MockHandler(_outputHelper); - var mockServerClient = new MockServerClient(new HttpClient(handler),"http://localhost:9000" , Mock.Of()); - + var mockServerClient = new MockServerClient(new HttpClient(handler),"http://localhost:9000" ,NullMockServerLogger.Instance); + // Act await mockServerClient.SetupAsync( builder => builder diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/LoggerTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/LoggerTests.cs new file mode 100644 index 00000000..d1fd7db0 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/LoggerTests.cs @@ -0,0 +1,39 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using FluentAssertions; +using JetBrains.Annotations; +using NinjaTools.FluentMockServer.Extensions; +using NinjaTools.FluentMockServer.Xunit; +using NinjaTools.FluentMockServer.Xunit.Attributes; +using Xunit; +using Xunit.Abstractions; + +[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = false)] +namespace NinjaTools.FluentMockServer.Tests.Xunit +{ + public class LoggerTests : MockServerTestBase, IDisposable + { + /// + public LoggerTests([NotNull] MockServerFixture fixture, ITestOutputHelper output) : base(fixture, output) + { + } + + [Fact] + public async Task Should_Log_Messages() + { + // Act + await MockClient.ResetAsync(); + + // Assert + var logs = XunitContext.Logs; + logs.Should().Contain("Resetting MockServer..."); + } + + /// + public void Dispose() + { + XunitContext.Flush(); + } + } +} diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs new file mode 100644 index 00000000..af848cb1 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs @@ -0,0 +1,70 @@ +using FluentAssertions; +using JetBrains.Annotations; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using NinjaTools.FluentMockServer.Xunit; +using NinjaTools.FluentMockServer.Xunit.Attributes; +using NinjaTools.FluentMockServer.Xunit.Attributes.NinjaTools.FluentMockServer.Xunit.Attributes; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.Tests.Xunit +{ + public class MockServerContextTests : XUnitTestBase, IClassFixture + { + [NotNull] public MockServerFixture Fixture { get; } + + /// + public MockServerContextTests([NotNull] MockServerFixture fixture, ITestOutputHelper output) : base(output) + { + Fixture = fixture; + } + + [Fact] + public void Name_Should_Be_Unique_TestClassTestMethod_Name() + { + // Arrange & Act + var context = Fixture.Register(Output); + Output.WriteLine($"Name={context.Name};"); + + // Assert + context.Name.Should().Be(XunitContext.Context.UniqueTestName); + } + + [Fact] + public void Id_Should_Be_Null_When_No_Context_Specififed() + { + // Arrange & Act + var context = Fixture.Register(Output); + Output.WriteLine($"Id={context.Id};"); + + // Assert + context.Id.Should().BeNullOrEmpty(); + } + + [Fact] + [IsolatedMockServerSetup] + public void Id_Should_Equal_Name_When_Isolated() + { + // Arrange & Act + var context = Fixture.Register(Output); + Output.WriteLine($"Id={context.Id};"); + + // Assert + context.Id.Should().Be(XunitContext.Context.UniqueTestName); + } + + + [Fact] + [MockServerCollection("feat(client): concurrent context domains")] + public void Id_Should_Be_Equal_To_The_CollectionAttribute_Value() + { + // Arrange & Act + var context = Fixture.Register(Output); + Output.WriteLine($"Id={context.Id};"); + + // Assert + context.Id.Should().Be("feat(client): concurrent context domains"); + } + + } +} diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs index 2b56b4c3..4db097e1 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs @@ -4,12 +4,14 @@ using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; +using JetBrains.Annotations; using NinjaTools.FluentMockServer.FluentAPI; using NinjaTools.FluentMockServer.FluentAPI.Builders; using NinjaTools.FluentMockServer.FluentAPI.Builders.HttpEntities; using NinjaTools.FluentMockServer.Models.ValueTypes; using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Xunit; +using NinjaTools.FluentMockServer.Xunit.Attributes; using Xunit; using Xunit.Abstractions; @@ -18,32 +20,33 @@ namespace NinjaTools.FluentMockServer.Tests.Xunit public class MockServerFixtureTests : MockServerTestBase { /// - public MockServerFixtureTests(MockServerFixture fixture, ITestOutputHelper output) : base(fixture, output) + public MockServerFixtureTests([NotNull] MockServerFixture fixture, ITestOutputHelper output) : base(fixture, output) { } [Fact] public async Task Should_Reset_Expectation_On_MockServer() { + // Arrange - await MockClient.SetupAsync(exp => - exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) - .Setup()); + await MockClient.SetupAsync(exp => + exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) + .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .Setup()); // Act await MockClient.ResetAsync(); - // Assert - var response = await HttpClient.GetAsync("test"); + // Assert + var response = await HttpClient.GetAsync("test"); + + response.StatusCode.Should().Be(HttpStatusCode.NotFound); - response.StatusCode.Should().Be(HttpStatusCode.NotFound); } [Fact] public async Task Should_Successfully_Setup_Up_Expectation_On_MockServer() { // Arrange - await MockClient.SetupAsync(exp => exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) @@ -71,32 +74,33 @@ public async Task Should_Successfully_Setup_Up_Expectation_On_MockServer2() } // Act - var expectation = factory(builder).Expectations.First(); Output.WriteLine(Serializer.Serialize(expectation)); await MockClient.SetupAsync(factory); - var response = await HttpClient.GetAsync("test"); + var response = await HttpClient.GetAsync("/test"); response.StatusCode.Should().Be(HttpStatusCode.Created); } [Fact] + [IsolatedMockServerSetup] public async Task Should_Verify_Expectation_Was_Met_On_MockServer() { // Arrange await MockClient.SetupAsync(exp => - exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) - .Setup()); - - await HttpClient.GetAsync("test"); - - var builder = new FluentHttpRequestBuilder(); - builder.WithMethod(HttpMethod.Get).WithPath("/test"); - - // Act - await MockClient.VerifyAsync(v => v.WithPath("/test").WithMethod(HttpMethod.Get), VerificationTimes.Once); + exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) + .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .Setup()); + + await HttpClient.GetAsync("test"); + var builder = new FluentHttpRequestBuilder(); + builder.WithMethod(HttpMethod.Get).WithPath("/test"); + + // Act + await MockClient.VerifyAsync( v => v + .WithPath("/test") + .WithMethod(HttpMethod.Get), VerificationTimes.Once); } } } diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs new file mode 100644 index 00000000..8a77f0a4 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs @@ -0,0 +1,82 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using JetBrains.Annotations; +using NinjaTools.FluentMockServer.Xunit; +using NinjaTools.FluentMockServer.Xunit.Attributes; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.Tests.Xunit +{ + public class MockTestFrameworkTests : MockServerTestBase, IDisposable + { + public MockTestFrameworkTests([NotNull] MockServerFixture fixture, ITestOutputHelper outputHelper) : base(fixture, outputHelper) + { } + + [Fact] + public void Should_Discover() + { + Output.WriteLine("Discovered!"); + } + + + [Fact] + [IsolatedMockServerSetup] + public async Task Should_Set_ContextHeader_On_HttpClient_Requests() + { + // Act + var result = await HttpClient.GetAsync("http://google.com"); + + // Assert + if(result.RequestMessage.TryGetMockContextHeader(out var context)) + { + context.Should().Contain(Context.Id); + } + } + + [Fact] + [IsolatedMockServerSetup] + public void Should_Discover_Isolate() + { + // Assert + ContextRegistry.Instance.Isolated.Count(m => m == XunitContext.Context.MethodInfo).Should().Be(1); + } + + [Fact] + [IsolatedMockServerSetup] + public void Should_Discover_Set_Unique_Id() + { + // Arrange + Context.Id.Should().NotBeNullOrWhiteSpace(); + } + + + [Fact] + [IsolatedMockServerSetup] + public async Task Should_Set_Unique_Id_In_Header() + { + // Arrange + Output.WriteLine($"Id = {Context.Id}"); + + // Act + var response = await Context.HttpClient.GetAsync("https://google.com"); + + // Assert + response.RequestMessage.TryGetMockContextHeader(out var contextId).Should().BeTrue(); + contextId.Should().Be(Context.Id); + } + + [Fact] + public void Should_Invoke_GlobalSetup() + { + GlobalSetup.Called.Should().BeTrue(); + } + + /// + public void Dispose() + { + } + } +} diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs index 84074adf..601ac9e8 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs @@ -20,7 +20,7 @@ public SoapServiceTests(MockServerFixture fixture, ITestOutputHelper output) : b { } - public MockServerClient MockedServer => Fixture.MockClient; + public MockServerClient MockedServer => MockClient; [Fact] public async Task Should_Setup_Expectation_With_Xml_Body_When_Setup_Using_Predefined_Setup() diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs index b755adbe..76e358e2 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs @@ -40,23 +40,20 @@ public void VerifyAsync_Should_Return_False_When_MockServer_Recieved_No_Matching } } - [CollectionDefinition(nameof(MockServerCollectionFixture), DisableParallelization = true)] - public class MockServerCollectionFixture : ICollectionFixture - { } + // [CollectionDefinition(nameof(MockServerCollectionFixture), DisableParallelization = true)] + // public class MockServerCollectionFixture : ICollectionFixture + // { } - [Collection(nameof(MockServerCollectionFixture))] - public abstract class MockServerTestBase : XUnitTestBase, IDisposable + // [Collection(nameof(MockServerCollectionFixture))] + public abstract class MockServerTestBase : XUnitTestBase, IDisposable, IClassFixture { - public MockServerFixture Fixture { get; } - - public MockServerClient MockClient => Fixture.MockClient; + public MockServerClient MockClient => Context.MockClient; public HttpClient HttpClient => MockClient.HttpClient; - + public MockServerContext Context { get; } /// protected MockServerTestBase(MockServerFixture fixture, ITestOutputHelper output) : base(output) { - Fixture = fixture; - Thread.Sleep(200); + Context = fixture.Register(output); } /// From 297849c9b45f9af4cf0cbf8c64ec3920907526bd Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 03:18:52 +0100 Subject: [PATCH 56/64] =?UTF-8?q?feat(client):=20enable=20fluent=20context?= =?UTF-8?q?=20configuration=20=F0=9F=8E=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.idea/contentModel.xml | 13 +++ .../Builders/FluentExpectationBuilder.cs | 87 ++++++++++--------- .../Builders/FluentVerificationBuilder.cs | 26 ++++-- .../FluentAPI/IFluentExpectationBuilder.cs | 21 ++++- .../FluentAPI/IFluentVerificationBuilder.cs | 2 +- .../MockServerClient.cs | 7 +- .../Models/Expectation.cs | 17 ++-- .../Models/HttpEntities/HttpRequest.cs | 10 ++- .../Models/MockContext.cs | 27 ++++++ .../Xunit/ManualMockServerSetupTests.cs | 4 +- .../Xunit/MockServerContextTests.cs | 1 + .../Xunit/MockServerFixtureTests.cs | 6 +- .../Xunit/MockServerTestBase.cs | 33 +++++++ .../Xunit/SoapServiceTests.cs | 50 +++++------ .../Xunit/VerificationTests.cs | 29 ++----- 15 files changed, 217 insertions(+), 116 deletions(-) create mode 100644 src/NinjaTools.FluentMockServer/Models/MockContext.cs create mode 100644 test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerTestBase.cs diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index 1d9109b6..a60aefd7 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -36,6 +36,7 @@ + @@ -74,6 +75,7 @@ + @@ -109,7 +111,14 @@ + + + + + + + @@ -335,8 +344,12 @@ + + + + diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentExpectationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentExpectationBuilder.cs index f4a85cd3..91ef4d28 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentExpectationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentExpectationBuilder.cs @@ -4,30 +4,33 @@ using System.Net.Http; using JetBrains.Annotations; using NinjaTools.FluentMockServer.FluentAPI.Builders.HttpEntities; +using NinjaTools.FluentMockServer.Models; using NinjaTools.FluentMockServer.Models.HttpEntities; using NinjaTools.FluentMockServer.Models.ValueTypes; // ReSharper disable UnusedAutoPropertyAccessor.Local namespace NinjaTools.FluentMockServer.FluentAPI.Builders { - internal sealed class FluentExpectationBuilder : IFluentExpectationBuilder + internal sealed class FluentExpectationBuilder : IFluentExpectationBuilder { [NotNull] - public static Models.Expectation Create( - [CanBeNull] HttpRequest httpRequest = null, - [CanBeNull] HttpResponse httpResponse = null, - [CanBeNull] HttpError httpError = null, - [CanBeNull] Times times = null, - [CanBeNull] LifeTime timeToLive = null) => new Models.Expectation(httpRequest, httpResponse, httpError, times, timeToLive); - - [CanBeNull] private HttpRequest HttpRequest { get; set; } - [CanBeNull] private HttpResponse HttpResponse { get; set; } - [CanBeNull] private HttpError HttpError { get; set; } - [CanBeNull] private Times Times { get; set; } - [CanBeNull] private LifeTime TimeToLive { get; set; } - + public static Expectation Create( + HttpRequest? httpRequest = null, + HttpResponse? httpResponse = null, + HttpError? httpError = null, + Times? times = null, + LifeTime? timeToLive = null, + MockContext context = null) => new Expectation(httpRequest, httpResponse, httpError, times, timeToLive, context); + + private MockContext? Context { get; set; } + private HttpRequest? HttpRequest { get; set; } + private HttpResponse? HttpResponse { get; set; } + private HttpError? HttpError { get; set; } + private Times? Times { get; set; } + private LifeTime? TimeToLive { get; set; } + private readonly MockServerSetup _setup; - + public FluentExpectationBuilder() : this(new MockServerSetup()) { } @@ -37,6 +40,13 @@ internal FluentExpectationBuilder(MockServerSetup setup) _setup = setup; } + /// + public IBlankExpectation UsingContext(string context) + { + Context = new MockContext(context); + return this; + } + /// [NotNull] public IWithRequest OnHandling([CanBeNull] HttpMethod method = null, Action requestFactory = null) @@ -65,43 +75,43 @@ public IWithRequest OnHandlingAny(HttpMethod method = null) /// - [NotNull] - public IWithResponse RespondWith(int statusCode, [CanBeNull] Action responseFactory) + public IWithResponse RespondWith(Action responseFactory) { var builder = new FluentHttpResponseBuilder(); - responseFactory?.Invoke(builder); - builder.WithStatusCode(statusCode); + responseFactory.Invoke(builder); HttpResponse = builder.Build(); return this; } /// - [NotNull] - public IWithResponse RespondWith(HttpStatusCode statusCode, Action responseFactory) + public IWithResponse RespondWith(HttpStatusCode statusCode, Action? responseFactory) { - return RespondWith((int) statusCode, responseFactory); + var builder = new FluentHttpResponseBuilder(); + responseFactory?.Invoke(builder); + builder.WithStatusCode(statusCode); + HttpResponse = builder.Build(); + return this; } - /// - [NotNull] - public IWithResponse RespondOnce(int statusCode, Action responseFactory) + public IWithResponse RespondOnce(Action responseFactory) { Times = Times.Once; - return RespondWith(statusCode, responseFactory); + return RespondWith(responseFactory); } /// [NotNull] - public IWithResponse RespondOnce(HttpStatusCode statusCode, Action responseFactory) + public IWithResponse RespondOnce(HttpStatusCode statusCode, Action? responseFactory) { - return RespondOnce((int) statusCode, responseFactory); + Times = Times.Once; + return RespondWith(statusCode, responseFactory); } /// [NotNull] - public IWithResponse RespondTimes(int times, int statusCode, [CanBeNull] Action responseFactory = null) + public IWithResponse RespondTimes(int times, int statusCode, Action? responseFactory = null) { Times = new Times(times); var builder = new FluentHttpResponseBuilder(); @@ -110,9 +120,9 @@ public IWithResponse RespondTimes(int times, int statusCode, [CanBeNull] Action< HttpResponse = builder.Build(); return this; } - + [NotNull] - public IWithResponse RespondTimes([NotNull] Expression> times, int statusCode, [CanBeNull] Action responseFactory = null) + public IWithResponse RespondTimes(Expression>? times, int statusCode, Action? responseFactory = null) { Times = times.Compile().Invoke(); var builder = new FluentHttpResponseBuilder(); @@ -134,17 +144,16 @@ public IWithResponse WhichIsValidFor(int value, TimeUnit timeUnit = TimeUnit.Sec [NotNull] public MockServerSetup Setup() { - _setup.Expectations.Add(BuildExpectation()); - return _setup; - } + if(Context != null) + { + HttpRequest = Context.Apply(HttpRequest); + } - [NotNull] - public Models.Expectation BuildExpectation() - { - return new Models.Expectation(HttpRequest, HttpResponse, HttpError, Times, TimeToLive); + var expectation = new Expectation(HttpRequest, HttpResponse, HttpError, Times, TimeToLive, Context); + _setup.Expectations.Add(expectation); + return _setup; } - /// [NotNull] IFluentExpectationBuilder IWithResponse.And diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs index 487bb4d5..ceaf0965 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/Builders/FluentVerificationBuilder.cs @@ -9,19 +9,23 @@ namespace NinjaTools.FluentMockServer.FluentAPI.Builders { internal class FluentVerificationBuilder : IFluentVerificationBuilder, IWithVerify { - private HttpRequest _httpRequest; - private VerificationTimes _verificationTimes; + private HttpRequest? _httpRequest; + private VerificationTimes? _verificationTimes; + private MockContext? _mockContext; /// - [NotNull] public Verify Build() { + if (_mockContext != null) + { + _httpRequest = _mockContext.Apply(_httpRequest); + } + return new Verify(_httpRequest, _verificationTimes); } /// - [NotNull] public IWithVerify Verify([NotNull] Action request) { var builder = new FluentHttpRequestBuilder(); @@ -31,17 +35,25 @@ public IWithVerify Verify([NotNull] Action request) } /// - public void AtLeastOnce() + public IWithVerify VerifyInContext(string context, Action request) { - _verificationTimes = VerificationTimes.MoreThan(1); + _mockContext = new MockContext(context); + return Verify(request); } + /// public void AtLeast(int value) { _verificationTimes = VerificationTimes.MoreThan(value); } - + + /// + public void AtLeastOnce() + { + _verificationTimes = VerificationTimes.MoreThan(0); + } + /// public void AtMost(int value) { diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs index 36d0e576..28396c90 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentExpectationBuilder.cs @@ -14,7 +14,13 @@ namespace NinjaTools.FluentMockServer.FluentAPI [PublicAPI] public interface IBlankExpectation : IFluentInterface { + [NotNull] + IBlankExpectation UsingContext([NotNull] string context); + + [NotNull] IWithRequest OnHandling(HttpMethod method = null, [CanBeNull] Action requestFactory = null); + + [NotNull] IWithRequest OnHandlingAny([CanBeNull] HttpMethod method = null); } @@ -25,8 +31,8 @@ public interface IBlankExpectation : IFluentInterface [PublicAPI] public interface IFluentExpectationBuilder : IBlankExpectation , IWithRequest, IWithResponse { - } + /// /// /// @@ -34,7 +40,9 @@ public interface IFluentExpectationBuilder : IBlankExpectation , IWithRequest, I [PublicAPI] public interface IWithResponse : IFluentInterface { + [NotNull] IFluentExpectationBuilder And { get; } + [NotNull] IWithResponse WhichIsValidFor(int value, TimeUnit timeUnit = TimeUnit.Seconds); MockServerSetup Setup(); } @@ -46,12 +54,19 @@ public interface IWithResponse : IFluentInterface [PublicAPI] public interface IWithRequest : IFluentInterface { - IWithResponse RespondWith(int statusCode, Action responseFactory = null); + [NotNull] + IWithResponse RespondWith(Action responseFactory); + + [NotNull] IWithResponse RespondWith(HttpStatusCode statusCode, Action responseFactory = null); - IWithResponse RespondOnce(int statusCode, Action responseFactory = null); + [NotNull] + IWithResponse RespondOnce(Action responseFactory); + + [NotNull] IWithResponse RespondOnce(HttpStatusCode statusCode, Action responseFactory = null); + [NotNull] IWithResponse RespondTimes(int times, int statusCode, Action responseFactory = null); } } diff --git a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs index d5c6eda6..52beb9d5 100644 --- a/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs +++ b/src/NinjaTools.FluentMockServer/FluentAPI/IFluentVerificationBuilder.cs @@ -9,7 +9,7 @@ namespace NinjaTools.FluentMockServer.FluentAPI public interface IFluentVerificationBuilder : IFluentBuilder { IWithVerify Verify(Action request); - + IWithVerify VerifyInContext(string context, Action request); } [PublicAPI] diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index 4f8f2671..2abe866d 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -83,7 +83,7 @@ public Task SetupAsync([NotNull] Func setupFactory) var builder = new FluentExpectationBuilder(new MockServerSetup()); setupFactory(builder); var setup = builder.Setup(); - return SetupAsync(setup); + return SetupInternal(setup); } /// /// Configures the MockServer Client using a predefined . /// + /// Cannot establish connecct /// - public async Task SetupAsync(MockServerSetup setup) + private async Task SetupInternal(MockServerSetup setup) { foreach (var request in setup.Expectations.Select(GetExpectationMessage)) { diff --git a/src/NinjaTools.FluentMockServer/Models/Expectation.cs b/src/NinjaTools.FluentMockServer/Models/Expectation.cs index be3f5587..d8c24056 100644 --- a/src/NinjaTools.FluentMockServer/Models/Expectation.cs +++ b/src/NinjaTools.FluentMockServer/Models/Expectation.cs @@ -13,24 +13,27 @@ namespace NinjaTools.FluentMockServer.Models [PublicAPI] public class Expectation { - [CanBeNull] - public string MockContext { get; set; } + [JsonIgnore] + public MockContext? Context { get; set; } [JsonConstructor] public Expectation( - [CanBeNull] HttpRequest httpRequest, - [CanBeNull] HttpResponse httpResponse, - [CanBeNull] HttpError httpError, - [CanBeNull] Times times, - [CanBeNull] LifeTime timeToLive) + HttpRequest? httpRequest, + HttpResponse? httpResponse, + HttpError? httpError, + Times? times, + LifeTime? timeToLive, + MockContext? context) { HttpRequest = httpRequest; HttpResponse = httpResponse; HttpError = httpError; Times = times; TimeToLive = timeToLive; + Context = context; } + /// /// The to match. /// diff --git a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs index 8eca3bf0..ca610743 100644 --- a/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs +++ b/src/NinjaTools.FluentMockServer/Models/HttpEntities/HttpRequest.cs @@ -10,10 +10,16 @@ namespace NinjaTools.FluentMockServer.Models.HttpEntities [PublicAPI] public class HttpRequest { + /// + /// Initializes an empty instance of . + /// + [UsedImplicitly] public HttpRequest() - { - } + { } + /// + /// Initializes a new instance of . + /// public HttpRequest(string method, Dictionary headers, Dictionary cookies, JToken body, string path, bool? secure, bool? keepAlive) { Method = method; diff --git a/src/NinjaTools.FluentMockServer/Models/MockContext.cs b/src/NinjaTools.FluentMockServer/Models/MockContext.cs new file mode 100644 index 00000000..83ac7b10 --- /dev/null +++ b/src/NinjaTools.FluentMockServer/Models/MockContext.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using JetBrains.Annotations; +using NinjaTools.FluentMockServer.Models.HttpEntities; + +namespace NinjaTools.FluentMockServer.Models +{ + public class MockContext + { + public const string Key = "X-MockServer-Context"; + public string Context { get; } + public MockContext(string context) => Context = context; + + [NotNull] + public static implicit operator string[](MockContext context) => new [] + { + context.Context + }; + + public HttpRequest Apply(HttpRequest? httpRequest) + { + httpRequest ??= new HttpRequest(); + httpRequest.Headers ??= new Dictionary(); + httpRequest.Headers.TryAdd(Key, this); + return httpRequest; + } + } +} diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs index 8a7dc72a..5687d536 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/ManualMockServerSetupTests.cs @@ -23,7 +23,7 @@ public async Task Should_Successfully_Setup_Up_Expecation_On_MockServer() // Act await MockClient.SetupAsync(exp => exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .RespondOnce(HttpStatusCode.Created, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) .Setup()); // Assert @@ -37,7 +37,7 @@ public async Task Should_Reset_Expecation_On_MockServer() // Arrange await MockClient.SetupAsync(exp => exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .RespondOnce(HttpStatusCode.Created, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) .Setup()); // Act diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs index af848cb1..b1b6aaf6 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs @@ -68,3 +68,4 @@ public void Id_Should_Be_Equal_To_The_CollectionAttribute_Value() } } + diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs index 4db097e1..ea68e8cc 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerFixtureTests.cs @@ -31,7 +31,7 @@ public async Task Should_Reset_Expectation_On_MockServer() // Arrange await MockClient.SetupAsync(exp => exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .RespondOnce(HttpStatusCode.Created, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) .Setup()); // Act await MockClient.ResetAsync(); @@ -49,7 +49,7 @@ public async Task Should_Successfully_Setup_Up_Expectation_On_MockServer() // Arrange await MockClient.SetupAsync(exp => exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .RespondOnce(HttpStatusCode.Created, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) .Setup()); var response = await HttpClient.GetAsync("test"); @@ -90,7 +90,7 @@ public async Task Should_Verify_Expectation_Was_Met_On_MockServer() // Arrange await MockClient.SetupAsync(exp => exp.OnHandling(HttpMethod.Get, req => req.WithPath("/test")) - .RespondOnce(201, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) + .RespondOnce(HttpStatusCode.Created, resp => resp.WithDelay(50, TimeUnit.Milliseconds)) .Setup()); await HttpClient.GetAsync("test"); diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerTestBase.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerTestBase.cs new file mode 100644 index 00000000..eb20f267 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerTestBase.cs @@ -0,0 +1,33 @@ +using System; +using System.Net.Http; +using NinjaTools.FluentMockServer.Tests.TestHelpers; +using NinjaTools.FluentMockServer.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.Tests.Xunit +{ + + // [CollectionDefinition(nameof(MockServerCollectionFixture), DisableParallelization = true)] + // public class MockServerCollectionFixture : ICollectionFixture + // { } + + // [Collection(nameof(MockServerCollectionFixture))] + public abstract class MockServerTestBase : XUnitTestBase, IDisposable, IClassFixture + { + public MockServerClient MockClient => Context.MockClient; + public HttpClient HttpClient => MockClient.HttpClient; + public MockServerContext Context { get; } + /// + protected MockServerTestBase(MockServerFixture fixture, ITestOutputHelper output) : base(output) + { + Context = fixture.Register(output); + } + + /// + public void Dispose() + { + MockClient.ResetAsync().GetAwaiter().GetResult(); + } + } +} diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs index 601ac9e8..05faa14f 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs @@ -1,7 +1,11 @@ +using System; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; +using FluentAssertions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NinjaTools.FluentMockServer.FluentAPI.Builders; @@ -28,42 +32,30 @@ public async Task Should_Setup_Expectation_With_Xml_Body_When_Setup_Using_Predef // Arrange const string id1 = "IP-100001"; const string id2 = "10001234"; - + const string contentType = "text/xml; charset=utf-8"; var response = ResponseTemplate .Replace("{Id1}", id1) .Replace("{Id2}", id2); - var httpResponse = new HttpResponse - { - Body = new JValue(response), - Delay = new Delay(TimeUnit.Milliseconds, 50), - Headers = new Dictionary - { - {"Content-Type", new[] {$"text/xml; charset=utf-8"}} - } - }; - - var expectation = FluentExpectationBuilder.Create(httpResponse: httpResponse); - - var responseJson = JsonConvert.SerializeObject(httpResponse); - Output.WriteLine($"responseJson: \n{responseJson}"); + // Act + await MockedServer.SetupAsync(_ => _ + .OnHandlingAny() + .RespondWith(r => r + .WithBody(response) + .AddContentType(contentType) + .WithDelay(50, TimeUnit.Milliseconds)) + .Setup()); - var expectationJson = expectation.ToString(); - Output.WriteLine($"expectationJson: \n{expectationJson}"); - - var setup = new MockServerSetup(); - setup.Expectations.Add(expectation); + var responseMessage = await HttpClient.GetAsync("test"); - var setupJson = JsonConvert.SerializeObject(setup, Formatting.Indented); - Output.WriteLine($"Setup json: \n{setupJson}"); - - - // Act - await MockedServer.SetupAsync(setup); - await SetupResponseForOrderPlacedRequest(id1, id2); + // Assert + var responseContentType = responseMessage.Content.Headers.ContentType.ToString(); + var responseBody = await responseMessage.Content.ReadAsStringAsync(); + Dump(responseContentType, "Response Content-Type"); + Dump(responseBody, "Response Body"); - // Assert - // TODO: verify setup + responseBody.Should().Be(response); + responseContentType.Should().Be(contentType); } private async Task SetupResponseForOrderPlacedRequest(string id1, string id2 = null) diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs index 76e358e2..6bc705de 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs @@ -1,12 +1,11 @@ using System; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using NinjaTools.FluentMockServer.Exceptions; using NinjaTools.FluentMockServer.Models.ValueTypes; -using NinjaTools.FluentMockServer.Tests.TestHelpers; using NinjaTools.FluentMockServer.Xunit; +using NinjaTools.FluentMockServer.Xunit.Attributes; using Xunit; using Xunit.Abstractions; @@ -38,28 +37,18 @@ public void VerifyAsync_Should_Return_False_When_MockServer_Recieved_No_Matching // Assert action.Should().ThrowExactly(); } - } - // [CollectionDefinition(nameof(MockServerCollectionFixture), DisableParallelization = true)] - // public class MockServerCollectionFixture : ICollectionFixture - // { } - // [Collection(nameof(MockServerCollectionFixture))] - public abstract class MockServerTestBase : XUnitTestBase, IDisposable, IClassFixture - { - public MockServerClient MockClient => Context.MockClient; - public HttpClient HttpClient => MockClient.HttpClient; - public MockServerContext Context { get; } - /// - protected MockServerTestBase(MockServerFixture fixture, ITestOutputHelper output) : base(output) + [Fact] + [IsolatedMockServerSetup] + public void VerifyAsync_Should_Return_True_When_MockServer_Recieved_In_Context() { - Context = fixture.Register(output); - } + // Act + // MockClient.SetupAsync() + Func action = async () => await MockClient.VerifyAsync(v => v.WithPath("test"), VerificationTimes.Once); - /// - public void Dispose() - { - MockClient.ResetAsync().GetAwaiter().GetResult(); + // Assert + action.Should().ThrowExactly(); } } } From d053c1776a25f496ebeff8605ccb2a05703f2707 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 03:43:02 +0100 Subject: [PATCH 57/64] =?UTF-8?q?style:=20apply=20code=20styles=20?= =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Xunit/Attributes/IsolatedMockContextAttribute.cs | 5 ----- .../Xunit/Attributes/MockServerCollectionAttribute.cs | 4 ++-- src/NinjaTools.FluentMockServer/Xunit/GlobalSetup.cs | 3 +-- .../Xunit/MockServerContainer.cs | 2 +- .../Xunit/MockServerContextTests.cs | 1 - .../Xunit/MockServerTestBase.cs | 6 ------ .../Xunit/MockTestFrameworkBase.cs | 7 +------ 7 files changed, 5 insertions(+), 23 deletions(-) diff --git a/src/NinjaTools.FluentMockServer/Xunit/Attributes/IsolatedMockContextAttribute.cs b/src/NinjaTools.FluentMockServer/Xunit/Attributes/IsolatedMockContextAttribute.cs index c2b2a66f..17635d02 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/Attributes/IsolatedMockContextAttribute.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/Attributes/IsolatedMockContextAttribute.cs @@ -12,9 +12,4 @@ namespace NinjaTools.FluentMockServer.Xunit.Attributes public class IsolatedMockServerSetupAttribute : BeforeAfterTestAttribute { } - - - namespace NinjaTools.FluentMockServer.Xunit.Attributes - { - } } diff --git a/src/NinjaTools.FluentMockServer/Xunit/Attributes/MockServerCollectionAttribute.cs b/src/NinjaTools.FluentMockServer/Xunit/Attributes/MockServerCollectionAttribute.cs index 46cddf4b..7f34a615 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/Attributes/MockServerCollectionAttribute.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/Attributes/MockServerCollectionAttribute.cs @@ -1,7 +1,7 @@ using System; using Xunit.Sdk; -namespace NinjaTools.FluentMockServer.Xunit.Attributes.NinjaTools.FluentMockServer.Xunit.Attributes +namespace NinjaTools.FluentMockServer.Xunit.Attributes { [AttributeUsage(AttributeTargets.Method)] public class MockServerCollectionAttribute : BeforeAfterTestAttribute @@ -9,7 +9,7 @@ public class MockServerCollectionAttribute : BeforeAfterTestAttribute public string Id { get; } /// - public MockServerCollectionAttribute(string collection) : base() + public MockServerCollectionAttribute(string collection) { Id = collection; } diff --git a/src/NinjaTools.FluentMockServer/Xunit/GlobalSetup.cs b/src/NinjaTools.FluentMockServer/Xunit/GlobalSetup.cs index 3ee0d6ae..6a06b0b8 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/GlobalSetup.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/GlobalSetup.cs @@ -4,7 +4,6 @@ using System.Reflection; using JetBrains.Annotations; using NinjaTools.FluentMockServer.Xunit.Attributes; -using NinjaTools.FluentMockServer.Xunit.Attributes.NinjaTools.FluentMockServer.Xunit.Attributes; using Xunit; namespace NinjaTools.FluentMockServer.Xunit @@ -73,6 +72,6 @@ private static ConcurrentBag ResolveIsolated() /// /// Whether the has already been executed. /// - public static bool Called; + public static bool Called { get; private set; } } } diff --git a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs index d87210e7..b4c7403c 100644 --- a/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs +++ b/src/NinjaTools.FluentMockServer/Xunit/MockServerContainer.cs @@ -16,7 +16,7 @@ internal class MockServerContainer : IDisposable { public const string Prefix = "NinjaTools-FluentMockServer"; public const string Suffix = "netcore-xunit-background-runner"; - public static string ContainerName = $"{Prefix}-{Suffix}"; + public static readonly string ContainerName = $"{Prefix}-{Suffix}"; /// /// Gets the Port exposed to the Host. diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs index b1b6aaf6..19703496 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerContextTests.cs @@ -3,7 +3,6 @@ using NinjaTools.FluentMockServer.Tests.TestHelpers; using NinjaTools.FluentMockServer.Xunit; using NinjaTools.FluentMockServer.Xunit.Attributes; -using NinjaTools.FluentMockServer.Xunit.Attributes.NinjaTools.FluentMockServer.Xunit.Attributes; using Xunit; using Xunit.Abstractions; diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerTestBase.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerTestBase.cs index eb20f267..1a3ba949 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerTestBase.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerTestBase.cs @@ -7,12 +7,6 @@ namespace NinjaTools.FluentMockServer.Tests.Xunit { - - // [CollectionDefinition(nameof(MockServerCollectionFixture), DisableParallelization = true)] - // public class MockServerCollectionFixture : ICollectionFixture - // { } - - // [Collection(nameof(MockServerCollectionFixture))] public abstract class MockServerTestBase : XUnitTestBase, IDisposable, IClassFixture { public MockServerClient MockClient => Context.MockClient; diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs index 8a77f0a4..ad167139 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs @@ -10,7 +10,7 @@ namespace NinjaTools.FluentMockServer.Tests.Xunit { - public class MockTestFrameworkTests : MockServerTestBase, IDisposable + public class MockTestFrameworkTests : MockServerTestBase { public MockTestFrameworkTests([NotNull] MockServerFixture fixture, ITestOutputHelper outputHelper) : base(fixture, outputHelper) { } @@ -73,10 +73,5 @@ public void Should_Invoke_GlobalSetup() { GlobalSetup.Called.Should().BeTrue(); } - - /// - public void Dispose() - { - } } } From 69b5c203023c26153b042872ce3f35643cc7958f Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 04:34:34 +0100 Subject: [PATCH 58/64] =?UTF-8?q?feat(client):=20add=20support=20to=20list?= =?UTF-8?q?=20setups=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MockServerClient.cs | 23 +++++++++++- .../Xunit/MockServerClientTests.cs | 35 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index 2abe866d..c5705c5f 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -1,17 +1,21 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using JetBrains.Annotations; +using Newtonsoft.Json; using NinjaTools.FluentMockServer.Exceptions; using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI; using NinjaTools.FluentMockServer.FluentAPI.Builders; +using NinjaTools.FluentMockServer.FluentAPI.Builders.HttpEntities; using NinjaTools.FluentMockServer.Models; using NinjaTools.FluentMockServer.Models.HttpEntities; using NinjaTools.FluentMockServer.Models.ValueTypes; +using NinjaTools.FluentMockServer.Serialization; using NinjaTools.FluentMockServer.Utils; using static NinjaTools.FluentMockServer.Utils.RequestFactory; @@ -103,7 +107,7 @@ public Task SetupAsync(Action setupFactory) /// /// Configures the MockServer Client using a predefined . /// - /// Cannot establish connecct + /// /// private async Task SetupInternal(MockServerSetup setup) { @@ -165,6 +169,23 @@ private async Task VerifyInternal(Verify verify) } } + /// + /// Retrieves a list of active from the MockServer. + /// + /// + [PublicAPI] + [ItemNotNull] + public async Task> ListSetupsAsync() + { + var requestMessage = new HttpRequestMessage(HttpMethod.Put, "mockserver/retrieve?type=ACTIVE_EXPECTATIONS"); + var response = await HttpClient.SendAsync(requestMessage); + response.EnsureSuccessfulMockServerOperation(); + var stringContent = await response.Content.ReadAsStringAsync(); + var setups = Serializer.Deserialize>(stringContent); + return setups; + } + + /// public void Dispose() { diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs new file mode 100644 index 00000000..ac79f678 --- /dev/null +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using FluentAssertions; +using NinjaTools.FluentMockServer.Serialization; +using NinjaTools.FluentMockServer.Xunit; +using Xunit; +using Xunit.Abstractions; + +namespace NinjaTools.FluentMockServer.Tests.Xunit +{ + public class MockServerClientTests : MockServerTestBase, IDisposable + { + /// + public MockServerClientTests(MockServerFixture fixture, ITestOutputHelper output) : base(fixture, output) + { + } + + + [Fact] + public async Task ListSetupsAsync_Returns_One_Active_Setups_When_One_Setup_Is_Active() + { + // Arrange + await MockClient.SetupAsync(_ => _.OnHandlingAny(HttpMethod.Patch).RespondOnce(HttpStatusCode.OK).Setup()); + + // Act + var response = await MockClient.ListSetupsAsync(); + + // Assert + response.Should().ContainSingle().Subject.HttpRequest.Method.Should().Be("PATCH"); + } + } +} From 232e896db326e095725fc3836dcdd8b693a527ae Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 04:35:36 +0100 Subject: [PATCH 59/64] =?UTF-8?q?feat(client):=20add=20support=20to=20list?= =?UTF-8?q?=20request=20the=20mockserver=20receives=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MockServerClient.cs | 16 +++++++++ .../Xunit/MockServerClientTests.cs | 36 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index c5705c5f..b2b27876 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -185,6 +185,22 @@ public async Task> ListSetupsAsync() return setups; } + /// + /// Retrieves a list of active from the MockServer. + /// + /// + [PublicAPI] + [ItemNotNull] + public async Task> ListRequestsAsync() + { + var requestMessage = new HttpRequestMessage(HttpMethod.Put, "mockserver/retrieve?type=REQUESTS"); + var response = await HttpClient.SendAsync(requestMessage); + response.EnsureSuccessfulMockServerOperation(); + var stringContent = await response.Content.ReadAsStringAsync(); + var setups = Serializer.Deserialize>(stringContent); + return setups; + } + /// public void Dispose() diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs index ac79f678..902610f1 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs @@ -31,5 +31,41 @@ public async Task ListSetupsAsync_Returns_One_Active_Setups_When_One_Setup_Is_Ac // Assert response.Should().ContainSingle().Subject.HttpRequest.Method.Should().Be("PATCH"); } + + + [Fact] + public async Task ListRequestsAsync_Returns_All_Requests_The_MockServer_Received() + { + // Arrange + await HttpClient.GetAsync("a"); + await HttpClient.PostAsync("b", new JsonContent(new + { + Type = "Request", + Value = 1 + })); + await HttpClient.GetAsync("c"); + + // Act + var response = await MockClient.ListRequestsAsync(); + + // Assert + response.Should().HaveCount(3); + + var a = response.First(); + a.Path.Should().Be("/a"); + a.Method.Should().Be("GET"); + + var b = response.ElementAt(1); + Dump(b, "B"); + b.Path.Should().Be("/b"); + b.Method.Should().Be("POST"); + b.Body.Value("type").Should().Be("STRING"); + var bodyValue = b.Body.Value("string"); + bodyValue.Should().Be("{\n \"type\": \"Request\",\n \"value\": 1\n}"); + + var c = response.Last(); + c.Path.Should().Be("/c"); + c.Method.Should().Be("GET"); + } } } From 5b3e5dae208b2bde3b7ad4c449dac5979968dc13 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 04:38:34 +0100 Subject: [PATCH 60/64] =?UTF-8?q?feat(client):=20add=20support=20to=20remo?= =?UTF-8?q?ve=20matching=20setups=20from=20mock-server=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.idea/contentModel.xml | 1 + .../MockServerClient.cs | 22 +++++++++++++++++++ .../Xunit/MockServerClientTests.cs | 22 ++++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml index a60aefd7..78128b24 100644 --- a/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml +++ b/.idea/.idea.NinjaTools.FluentMockServer/.idea/contentModel.xml @@ -346,6 +346,7 @@ + diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index b2b27876..d0eb9a65 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -157,6 +157,7 @@ public async Task VerifyAsync([NotNull] Action verif await VerifyInternal(verification); } + /// private async Task VerifyInternal(Verify verify) { var request = GetVerifyRequest(verify); @@ -173,6 +174,7 @@ private async Task VerifyInternal(Verify verify) /// Retrieves a list of active from the MockServer. /// /// + /// [PublicAPI] [ItemNotNull] public async Task> ListSetupsAsync() @@ -189,6 +191,7 @@ public async Task> ListSetupsAsync() /// Retrieves a list of active from the MockServer. /// /// + /// [PublicAPI] [ItemNotNull] public async Task> ListRequestsAsync() @@ -201,6 +204,25 @@ public async Task> ListRequestsAsync() return setups; } + /// + /// Removes all on the MockServer that define a matching . + /// + /// + [PublicAPI] + public async Task RemoveSetupsAsync(Action action) + { + var builder = new FluentHttpRequestBuilder(); + action(builder); + var request = builder.Build(); + + var requestMessage = new HttpRequestMessage(HttpMethod.Put, "clear") + { + Content = new JsonContent(request) + }; + + var response = await HttpClient.SendAsync(requestMessage); + response.EnsureSuccessfulMockServerOperation(); + } /// public void Dispose() diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs index 902610f1..41eefefc 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs @@ -11,7 +11,7 @@ namespace NinjaTools.FluentMockServer.Tests.Xunit { - public class MockServerClientTests : MockServerTestBase, IDisposable + public class MockServerClientTests : MockServerTestBase { /// public MockServerClientTests(MockServerFixture fixture, ITestOutputHelper output) : base(fixture, output) @@ -67,5 +67,25 @@ public async Task ListRequestsAsync_Returns_All_Requests_The_MockServer_Received c.Path.Should().Be("/c"); c.Method.Should().Be("GET"); } + + + [Fact] + public async Task RemoveSetupsAsync_Removes_Active_Setups_When_Match() + { + // Arrange + await MockClient.SetupAsync(_ => _. + OnHandlingAny(HttpMethod.Get).RespondOnce(HttpStatusCode.OK) + .And + .OnHandlingAny(HttpMethod.Post).RespondOnce(HttpStatusCode.NoContent) + .Setup()); + + // Act + await MockClient.RemoveSetupsAsync(_ => _.WithMethod(HttpMethod.Get)); + + // Assert + var response = await MockClient.ListSetupsAsync(); + response.Should().ContainSingle(); + response.Single().HttpRequest.Method.Should().Be("POST"); + } } } From 0d327780a92245b8a17b81e03fef2eb84aa54855 Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 04:52:24 +0100 Subject: [PATCH 61/64] =?UTF-8?q?style:=20optimize=20using=20imports=20?= =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/NinjaTools.FluentMockServer/MockServerClient.cs | 1 - .../FluentAPI/Builders/FluentExpectationBuilderTests.cs | 1 - .../Xunit/LoggerTests.cs | 3 --- .../Xunit/MockServerClientTests.cs | 1 - .../Xunit/MockTestFrameworkBase.cs | 1 - .../Xunit/SoapServiceTests.cs | 8 -------- .../Xunit/VerificationTests.cs | 1 - 7 files changed, 16 deletions(-) diff --git a/src/NinjaTools.FluentMockServer/MockServerClient.cs b/src/NinjaTools.FluentMockServer/MockServerClient.cs index d0eb9a65..cecc42db 100644 --- a/src/NinjaTools.FluentMockServer/MockServerClient.cs +++ b/src/NinjaTools.FluentMockServer/MockServerClient.cs @@ -6,7 +6,6 @@ using System.Net.Http; using System.Threading.Tasks; using JetBrains.Annotations; -using Newtonsoft.Json; using NinjaTools.FluentMockServer.Exceptions; using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.FluentAPI; diff --git a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs index 73d054f5..65b33214 100644 --- a/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/FluentAPI/Builders/FluentExpectationBuilderTests.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; -using Moq; using NinjaTools.FluentMockServer.FluentAPI; using NinjaTools.FluentMockServer.FluentAPI.Builders; using NinjaTools.FluentMockServer.Models.ValueTypes; diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/LoggerTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/LoggerTests.cs index d1fd7db0..8758b9df 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/LoggerTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/LoggerTests.cs @@ -1,11 +1,8 @@ using System; -using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; using JetBrains.Annotations; -using NinjaTools.FluentMockServer.Extensions; using NinjaTools.FluentMockServer.Xunit; -using NinjaTools.FluentMockServer.Xunit.Attributes; using Xunit; using Xunit.Abstractions; diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs index 41eefefc..99254d5e 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockServerClientTests.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using System.Net; using System.Net.Http; diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs index ad167139..cff9cacd 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/MockTestFrameworkBase.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using System.Threading.Tasks; using FluentAssertions; diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs index 05faa14f..17012d0e 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/SoapServiceTests.cs @@ -1,15 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NinjaTools.FluentMockServer.FluentAPI.Builders; -using NinjaTools.FluentMockServer.Models.HttpEntities; using NinjaTools.FluentMockServer.Models.ValueTypes; using NinjaTools.FluentMockServer.Xunit; using Xunit; diff --git a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs index 6bc705de..3119962c 100644 --- a/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs +++ b/test/NinjaTools.FluentMockServer.Tests/Xunit/VerificationTests.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using System.Threading.Tasks; using FluentAssertions; using NinjaTools.FluentMockServer.Exceptions; From a14c4a36308994a058e8768a9f500a128562008a Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 04:54:40 +0100 Subject: [PATCH 62/64] chore(client): bump nuget version to 1.1.1 --- .../NinjaTools.FluentMockServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj index 2f6cd755..6ad4c5fe 100644 --- a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj +++ b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj @@ -16,7 +16,7 @@ true true mockserver, test, mock, docker, xunit, api, soap, rest - 1.1.0 + 1.1.1 From 2c0f129c3df08264bafa56335d30930673d49fcd Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 04:56:46 +0100 Subject: [PATCH 63/64] ci: fix `parameters` -> `variables` --- build/publish-nuget.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/publish-nuget.yaml b/build/publish-nuget.yaml index be13c1f0..eba0adf9 100644 --- a/build/publish-nuget.yaml +++ b/build/publish-nuget.yaml @@ -8,7 +8,7 @@ trigger: pr: none -parameters: +variables: sdkVersion: "3.1.100" buildConfiguration: "Release" projects: "src/**/*.csproj" From 5a58183355d64995189c7638f61fbaed2f40f21b Mon Sep 17 00:00:00 2001 From: alex held Date: Sat, 1 Feb 2020 05:05:01 +0100 Subject: [PATCH 64/64] ci: rename nuget push step --- build/nuget-pack.yaml | 2 +- build/publish-nuget.yaml | 2 +- .../NinjaTools.FluentMockServer.csproj | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/build/nuget-pack.yaml b/build/nuget-pack.yaml index 7859a88c..bd92db6d 100644 --- a/build/nuget-pack.yaml +++ b/build/nuget-pack.yaml @@ -46,7 +46,7 @@ jobs: - download: current artifact: "packages" - task: NuGetCommand@2 - displayName: "Publish Prerelease NuGet Package" + displayName: "🚀 Publish NuGet Package 📨" inputs: command: "push" packagesToPush: "$(Pipeline.Workspace)/packages/**/*.nupkg" diff --git a/build/publish-nuget.yaml b/build/publish-nuget.yaml index eba0adf9..5b6b1efc 100644 --- a/build/publish-nuget.yaml +++ b/build/publish-nuget.yaml @@ -50,7 +50,7 @@ jobs: - download: current artifact: "packages" - task: NuGetCommand@2 - displayName: "Publish Prerelease NuGet Package" + displayName: "Publish NuGet Package" inputs: command: "push" packagesToPush: "$(Pipeline.Workspace)/packages/**/*.nupkg" diff --git a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj index 6ad4c5fe..dcff380a 100644 --- a/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj +++ b/src/NinjaTools.FluentMockServer/NinjaTools.FluentMockServer.csproj @@ -3,11 +3,9 @@ 36A37D14-D7FC-4E32-8F48-5FB606818757 - 1.1.0 - netcoreapp3.1 + 1.1.1 8.0 netcoreapp3.1; - Enabled enable