From 202ceb3a30205e6c2a8f35efc3bf4cc6b6ae1bf3 Mon Sep 17 00:00:00 2001 From: Adam Rodger Date: Tue, 1 Apr 2025 20:23:50 +0100 Subject: [PATCH] feat!: Invoke async message scenario factories on request We still have to invoke the factory with sync-over-async at request time and this puts a burden on the user not to use this improperly, but if we want to delay invocation of the factory until after provider states have been configured then this is the first step. This is also obviously a breaking API change and requires a major version bump. `WithAsyncContent` makes way more sense because it's the content factory that's async, not the call to the builder as `WithContentAsync` would imply. This also means the return type changes to void instead of Task. See #459 --- .../Messaging/IMessageScenarioBuilder.cs | 12 ++++----- .../Verifier/Messaging/IMessageScenarios.cs | 13 ++-------- .../Messaging/MessageScenarioBuilder.cs | 26 ++++++++++++------- .../Verifier/Messaging/MessageScenarios.cs | 22 ++-------------- .../Messaging/MessageScenarioBuilderTests.cs | 8 +++--- .../Messaging/MessageScenariosTests.cs | 2 +- 6 files changed, 32 insertions(+), 51 deletions(-) diff --git a/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarioBuilder.cs b/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarioBuilder.cs index a225aaad..31ca9e79 100644 --- a/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarioBuilder.cs +++ b/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarioBuilder.cs @@ -17,29 +17,29 @@ public interface IMessageScenarioBuilder IMessageScenarioBuilder WithMetadata(dynamic metadata); /// - /// Set the action of the scenario + /// Set the content factory of the scenario. The factory is invoked each time the scenario is required. /// /// Content factory void WithContent(Func factory); /// - /// Set the content of the scenario + /// Set the content factory of the scenario. The factory is invoked each time the scenario is required. /// /// Content factory /// Custom JSON serializer settings void WithContent(Func factory, JsonSerializerOptions settings); /// - /// Set the action of the scenario + /// Set the content factory of the scenario. The factory is invoked each time the scenario is required. /// /// Content factory - Task WithContentAsync(Func> factory); + void WithAsyncContent(Func> factory); /// - /// Set the content of the scenario + /// Set the content factory of the scenario. The factory is invoked each time the scenario is required. /// /// Content factory /// Custom JSON serializer settings - Task WithContentAsync(Func> factory, JsonSerializerOptions settings); + void WithAsyncContent(Func> factory, JsonSerializerOptions settings); } } diff --git a/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarios.cs b/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarios.cs index cd37db36..58b728a6 100644 --- a/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarios.cs +++ b/src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarios.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; namespace PactNet.Verifier.Messaging { @@ -22,19 +21,11 @@ public interface IMessageScenarios IMessageScenarios Add(string description, Func factory); /// - /// Add a message scenario + /// Add a message scenario by configuring a scenario builder /// /// Scenario description /// Scenario configure - /// + /// Fluent builder IMessageScenarios Add(string description, Action configure); - - /// - /// Add a message scenario - /// - /// Scenario description - /// Scenario configure - /// - IMessageScenarios Add(string description, Func configure); } } diff --git a/src/PactNet/Verifier/Messaging/MessageScenarioBuilder.cs b/src/PactNet/Verifier/Messaging/MessageScenarioBuilder.cs index ac901da0..06b483f4 100644 --- a/src/PactNet/Verifier/Messaging/MessageScenarioBuilder.cs +++ b/src/PactNet/Verifier/Messaging/MessageScenarioBuilder.cs @@ -10,6 +10,7 @@ namespace PactNet.Verifier.Messaging internal class MessageScenarioBuilder : IMessageScenarioBuilder { private readonly string description; + private Func factory; private dynamic metadata = new { ContentType = "application/json" }; private JsonSerializerOptions settings; @@ -55,25 +56,32 @@ public void WithContent(Func factory, JsonSerializerOptions settings) } /// - /// Set the action of the scenario + /// Set the content factory of the scenario. The factory is invoked each time the scenario is required. /// /// Content factory - public async Task WithContentAsync(Func> factory) + public void WithAsyncContent(Func> factory) { - dynamic value = await factory(); - this.factory = () => value; + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + this.WithContent(() => factory().GetAwaiter().GetResult()); } /// - /// Set the content of the scenario + /// Set the content factory of the scenario. The factory is invoked each time the scenario is required. /// /// Content factory /// Custom JSON serializer settings - public async Task WithContentAsync(Func> factory, JsonSerializerOptions settings) + public void WithAsyncContent(Func> factory, JsonSerializerOptions settings) { - dynamic value = await factory(); - this.factory = () => value; - this.settings = settings; + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + this.WithContent(() => factory().GetAwaiter().GetResult(), settings); } /// diff --git a/src/PactNet/Verifier/Messaging/MessageScenarios.cs b/src/PactNet/Verifier/Messaging/MessageScenarios.cs index 7afb06b2..25b23af7 100644 --- a/src/PactNet/Verifier/Messaging/MessageScenarios.cs +++ b/src/PactNet/Verifier/Messaging/MessageScenarios.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Threading.Tasks; namespace PactNet.Verifier.Messaging { @@ -44,11 +43,11 @@ public IMessageScenarios Add(string description, Func factory) } /// - /// Add a message scenario + /// Add a message scenario by configuring a scenario builder /// /// Scenario description /// Scenario configure - /// + /// Fluent builder public IMessageScenarios Add(string description, Action configure) { var builder = new MessageScenarioBuilder(description); @@ -59,22 +58,5 @@ public IMessageScenarios Add(string description, Action return this; } - - /// - /// Add a message scenario - /// - /// Scenario description - /// Scenario configure - /// - public IMessageScenarios Add(string description, Func configure) - { - var builder = new MessageScenarioBuilder(description); - configure?.Invoke(builder).GetAwaiter().GetResult(); - - Scenario scenario = builder.Build(); - this.scenarios.Add(description, scenario); - - return this; - } } } diff --git a/tests/PactNet.Tests/Verifier/Messaging/MessageScenarioBuilderTests.cs b/tests/PactNet.Tests/Verifier/Messaging/MessageScenarioBuilderTests.cs index 68a319f5..c7969d35 100644 --- a/tests/PactNet.Tests/Verifier/Messaging/MessageScenarioBuilderTests.cs +++ b/tests/PactNet.Tests/Verifier/Messaging/MessageScenarioBuilderTests.cs @@ -64,22 +64,22 @@ public void WithContent_WithCustomSettings_SetsSettings() } [Fact] - public async Task WithContentAsync_WhenCalled_SetsContent() + public void WithAsyncContent_WhenCalled_SetsContent() { dynamic expected = new { Foo = 42 }; - await this.builder.WithContentAsync(() => Task.FromResult(expected)); + this.builder.WithAsyncContent(() => Task.FromResult(expected)); object actual = this.builder.Build().Invoke(); actual.Should().Be(expected); } [Fact] - public async Task WithContentAsync_WithCustomSettings_SetsSettings() + public void WithAsyncContent_WithCustomSettings_SetsSettings() { var expected = new JsonSerializerOptions(); - await this.builder.WithContentAsync(() => Task.FromResult(new { Foo = "Bar" }), expected); + this.builder.WithAsyncContent(() => Task.FromResult(new { Foo = "Bar" }), expected); var actual = this.builder.Build().JsonSettings; actual.Should().Be(expected); diff --git a/tests/PactNet.Tests/Verifier/Messaging/MessageScenariosTests.cs b/tests/PactNet.Tests/Verifier/Messaging/MessageScenariosTests.cs index 5f1c46ec..3aafa298 100644 --- a/tests/PactNet.Tests/Verifier/Messaging/MessageScenariosTests.cs +++ b/tests/PactNet.Tests/Verifier/Messaging/MessageScenariosTests.cs @@ -52,7 +52,7 @@ public void Add_AsyncBuilder_AddsScenario() Func> factory = () => Task.FromResult(new { Foo = 42 }); JsonSerializerOptions settings = new JsonSerializerOptions(); - this.scenarios.Add("description", async builder => await builder.WithMetadata(metadata).WithContentAsync(factory, settings)); + this.scenarios.Add("description", builder => builder.WithMetadata(metadata).WithAsyncContent(factory, settings)); this.scenarios.Scenarios.Should().BeEquivalentTo(new Dictionary {