Skip to content

feat!: Make the messaging provider handle scenario requests async #537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: dev/async-message-factory
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions samples/OrdersApi/Consumer.Tests/Consumer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
8 changes: 4 additions & 4 deletions samples/OrdersApi/Provider.Tests/Provider.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
18 changes: 13 additions & 5 deletions samples/OrdersApi/Provider.Tests/ProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using PactNet;
Expand All @@ -14,7 +15,7 @@

namespace Provider.Tests
{
public class ProviderTests : IDisposable
public class ProviderTests : IAsyncLifetime
{
private static readonly Uri ProviderUri = new("http://localhost:5000");

Expand All @@ -36,8 +37,6 @@ public ProviderTests(ITestOutputHelper output)
webBuilder.UseStartup<TestStartup>();
})
.Build();

this.server.Start();

this.verifier = new PactVerifier("Orders API", new PactVerifierConfig
{
Expand All @@ -49,10 +48,19 @@ public ProviderTests(ITestOutputHelper output)
});
}

public void Dispose()
/// <summary>
/// Called immediately after the class has been created, before it is used.
/// </summary>
public Task InitializeAsync()
{
this.server.Start();
return Task.CompletedTask;
}

public async Task DisposeAsync()
{
await this.verifier.DisposeAsync();
this.server.Dispose();
this.verifier.Dispose();
}

[Fact]
Expand Down
1 change: 1 addition & 0 deletions src/PactNet.Abstractions/PactNet.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace PactNet.Verifier.Messaging
{
Expand All @@ -20,6 +21,13 @@ public interface IMessageScenarios
/// <param name="factory">Message content factory</param>
IMessageScenarios Add(string description, Func<dynamic> factory);

/// <summary>
/// Add a message scenario
/// </summary>
/// <param name="description">Scenario description</param>
/// <param name="factory">Message content factory</param>
IMessageScenarios Add(string description, Func<Task<dynamic>> factory);

/// <summary>
/// Add a message scenario by configuring a scenario builder
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace PactNet.Verifier.Messaging
/// <summary>
/// Messaging provider service, which simulates messaging responses in order to verify interactions
/// </summary>
public interface IMessagingProvider : IDisposable
public interface IMessagingProvider : IAsyncDisposable
{
/// <summary>
/// Scenarios configured for the provider
Expand Down
44 changes: 38 additions & 6 deletions src/PactNet.Abstractions/Verifier/Messaging/Scenario.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Text.Json;
using System.Threading.Tasks;

namespace PactNet.Verifier.Messaging
{
Expand All @@ -8,7 +9,7 @@ namespace PactNet.Verifier.Messaging
/// </summary>
public class Scenario
{
private readonly Func<dynamic> factory;
private readonly Func<Task<dynamic>> factory;

/// <summary>
/// The description of the scenario
Expand All @@ -30,7 +31,16 @@ public class Scenario
/// </summary>
/// <param name="description">the scenario description</param>
/// <param name="factory">Message content factory</param>
public Scenario(string description, Func<dynamic> factory)
public Scenario(string description, Func<dynamic> factory) : this(description, Wrap(factory))
{
}

/// <summary>
/// Creates an instance of <see cref="Scenario"/>
/// </summary>
/// <param name="description">the scenario description</param>
/// <param name="factory">Message content factory</param>
public Scenario(string description, Func<Task<dynamic>> factory)
{
this.Description = !string.IsNullOrWhiteSpace(description) ? description : throw new ArgumentException("Description cannot be null or empty");
this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
Expand All @@ -44,19 +54,41 @@ public Scenario(string description, Func<dynamic> factory)
/// <param name="metadata">the metadata</param>
/// <param name="settings">Custom JSON serializer settings</param>
public Scenario(string description, Func<dynamic> factory, dynamic metadata, JsonSerializerOptions settings)
: this(description, Wrap(factory))
{
this.Metadata = metadata;
this.JsonSettings = settings;
}

/// <summary>
/// Creates an instance of <see cref="Scenario"/>
/// </summary>
/// <param name="description">the scenario description</param>
/// <param name="factory">Message content factory</param>
/// <param name="metadata">the metadata</param>
/// <param name="settings">Custom JSON serializer settings</param>
public Scenario(string description, Func<Task<dynamic>> factory, dynamic metadata, JsonSerializerOptions settings)
: this(description, factory)
{
this.Metadata = metadata;
this.JsonSettings = settings;
}

/// <summary>
/// Invoke a scenario
/// Invoke a scenario to generate message content
/// </summary>
/// <returns>The scenario message content</returns>
public dynamic Invoke()
public async Task<dynamic> InvokeAsync() => await this.factory();

/// <summary>
/// Wraps a sync factory to be async
/// </summary>
/// <param name="factory">Sync factory</param>
/// <returns>Async factory</returns>
private static Func<Task<dynamic>> Wrap(Func<dynamic> factory) => () =>
{
return this.factory.Invoke();
}
dynamic d = factory();
return Task.FromResult(d);
};
}
}
41 changes: 23 additions & 18 deletions src/PactNet/Verifier/Messaging/MessageScenarioBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal class MessageScenarioBuilder : IMessageScenarioBuilder
{
private readonly string description;

private Func<dynamic> factory;
private Func<Task<dynamic>> factory;
private dynamic metadata = new { ContentType = "application/json" };
private JsonSerializerOptions settings;

Expand All @@ -36,23 +36,37 @@ public IMessageScenarioBuilder WithMetadata(dynamic metadata)
}

/// <summary>
/// Set the action of the scenario
/// Set the content factory of the scenario. The factory is invoked each time the scenario is required.
/// </summary>
/// <param name="factory">Content factory</param>
public void WithContent(Func<dynamic> factory)
{
this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}

this.WithAsyncContent(() => Task.FromResult<dynamic>(factory()));
}

/// <summary>
/// Set the content of the scenario
/// Set the content factory of the scenario. The factory is invoked each time the scenario is required.
/// </summary>
/// <param name="factory">Content factory</param>
/// <param name="settings">Custom JSON serializer settings</param>
public void WithContent(Func<dynamic> factory, JsonSerializerOptions settings)
{
this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
this.settings = settings ?? throw new ArgumentNullException(nameof(settings));
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}

if (settings == null)
{
throw new ArgumentNullException(nameof(settings));
}

this.WithAsyncContent(() => Task.FromResult<dynamic>(factory()), settings);
}

/// <summary>
Expand All @@ -61,12 +75,7 @@ public void WithContent(Func<dynamic> factory, JsonSerializerOptions settings)
/// <param name="factory">Content factory</param>
public void WithAsyncContent(Func<Task<dynamic>> factory)
{
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}

this.WithContent(() => factory().GetAwaiter().GetResult());
this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
}

/// <summary>
Expand All @@ -76,12 +85,8 @@ public void WithAsyncContent(Func<Task<dynamic>> factory)
/// <param name="settings">Custom JSON serializer settings</param>
public void WithAsyncContent(Func<Task<dynamic>> factory, JsonSerializerOptions settings)
{
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}

this.WithContent(() => factory().GetAwaiter().GetResult(), settings);
this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
this.settings = settings ?? throw new ArgumentNullException(nameof(settings));
}

/// <summary>
Expand Down
13 changes: 13 additions & 0 deletions src/PactNet/Verifier/Messaging/MessageScenarios.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;

namespace PactNet.Verifier.Messaging
{
Expand Down Expand Up @@ -35,6 +36,18 @@ public MessageScenarios()
/// <param name="description">Scenario description</param>
/// <param name="factory">Message content factory</param>
public IMessageScenarios Add(string description, Func<dynamic> factory)
{
Func<Task<dynamic>> asyncFactory = () => Task.FromResult<dynamic>(factory());

return this.Add(description, asyncFactory);
}

/// <summary>
/// Add a message scenario
/// </summary>
/// <param name="description">Scenario description</param>
/// <param name="factory">Message content factory</param>
public IMessageScenarios Add(string description, Func<Task<dynamic>> factory)
{
var scenario = new Scenario(description, factory, JsonMetadata, null);
this.scenarios.Add(description, scenario);
Expand Down
Loading