Skip to content
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

stateful behavior #49

Closed
wants to merge 3 commits into from
Closed
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
28 changes: 28 additions & 0 deletions src/WireMock.Net/Mapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ public class Mapping
/// </value>
public int Priority { get; }

/// <summary>
/// Execution state condition for the current mapping.
/// </summary>
public object ExecutionConditionState { get; }

/// <summary>
/// The next state which will be signaled after the current mapping execution.
/// In case the value is null state will not be changed.
/// </summary>
public object NextState { get; }

/// <summary>
/// The Request matcher.
/// </summary>
Expand All @@ -53,8 +64,25 @@ public class Mapping
/// <param name="provider">The provider.</param>
/// <param name="priority">The priority for this mapping.</param>
public Mapping(Guid guid, [CanBeNull] string title, IRequestMatcher requestMatcher, IResponseProvider provider, int priority)
: this(guid, title, requestMatcher, provider, priority, null, null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary>
/// <param name="guid">The unique identifier.</param>
/// <param name="title">The unique title (can be null_.</param>
/// <param name="requestMatcher">The request matcher.</param>
/// <param name="provider">The provider.</param>
/// <param name="priority">The priority for this mapping.</param>
/// <param name="executionConditionState">State in which the current mapping can occur. Happens if not null</param>
/// <param name="nextState">The next state which will occur after the current mapping execution. Happens if not null</param>
public Mapping(Guid guid, [CanBeNull] string title, IRequestMatcher requestMatcher, IResponseProvider provider, int priority, object executionConditionState, object nextState)
{
Priority = priority;
ExecutionConditionState = executionConditionState;
NextState = nextState;
Guid = guid;
Title = title;
RequestMatcher = requestMatcher;
Expand Down
7 changes: 7 additions & 0 deletions src/WireMock.Net/Owin/WireMockMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,19 @@ internal class WireMockMiddleware
private readonly OwinRequestMapper _requestMapper = new OwinRequestMapper();
private readonly OwinResponseMapper _responseMapper = new OwinResponseMapper();

public object State { get; private set; }

#if !NETSTANDARD
public WireMockMiddleware(OwinMiddleware next, WireMockMiddlewareOptions options) : base(next)
{
_options = options;
State = null;
}
#else
public WireMockMiddleware(RequestDelegate next, WireMockMiddlewareOptions options)
{
_options = options;
State = null;
}
#endif

Expand All @@ -51,6 +55,7 @@ public async Task Invoke(HttpContext ctx)
try
{
var mappings = _options.Mappings
.Where(m => object.Equals(m.ExecutionConditionState, State))
.Select(m => new
{
Mapping = m,
Expand Down Expand Up @@ -107,6 +112,8 @@ public async Task Invoke(HttpContext ctx)
}

response = await targetMapping.ResponseToAsync(request);
if (targetMapping.NextState != null)
State = targetMapping.NextState;
}
catch (Exception ex)
{
Expand Down
3 changes: 2 additions & 1 deletion src/WireMock.Net/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("WireMock.Net.StandAlone")]
[assembly: InternalsVisibleTo("WireMock.Net.StandAlone")]
[assembly: InternalsVisibleTo("WireMock.Net.Tests")]
14 changes: 14 additions & 0 deletions src/WireMock.Net/Server/IRespondWithAProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,19 @@ public interface IRespondWithAProvider
/// </summary>
/// <param name="provider">The provider.</param>
void RespondWith(IResponseProvider provider);

/// <summary>
/// Execute this respond only in case the current state is equal to specified one
/// </summary>
/// <param name="state">Any object which identifies the current state</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WhenStateIs(object state);

/// <summary>
/// Once this mapping is executed the state will be changed to specified one
/// </summary>
/// <param name="state">Any object which identifies the new state</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WillSetStateTo(object state);
}
}
21 changes: 20 additions & 1 deletion src/WireMock.Net/Server/RespondWithAProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using JetBrains.Annotations;
using WireMock.Matchers.Request;

namespace WireMock.Server
Expand All @@ -12,6 +13,9 @@ internal class RespondWithAProvider : IRespondWithAProvider
private Guid? _guid;
private string _title;

private object _executionConditionState = null;
private object _nextState = null;

/// <summary>
/// The _registration callback.
/// </summary>
Expand Down Expand Up @@ -42,7 +46,22 @@ public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestM
public void RespondWith(IResponseProvider provider)
{
var mappingGuid = _guid ?? Guid.NewGuid();
_registrationCallback(new Mapping(mappingGuid, _title, _requestMatcher, provider, _priority));
_registrationCallback(new Mapping(mappingGuid, _title, _requestMatcher, provider, _priority, _executionConditionState, _nextState));
}

public IRespondWithAProvider WhenStateIs(object state)
{
_executionConditionState = state;
return this;
}

public IRespondWithAProvider WillSetStateTo([NotNull] object state)
{
if (state == null)
throw new ArgumentException("The next state is not expected to be null", nameof(state));

_nextState = state;
return this;
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion test/WireMock.Net.Tests/FluentMockServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ public async Task FluentMockServer_Logging_SetMaxRequestLogCount()

var requestLoggedB = _server.LogEntries.Last();
Check.That(requestLoggedB.RequestMessage.Path).EndsWith("/foo3");
}
}

public void Dispose()
{
Expand Down
112 changes: 112 additions & 0 deletions test/WireMock.Net.Tests/StatefulBehaviorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NFluent;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using Xunit;

namespace WireMock.Net.Tests
{
public class StatefulBehaviorTests : IDisposable
{
private FluentMockServer _server;

[Fact]
public async Task Should_skip_non_relevant_states()
{
// given
_server = FluentMockServer.Start();

_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.WhenStateIs("Test state")
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody(@"{ msg: ""Hello world!""}"));

// when
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");

// then
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
}

[Fact]
public async Task Should_process_request_if_equals_state()
{
// given
_server = FluentMockServer.Start();

_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.WillSetStateTo("Test state")
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody(@"No state msg"));

_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.WhenStateIs("Test state")
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody(@"Test state msg"));

// when
var responseNoState = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
var responseWithState = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");

// then
Check.That(responseNoState).Equals("No state msg");
Check.That(responseWithState).Equals("Test state msg");
}

[Fact]
public async Task Should_not_change_state_if_next_state_is_not_defined()
{
// given
_server = FluentMockServer.Start();

_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.WillSetStateTo("Test state")
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody(@"No state msg"));

_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.WhenStateIs("Test state")
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody(@"Test state msg"));

// when
var responseNoState = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
var responseWithState = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
var responseWithStateNotChanged = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");

// then
Check.That(responseNoState).Equals("No state msg");
Check.That(responseWithState).Equals("Test state msg");
Check.That(responseWithStateNotChanged).Equals("Test state msg");
}

public void Dispose()
{
_server?.Dispose();
}
}
}
49 changes: 49 additions & 0 deletions test/WireMock.Net.Tests/WireMockMiddlewareTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Owin;
using Moq;
using NFluent;
using WireMock.Owin;
using Xunit;

namespace WireMock.Net.Tests
{
public class WireMockMiddlewareTests
{
private readonly ObjectMother _objectMother = new ObjectMother();

[Fact]
public void Should_have_default_state_as_null()
{
// given

// when
var sut = _objectMother.Create();

// then
Check.That(sut.State).IsNull();
}

internal class ObjectMother
{
public Mock<OwinMiddleware> OwinMiddleware { get; set; }
public Mock<IOwinContext> OwinContext { get; set; }
public WireMockMiddlewareOptions WireMockMiddlewareOptions { get; set; }

public ObjectMother()
{
OwinContext = new Mock<IOwinContext>();
OwinMiddleware = new Mock<OwinMiddleware>(null);
WireMockMiddlewareOptions = new WireMockMiddlewareOptions();
}

public WireMockMiddleware Create()
{
return new WireMockMiddleware(OwinMiddleware.Object, WireMockMiddlewareOptions);
}
}
}
}