From b03a9ea96725faf4c0a7db152397ef0a15e76fbe Mon Sep 17 00:00:00 2001 From: "M. J. W-H. Campman" Date: Mon, 5 Aug 2024 20:46:06 -0400 Subject: [PATCH 1/3] feat: add array contains matcher --- .../Consumer.Tests/OrdersClientTests.cs | 52 ++++ .../pacts/Fulfilment API-Orders API.json | 120 +++++++++ samples/OrdersApi/Consumer/OrdersClient.cs | 16 +- .../Provider.Tests/ProviderStateMiddleware.cs | 15 +- .../Provider/Orders/OrdersController.cs | 24 ++ .../Matchers/ArrayContainsMatcher.cs | 28 ++ src/PactNet.Abstractions/Matchers/Match.cs | 5 + .../Matchers/MatcherConverter.cs | 3 + .../Matchers/ArrayContainsMatcherTests.cs | 29 ++ .../Matchers/MatchTests.cs | 19 +- .../Matchers/ArrayContainsMatcherTests.cs | 252 ++++++++++++++++++ 11 files changed, 559 insertions(+), 4 deletions(-) create mode 100644 src/PactNet.Abstractions/Matchers/ArrayContainsMatcher.cs create mode 100644 tests/PactNet.Abstractions.Tests/Matchers/ArrayContainsMatcherTests.cs create mode 100644 tests/PactNet.Tests/Matchers/ArrayContainsMatcherTests.cs diff --git a/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs b/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs index a9127c1b..70323032 100644 --- a/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs +++ b/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs @@ -84,6 +84,58 @@ await this.pact.VerifyAsync(async ctx => }); } + [Fact] + public async Task GetOrdersAsync_WhenCalled_ReturnsMultipleOrders() + { + var expected1 = new OrderDto(1, OrderStatus.Pending, new DateTimeOffset(2023, 6, 28, 12, 13, 14, TimeSpan.FromHours(1))); + var expected2 = new OrderDto(2, OrderStatus.Pending, new DateTimeOffset(2023, 6, 29, 12, 13, 14, TimeSpan.FromHours(1))); + + this.pact + .UponReceiving("a request for multiple orders by id") + .Given("orders with ids {ids} exist", new Dictionary { ["ids"] = "1,2" }) + .WithRequest(HttpMethod.Get, "/api/orders/many/1,2") + .WithHeader("Accept", "application/json") + .WillRespond() + .WithStatus(HttpStatusCode.OK) + .WithJsonBody(Match.ArrayContains(new dynamic[] + { + new + { + Id = Match.Integer(expected1.Id), + Status = Match.Regex(expected1.Status.ToString(), string.Join("|", Enum.GetNames())), + Date = Match.Type(expected1.Date.ToString("O")) + }, + new + { + Id = Match.Integer(expected2.Id), + Status = Match.Regex(expected2.Status.ToString(), string.Join("|", Enum.GetNames())), + Date = Match.Type(expected2.Date.ToString("O")) + }, + })); + + await this.pact.VerifyAsync(async ctx => + { + this.mockFactory + .Setup(f => f.CreateClient("Orders")) + .Returns(() => new HttpClient + { + BaseAddress = ctx.MockServerUri, + DefaultRequestHeaders = + { + Accept = { MediaTypeWithQualityHeaderValue.Parse("application/json") } + } + }); + + var client = new OrdersClient(this.mockFactory.Object); + + OrderDto[] orders = await client.GetOrdersAsync(new[] { 1, 2 }); + + orders.Should().HaveCount(2); + orders[0].Should().Be(expected1); + orders[1].Should().Be(expected2); + }); + } + [Fact] public async Task GetOrderAsync_UnknownOrder_ReturnsNotFound() { diff --git a/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json b/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json index 24dd275e..6921e54d 100644 --- a/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json +++ b/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json @@ -88,6 +88,126 @@ }, "type": "Synchronous/HTTP" }, + { + "description": "a request for multiple orders by id", + "pending": false, + "providerStates": [ + { + "name": "orders with ids {ids} exist", + "params": { + "ids": "1,2" + } + } + ], + "request": { + "headers": { + "Accept": [ + "application/json" + ] + }, + "method": "GET", + "path": "/api/orders/many/1,2" + }, + "response": { + "body": { + "content": [ + { + "date": "2023-06-28T12:13:14.0000000+01:00", + "id": 1, + "status": "Pending" + }, + { + "date": "2023-06-29T12:13:14.0000000+01:00", + "id": 2, + "status": "Pending" + } + ], + "contentType": "application/json", + "encoded": false + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "matchingRules": { + "body": { + "$": { + "combine": "AND", + "matchers": [ + { + "match": "arrayContains", + "variants": [ + { + "index": 0, + "rules": { + "$.date": { + "combine": "AND", + "matchers": [ + { + "match": "type" + } + ] + }, + "$.id": { + "combine": "AND", + "matchers": [ + { + "match": "integer" + } + ] + }, + "$.status": { + "combine": "AND", + "matchers": [ + { + "match": "regex", + "regex": "Pending|Fulfilling|Shipped" + } + ] + } + } + }, + { + "index": 1, + "rules": { + "$.date": { + "combine": "AND", + "matchers": [ + { + "match": "type" + } + ] + }, + "$.id": { + "combine": "AND", + "matchers": [ + { + "match": "integer" + } + ] + }, + "$.status": { + "combine": "AND", + "matchers": [ + { + "match": "regex", + "regex": "Pending|Fulfilling|Shipped" + } + ] + } + } + } + ] + } + ] + } + } + }, + "status": 200 + }, + "type": "Synchronous/HTTP" + }, { "description": "a request to update the status of an order", "pending": false, diff --git a/samples/OrdersApi/Consumer/OrdersClient.cs b/samples/OrdersApi/Consumer/OrdersClient.cs index 85ec5ba6..6ce6aeea 100644 --- a/samples/OrdersApi/Consumer/OrdersClient.cs +++ b/samples/OrdersApi/Consumer/OrdersClient.cs @@ -1,4 +1,5 @@ -using System.Net.Http; +using System.Collections.Generic; +using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; @@ -40,6 +41,19 @@ public async Task GetOrderAsync(int orderId) return order; } + /// + /// Get a orders by ID + /// + /// Order IDs + /// Order + public async Task GetOrdersAsync(IEnumerable orderIds) + { + using HttpClient client = this.factory.CreateClient("Orders"); + + OrderDto[] orders = await client.GetFromJsonAsync($"/api/orders/many/{string.Join(',', orderIds)}", Options); + return orders; + } + /// /// Update the status of an order /// diff --git a/samples/OrdersApi/Provider.Tests/ProviderStateMiddleware.cs b/samples/OrdersApi/Provider.Tests/ProviderStateMiddleware.cs index ee1760bc..62da82a2 100644 --- a/samples/OrdersApi/Provider.Tests/ProviderStateMiddleware.cs +++ b/samples/OrdersApi/Provider.Tests/ProviderStateMiddleware.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Http; using System.Text; using System.Text.Json; @@ -36,7 +37,8 @@ public ProviderStateMiddleware(RequestDelegate next, IOrderRepository orders) this.providerStates = new Dictionary, Task>> { - ["an order with ID {id} exists"] = this.EnsureEventExistsAsync + ["an order with ID {id} exists"] = this.EnsureEventExistsAsync, + ["orders with ids {ids} exist"] = this.EnsureEventsExistAsync }; } @@ -52,6 +54,15 @@ private async Task EnsureEventExistsAsync(IDictionary parameters await this.orders.InsertAsync(new OrderDto(id.GetInt32(), OrderStatus.Fulfilling, DateTimeOffset.Now)); } + private async Task EnsureEventsExistAsync(IDictionary parameters) + { + var ids = (JsonElement)parameters["ids"]; + foreach (var id in ids.GetString()!.Split(',').Select(int.Parse)) + { + await this.orders.InsertAsync(new OrderDto(id, OrderStatus.Fulfilling, DateTimeOffset.Now)); + } + } + /// /// Handle the request /// @@ -79,7 +90,7 @@ public async Task InvokeAsync(HttpContext context) try { ProviderState providerState = JsonSerializer.Deserialize(jsonRequestBody, Options); - + if (!string.IsNullOrEmpty(providerState?.State)) { await this.providerStates[providerState.State].Invoke(providerState.Params); diff --git a/samples/OrdersApi/Provider/Orders/OrdersController.cs b/samples/OrdersApi/Provider/Orders/OrdersController.cs index 3bfb5a7b..c97460fb 100644 --- a/samples/OrdersApi/Provider/Orders/OrdersController.cs +++ b/samples/OrdersApi/Provider/Orders/OrdersController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -46,6 +47,29 @@ public async Task GetByIdAsync(int id) } } + [HttpGet("many/{ids}", Name = "getMany")] + [ProducesResponseType(typeof(OrderDto[]), StatusCodes.Status200OK)] + public async Task GetManyAsync(string ids) + { + try + { + var idsAsInts = ids.Split(',').Select(int.Parse); + + List result = new List(); + foreach (int id in idsAsInts) + { + var order = await this.orders.GetAsync(id); + result.Add(order); + } + + return this.Ok(result.ToArray()); + } + catch (KeyNotFoundException) + { + return this.NotFound(); + } + } + /// /// Create a new pending order /// diff --git a/src/PactNet.Abstractions/Matchers/ArrayContainsMatcher.cs b/src/PactNet.Abstractions/Matchers/ArrayContainsMatcher.cs new file mode 100644 index 00000000..7f6b1975 --- /dev/null +++ b/src/PactNet.Abstractions/Matchers/ArrayContainsMatcher.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; + +namespace PactNet.Matchers +{ + public class ArrayContainsMatcher : IMatcher + { + /// + /// Type of the matcher + /// + [JsonPropertyName("pact:matcher:type")] + public string Type => "array-contains"; + + /// + /// The items expected to be in the array. + /// + [JsonPropertyName("variants")] + public dynamic Value { get; } + + /// + /// Initialises a new instance of the class. + /// + /// + public ArrayContainsMatcher(dynamic[] variants) + { + Value = variants; + } + } +} diff --git a/src/PactNet.Abstractions/Matchers/Match.cs b/src/PactNet.Abstractions/Matchers/Match.cs index 7531fb29..5efbcd7a 100644 --- a/src/PactNet.Abstractions/Matchers/Match.cs +++ b/src/PactNet.Abstractions/Matchers/Match.cs @@ -168,5 +168,10 @@ public static IMatcher Include(string example) { return new IncludeMatcher(example); } + + public static IMatcher ArrayContains(dynamic[] variations) + { + return new ArrayContainsMatcher(variations); + } } } diff --git a/src/PactNet.Abstractions/Matchers/MatcherConverter.cs b/src/PactNet.Abstractions/Matchers/MatcherConverter.cs index e6493de3..42fd7881 100644 --- a/src/PactNet.Abstractions/Matchers/MatcherConverter.cs +++ b/src/PactNet.Abstractions/Matchers/MatcherConverter.cs @@ -52,6 +52,9 @@ public override void Write(Utf8JsonWriter writer, IMatcher value, JsonSerializer case TypeMatcher matcher: JsonSerializer.Serialize(writer, matcher, options); break; + case ArrayContainsMatcher matcher: + JsonSerializer.Serialize(writer, matcher, options); + break; default: throw new ArgumentOutOfRangeException($"Unsupported matcher: {value.GetType()}"); } diff --git a/tests/PactNet.Abstractions.Tests/Matchers/ArrayContainsMatcherTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/ArrayContainsMatcherTests.cs new file mode 100644 index 00000000..927e8f41 --- /dev/null +++ b/tests/PactNet.Abstractions.Tests/Matchers/ArrayContainsMatcherTests.cs @@ -0,0 +1,29 @@ +using System.Text.Json; +using FluentAssertions; +using PactNet.Matchers; +using Xunit; + +namespace PactNet.Abstractions.Tests.Matchers +{ + public class ArrayContainsMatcherTests + { + [Fact] + public void Ctor_String_SerializesCorrectly() + { + // Arrange + var example = new[] + { + "Thing1", + "Thing2", + }; + + var matcher = new ArrayContainsMatcher(example); + + // Act + var actual = JsonSerializer.Serialize(matcher); + + // Assert + actual.Should().Be(@"{""pact:matcher:type"":""array-contains"",""variants"":[""Thing1"",""Thing2""]}"); + } + } +} diff --git a/tests/PactNet.Abstractions.Tests/Matchers/MatchTests.cs b/tests/PactNet.Abstractions.Tests/Matchers/MatchTests.cs index d0c96cf8..80f8543c 100644 --- a/tests/PactNet.Abstractions.Tests/Matchers/MatchTests.cs +++ b/tests/PactNet.Abstractions.Tests/Matchers/MatchTests.cs @@ -166,6 +166,23 @@ public void Include_WhenCalled_ReturnsMatcher() matcher.Should().BeEquivalentTo(new IncludeMatcher(example)); } + [Fact] + public void ArrayContainsMatcher_WhenCalled_ReturnsMatcher() + { + // Arrange + var example = new[] + { + "test1", + "test2", + }; + + // Act + var matcher = Match.ArrayContains(example); + + // Assert + matcher.Should().BeEquivalentTo(new ArrayContainsMatcher(example)); + } + [Fact] public void ComposingTwoTypeMatchers_WhenCalled_ReturnsAllMatchers() { @@ -200,7 +217,7 @@ public void ComposingAMinTypeMatcherAndATypeMatcher_WhenCalled_ReturnsAllMatcher }; IMatcher matcher = Match.MinType(expected, 2); - + object[] value = matcher.Value as object[]; value.First().Should().BeEquivalentTo(expected); } diff --git a/tests/PactNet.Tests/Matchers/ArrayContainsMatcherTests.cs b/tests/PactNet.Tests/Matchers/ArrayContainsMatcherTests.cs new file mode 100644 index 00000000..abec204d --- /dev/null +++ b/tests/PactNet.Tests/Matchers/ArrayContainsMatcherTests.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using PactNet.Matchers; +using Xunit; + +namespace PactNet.Tests.Matchers; + +public class ArrayContainsMatcherTests +{ + private readonly IPactBuilderV4 pactBuilder; + + public ArrayContainsMatcherTests() + { + pactBuilder = Pact.V4("Some Consumer", "Some Producer") + .WithHttpInteractions(); + } + + [Fact] + public async Task ConsumerTests_Simple_StringList() + { + // Arrange + this.pactBuilder + .UponReceiving("A request for strings") + .WithRequest(HttpMethod.Get, "/api/list/string") + .WillRespond() + .WithStatus(HttpStatusCode.OK) + .WithJsonBody(Match.ArrayContains([ + "Thing 1", + "Thing 2" + ])); + + await this.pactBuilder.VerifyAsync(async ctx => + { + using var service = Service(ctx.MockServerUri); + + // Act + var strings = await service.GetStringList(); + + // Assert + strings.Should().Contain("Thing 1").And.Contain("Thing 2"); + }); + } + + [Fact] + public async Task ConsumerTests_Simple_StringList_RegexMatch() + { + // Arrange + this.pactBuilder + .UponReceiving("A request for strings") + .WithRequest(HttpMethod.Get, "/api/list/string") + .WillRespond() + .WithStatus(HttpStatusCode.OK) + .WithJsonBody(Match.ArrayContains([ + Match.Regex("Thing 1", "Thing [0-9]+"), + Match.Regex("Thing X", "Thing [A-Z]+"), + ])); + + await this.pactBuilder.VerifyAsync(async ctx => + { + using var service = Service(ctx.MockServerUri); + + // Act + var strings = await service.GetStringList(); + + // Assert + strings.Should().Contain("Thing 1").And.Contain("Thing X"); + }); + } + + [Fact] + public async Task ConsumerTests_Simple_ObjectList() + { + // Arrange + this.pactBuilder + .UponReceiving("A request for strings") + .WithRequest(HttpMethod.Get, "/api/list/insert-or-update") + .WillRespond() + .WithStatus(HttpStatusCode.OK) + .WithJsonBody(Match.ArrayContains([ + new InsertOrUpdate + { + Type = ActionEnum.Insert, + Content = new ContentDto + { + Name = "Name 1", + } + }, + new InsertOrUpdate + { + Type = ActionEnum.Update, + Content = new ContentDto + { + Name = "Name 2", + } + }, + ])); + + await this.pactBuilder.VerifyAsync(async ctx => + { + using var service = Service(ctx.MockServerUri); + + // Act + var strings = await service.GetInsertOrUpdateList(); + + // Assert + strings.Should() + .Contain(new InsertOrUpdate + { + Type = ActionEnum.Insert, + Content = new ContentDto + { + Name = "Name 1", + } + }) + .And + .Contain(new InsertOrUpdate + { + Type = ActionEnum.Update, + Content = new ContentDto + { + Name = "Name 2", + } + }); + }); + } + + [Fact] + public async Task ConsumerTests_Simple_StringListList() + { + // Arrange + this.pactBuilder + .UponReceiving("A request for strings") + .WithRequest(HttpMethod.Get, "/api/list/list/string") + .WillRespond() + .WithStatus(HttpStatusCode.OK) + .WithJsonBody(Match.ArrayContains([ + new[] { "A", "B" }, + new[] { "C", "D" }, + ])); + + await this.pactBuilder.VerifyAsync(async ctx => + { + using var service = Service(ctx.MockServerUri); + + // Act + var strings = await service.GetStringListList(); + + // Assert + strings.Should() + .Contain(xs => xs.Count == 2 && xs[0] == "A" && xs[1] == "B") + .And + .Contain(xs => xs.Count == 2 && xs[0] == "C" && xs[1] == "D"); + }); + } + + [Fact] + public async Task ConsumerTests_Nested_StringListList() + { + // Arrange + this.pactBuilder + .UponReceiving("A request for strings") + .WithRequest(HttpMethod.Get, "/api/list/list/string") + .WillRespond() + .WithStatus(HttpStatusCode.OK) + .WithJsonBody(Match.ArrayContains([ + Match.ArrayContains(["A", "B"]), + Match.ArrayContains(["C", "D"]), + ])); + + await this.pactBuilder.VerifyAsync(async ctx => + { + using var service = Service(ctx.MockServerUri); + + // Act + var strings = await service.GetStringListList(); + + // Assert + strings.Should() + .Contain(xs => xs.Count == 2 && xs[0] == "A" && xs[1] == "B") + .And + .Contain(xs => xs.Count == 2 && xs[0] == "C" && xs[1] == "D"); + }); + } + + private static ArrayIncludeTestService Service(Uri uri) + => new(uri); + + private enum ActionEnum + { + Insert, + Update, + } + + private sealed record ContentDto + { + public string Name { get; set; } + } + + private sealed record InsertOrUpdate where T : IEquatable + { + public ActionEnum Type { get; set; } + public T Content { get; set; } + } + + private sealed class ArrayIncludeTestService : IDisposable + { + private readonly Uri baseUri; + private readonly HttpClient client; + + public ArrayIncludeTestService(Uri baseUri) + { + this.baseUri = baseUri; + this.client = new HttpClient(); + } + + private string BaseUri => this.baseUri.ToString().TrimEnd('/'); + + public Task> GetStringList() + { + return Get>("/api/list/string"); + } + + public Task>> GetStringListList() + { + return Get>>("/api/list/list/string"); + } + + public Task>> GetInsertOrUpdateList() + { + return Get>>("/api/list/insert-or-update"); + } + + private async Task Get(string url) + { + using var response = await this.client.GetAsync($"{BaseUri}{url}"); + response.EnsureSuccessStatusCode(); + + using var stream = await response.Content.ReadAsStreamAsync(); + return await JsonSerializer.DeserializeAsync(stream); + } + + public void Dispose() + { + this.client.Dispose(); + } + } +} From e829d9408a26236ceaa1c2f8c0bc0c8bb72e5a78 Mon Sep 17 00:00:00 2001 From: "M. J. W-H. Campman" Date: Mon, 28 Oct 2024 18:26:03 -0400 Subject: [PATCH 2/3] refactor: vary matching to show it works with multiple matchers --- .../OrdersApi/Consumer.Tests/OrdersClientTests.cs | 4 ++-- .../pacts/Fulfilment API-Orders API.json | 14 +++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs b/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs index 70323032..c09afcd7 100644 --- a/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs +++ b/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs @@ -108,8 +108,8 @@ public async Task GetOrdersAsync_WhenCalled_ReturnsMultipleOrders() new { Id = Match.Integer(expected2.Id), - Status = Match.Regex(expected2.Status.ToString(), string.Join("|", Enum.GetNames())), - Date = Match.Type(expected2.Date.ToString("O")) + Status = expected2.Status, + Date = Match.Regex("2023-06-29T12:13:14.000000+01:00", @"\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\d\d\d\d+\d\d:\d\d") }, })); diff --git a/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json b/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json index 6921e54d..f768ae31 100644 --- a/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json +++ b/samples/OrdersApi/Consumer.Tests/pacts/Fulfilment API-Orders API.json @@ -117,7 +117,7 @@ "status": "Pending" }, { - "date": "2023-06-29T12:13:14.0000000+01:00", + "date": "2023-06-29T12:13:14.000000+01:00", "id": 2, "status": "Pending" } @@ -175,7 +175,8 @@ "combine": "AND", "matchers": [ { - "match": "type" + "match": "regex", + "regex": "\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d\\d\\d\\d\\d\\d+\\d\\d:\\d\\d" } ] }, @@ -186,15 +187,6 @@ "match": "integer" } ] - }, - "$.status": { - "combine": "AND", - "matchers": [ - { - "match": "regex", - "regex": "Pending|Fulfilling|Shipped" - } - ] } } } From 4daaa090c537e16de69f9d239232f474e8956eb1 Mon Sep 17 00:00:00 2001 From: "M. J. W-H. Campman" Date: Mon, 28 Oct 2024 18:26:42 -0400 Subject: [PATCH 3/3] doc comment --- samples/OrdersApi/Provider.Tests/ProviderStateMiddleware.cs | 5 +++++ samples/OrdersApi/Provider/Orders/OrdersController.cs | 5 +++++ src/PactNet.Abstractions/Matchers/ArrayContainsMatcher.cs | 3 +++ src/PactNet.Abstractions/Matchers/Match.cs | 5 +++++ 4 files changed, 18 insertions(+) diff --git a/samples/OrdersApi/Provider.Tests/ProviderStateMiddleware.cs b/samples/OrdersApi/Provider.Tests/ProviderStateMiddleware.cs index 62da82a2..c9ef68ed 100644 --- a/samples/OrdersApi/Provider.Tests/ProviderStateMiddleware.cs +++ b/samples/OrdersApi/Provider.Tests/ProviderStateMiddleware.cs @@ -54,6 +54,11 @@ private async Task EnsureEventExistsAsync(IDictionary parameters await this.orders.InsertAsync(new OrderDto(id.GetInt32(), OrderStatus.Fulfilling, DateTimeOffset.Now)); } + /// + /// Ensure a series of events exist + /// + /// Event parameters + /// Awaitable private async Task EnsureEventsExistAsync(IDictionary parameters) { var ids = (JsonElement)parameters["ids"]; diff --git a/samples/OrdersApi/Provider/Orders/OrdersController.cs b/samples/OrdersApi/Provider/Orders/OrdersController.cs index c97460fb..03f0e15d 100644 --- a/samples/OrdersApi/Provider/Orders/OrdersController.cs +++ b/samples/OrdersApi/Provider/Orders/OrdersController.cs @@ -47,6 +47,11 @@ public async Task GetByIdAsync(int id) } } + /// + /// Get several orders by their comma-separated IDs + /// + /// + /// [HttpGet("many/{ids}", Name = "getMany")] [ProducesResponseType(typeof(OrderDto[]), StatusCodes.Status200OK)] public async Task GetManyAsync(string ids) diff --git a/src/PactNet.Abstractions/Matchers/ArrayContainsMatcher.cs b/src/PactNet.Abstractions/Matchers/ArrayContainsMatcher.cs index 7f6b1975..8080604d 100644 --- a/src/PactNet.Abstractions/Matchers/ArrayContainsMatcher.cs +++ b/src/PactNet.Abstractions/Matchers/ArrayContainsMatcher.cs @@ -2,6 +2,9 @@ namespace PactNet.Matchers { + /// + /// Matcher for array-contains. Checks whether an array contains the specified variations. + /// public class ArrayContainsMatcher : IMatcher { /// diff --git a/src/PactNet.Abstractions/Matchers/Match.cs b/src/PactNet.Abstractions/Matchers/Match.cs index 5efbcd7a..e7cbefe0 100644 --- a/src/PactNet.Abstractions/Matchers/Match.cs +++ b/src/PactNet.Abstractions/Matchers/Match.cs @@ -169,6 +169,11 @@ public static IMatcher Include(string example) return new IncludeMatcher(example); } + /// + /// Matcher which matches an array containing the specified variations. + /// + /// Variations which should be contained in the array. + /// Matcher public static IMatcher ArrayContains(dynamic[] variations) { return new ArrayContainsMatcher(variations);