diff --git a/examples/WireMock.Net.Client/Program.cs b/examples/WireMock.Net.Client/Program.cs index 84c77fa2..560195f0 100644 --- a/examples/WireMock.Net.Client/Program.cs +++ b/examples/WireMock.Net.Client/Program.cs @@ -43,11 +43,14 @@ namespace WireMock.Net.Client var request = api.GetRequestsAsync().Result; Console.WriteLine($"request = {JsonConvert.SerializeObject(request)}"); - var deleteRequestsAsync = api.DeleteRequestsAsync().Result; - Console.WriteLine($"deleteRequestsAsync = {deleteRequestsAsync.Status}"); + //var deleteRequestsAsync = api.DeleteRequestsAsync().Result; + //Console.WriteLine($"DeleteRequestsAsync = {deleteRequestsAsync.Status}"); - var resetRequestsAsync = api.ResetRequestsAsync().Result; - Console.WriteLine($"resetRequestsAsync = {resetRequestsAsync.Status}"); + //var resetRequestsAsync = api.ResetRequestsAsync().Result; + //Console.WriteLine($"ResetRequestsAsync = {resetRequestsAsync.Status}"); + + var scenarioStates = api.GetScenariosAsync().Result; + Console.WriteLine($"GetScenariosAsync = {JsonConvert.SerializeObject(scenarioStates)}"); Console.WriteLine("Press any key to quit"); Console.ReadKey(); diff --git a/src/WireMock.Net/Admin/Mappings/MappingModel.cs b/src/WireMock.Net/Admin/Mappings/MappingModel.cs index 200b5297..2e005729 100644 --- a/src/WireMock.Net/Admin/Mappings/MappingModel.cs +++ b/src/WireMock.Net/Admin/Mappings/MappingModel.cs @@ -30,13 +30,13 @@ namespace WireMock.Admin.Mappings /// /// Execution state condition for the current mapping. /// - public object WhenStateIs { get; set; } + public string WhenStateIs { get; set; } /// /// The next state which will be signaled after the current mapping execution. /// In case the value is null state will not be changed. /// - public object SetStateTo { get; set; } + public string SetStateTo { get; set; } /// /// The request. diff --git a/src/WireMock.Net/Admin/Scenarios/ScenarioStateModel.cs b/src/WireMock.Net/Admin/Scenarios/ScenarioStateModel.cs new file mode 100644 index 00000000..a6b1fa9e --- /dev/null +++ b/src/WireMock.Net/Admin/Scenarios/ScenarioStateModel.cs @@ -0,0 +1,28 @@ +namespace WireMock.Admin.Scenarios +{ + /// + /// ScenarioStateModel + /// + public class ScenarioStateModel + { + /// + /// Gets or sets the name. + /// + public string Name { get; set; } + + /// + /// Gets or sets the NextState. + /// + public string NextState { get; set; } + + /// + /// Gets or sets a value indicating whether this is started. + /// + public bool Started { get; set; } + + /// + /// Gets or sets a value indicating whether this is finished. + /// + public bool Finished { get; set; } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Client/IFluentMockServerAdmin.cs b/src/WireMock.Net/Client/IFluentMockServerAdmin.cs index c1539ea9..e7acb7d9 100644 --- a/src/WireMock.Net/Client/IFluentMockServerAdmin.cs +++ b/src/WireMock.Net/Client/IFluentMockServerAdmin.cs @@ -145,7 +145,7 @@ namespace WireMock.Client /// Get all scenarios /// [Get("__admin/scenarios")] - Task GetScenariosAsync(); + Task> GetScenariosAsync(); /// /// Delete (reset) all scenarios diff --git a/src/WireMock.Net/Mapping.cs b/src/WireMock.Net/Mapping.cs index 8eb56d73..31e40de9 100644 --- a/src/WireMock.Net/Mapping.cs +++ b/src/WireMock.Net/Mapping.cs @@ -41,14 +41,14 @@ namespace WireMock /// Execution state condition for the current mapping. /// [CanBeNull] - public object ExecutionConditionState { get; } + public string ExecutionConditionState { get; } /// /// The next state which will be signaled after the current mapping execution. - /// In case the value is null state will not be changed. + /// In case the value is null, state will not be changed. /// [CanBeNull] - public object NextState { get; } + public string NextState { get; } /// /// The Request matcher. @@ -77,7 +77,7 @@ namespace WireMock /// The scenario. [Optional] /// State in which the current mapping can occur. [Optional] /// The next state which will occur after the current mapping execution. [Optional] - public Mapping(Guid guid, [CanBeNull] string title, [CanBeNull] string path, IRequestMatcher requestMatcher, IResponseProvider provider, int priority, [CanBeNull] string scenario, [CanBeNull] object executionConditionState, [CanBeNull] object nextState) + public Mapping(Guid guid, [CanBeNull] string title, [CanBeNull] string path, IRequestMatcher requestMatcher, IResponseProvider provider, int priority, [CanBeNull] string scenario, [CanBeNull] string executionConditionState, [CanBeNull] string nextState) { Guid = guid; Title = title; @@ -106,7 +106,7 @@ namespace WireMock /// The request message. /// The Next State. /// The . - public RequestMatchResult GetRequestMatchResult(RequestMessage requestMessage, [CanBeNull] object nextState) + public RequestMatchResult GetRequestMatchResult(RequestMessage requestMessage, [CanBeNull] string nextState) { var result = new RequestMatchResult(); diff --git a/src/WireMock.Net/Matchers/Request/RequestMessageScenarioAndStateMatcher.cs b/src/WireMock.Net/Matchers/Request/RequestMessageScenarioAndStateMatcher.cs index 8bb0aeb7..f4ffde5d 100644 --- a/src/WireMock.Net/Matchers/Request/RequestMessageScenarioAndStateMatcher.cs +++ b/src/WireMock.Net/Matchers/Request/RequestMessageScenarioAndStateMatcher.cs @@ -11,21 +11,21 @@ namespace WireMock.Matchers.Request /// Execution state condition for the current mapping. /// [CanBeNull] - private readonly object _executionConditionState; + private readonly string _executionConditionState; /// /// The next state which will be signaled after the current mapping execution. /// In case the value is null state will not be changed. /// [CanBeNull] - private readonly object _nextState; + private readonly string _nextState; /// /// Initializes a new instance of the class. /// /// The next state. /// Execution state condition for the current mapping. - public RequestMessageScenarioAndStateMatcher([CanBeNull] object nextState, [CanBeNull] object executionConditionState) + public RequestMessageScenarioAndStateMatcher([CanBeNull] string nextState, [CanBeNull] string executionConditionState) { _nextState = nextState; _executionConditionState = executionConditionState; diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs index 67c85189..d7575c32 100644 --- a/src/WireMock.Net/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs @@ -60,7 +60,10 @@ namespace WireMock.Owin // Set start if (!_options.Scenarios.ContainsKey(mapping.Scenario) && mapping.IsStartState) { - _options.Scenarios.TryAdd(mapping.Scenario, null); + _options.Scenarios.TryAdd(mapping.Scenario, new ScenarioState + { + Name = mapping.Scenario + }); } } @@ -68,7 +71,7 @@ namespace WireMock.Owin .Select(m => new { Mapping = m, - MatchResult = m.GetRequestMatchResult(request, m.Scenario != null && _options.Scenarios.ContainsKey(m.Scenario) ? _options.Scenarios[m.Scenario] : null) + MatchResult = m.GetRequestMatchResult(request, m.Scenario != null && _options.Scenarios.ContainsKey(m.Scenario) ? _options.Scenarios[m.Scenario].NextState : null) }) .ToList(); @@ -125,7 +128,9 @@ namespace WireMock.Owin if (targetMapping.Scenario != null) { - _options.Scenarios[targetMapping.Scenario] = targetMapping.NextState; + _options.Scenarios[targetMapping.Scenario].NextState = targetMapping.NextState; + _options.Scenarios[targetMapping.Scenario].Started = true; + _options.Scenarios[targetMapping.Scenario].Finished = targetMapping.NextState == null; } } catch (Exception ex) diff --git a/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs b/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs index 694ec065..2111e7e8 100644 --- a/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs +++ b/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs @@ -22,9 +22,9 @@ namespace WireMock.Owin public bool AllowPartialMapping { get; set; } - public ConcurrentDictionary Mappings { get; } = new ConcurrentDictionary(); // Checked + public ConcurrentDictionary Mappings { get; } = new ConcurrentDictionary(); - public ConcurrentDictionary Scenarios { get; } = new ConcurrentDictionary(); // Checked + public ConcurrentDictionary Scenarios { get; } = new ConcurrentDictionary(); public ObservableCollection LogEntries { get; } = new ConcurentObservableCollection(); diff --git a/src/WireMock.Net/ScenarioState.cs b/src/WireMock.Net/ScenarioState.cs new file mode 100644 index 00000000..8a28da93 --- /dev/null +++ b/src/WireMock.Net/ScenarioState.cs @@ -0,0 +1,28 @@ +namespace WireMock +{ + /// + /// The ScenarioState + /// + public class ScenarioState + { + /// + /// Gets or sets the Name (from the Scenario). + /// + public string Name { get; set; } + + /// + /// Gets or sets the NextState. + /// + public string NextState { get; set; } + + /// + /// Gets or sets a value indicating whether this is started. + /// + public bool Started { get; set; } + + /// + /// Gets or sets a value indicating whether this is finished. + /// + public bool Finished { get; set; } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index 92069cc7..c5a11fa9 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -37,19 +37,16 @@ namespace WireMock.Serialization ClientIP = clientIPMatchers != null && clientIPMatchers.Any() ? new ClientIPModel { Matchers = MatcherMapper.Map(clientIPMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)) - //Funcs = Map(clientIPMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs)) } : null, Path = pathMatchers != null && pathMatchers.Any() ? new PathModel { Matchers = MatcherMapper.Map(pathMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)) - //Funcs = Map(pathMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs)) } : null, Url = urlMatchers != null && urlMatchers.Any() ? new UrlModel { Matchers = MatcherMapper.Map(urlMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)) - //Funcs = Map(urlMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs)) } : null, Methods = methodMatcher?.Methods, @@ -58,28 +55,23 @@ namespace WireMock.Serialization { Name = hm.Name, Matchers = MatcherMapper.Map(hm.Matchers) - //Funcs = Map(hm.Funcs) }).ToList() : null, Cookies = cookieMatchers != null && cookieMatchers.Any() ? cookieMatchers.Select(cm => new CookieModel { Name = cm.Name, Matchers = MatcherMapper.Map(cm.Matchers) - //Funcs = Map(cm.Funcs) }).ToList() : null, Params = paramsMatchers != null && paramsMatchers.Any() ? paramsMatchers.Select(pm => new ParamModel { Name = pm.Key, Matchers = MatcherMapper.Map(pm.Matchers) - //Funcs = Map(pm.Funcs) }).ToList() : null, Body = methodMatcher?.Methods != null && methodMatcher.Methods.Any(m => m == "get") ? null : new BodyModel { Matcher = bodyMatcher != null ? MatcherMapper.Map(bodyMatcher.Matcher) : null - //Func = bodyMatcher != null ? Map(bodyMatcher.Func) : null, - //DataFunc = bodyMatcher != null ? Map(bodyMatcher.DataFunc) : null } }, Response = new ResponseModel diff --git a/src/WireMock.Net/Server/FluentMockServer.Admin.cs b/src/WireMock.Net/Server/FluentMockServer.Admin.cs index ddb397cd..c32d2991 100644 --- a/src/WireMock.Net/Server/FluentMockServer.Admin.cs +++ b/src/WireMock.Net/Server/FluentMockServer.Admin.cs @@ -9,6 +9,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using WireMock.Admin.Mappings; +using WireMock.Admin.Scenarios; using WireMock.Admin.Settings; using WireMock.Http; using WireMock.Logging; @@ -41,7 +42,13 @@ namespace WireMock.Server private readonly JsonSerializerSettings _settings = new JsonSerializerSettings { Formatting = Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore, + NullValueHandling = NullValueHandling.Ignore + }; + + private readonly JsonSerializerSettings _settingsIncludeNullValues = new JsonSerializerSettings + { + Formatting = Formatting.Indented, + NullValueHandling = NullValueHandling.Include }; #region InitAdmin @@ -525,13 +532,15 @@ namespace WireMock.Server #region Scenarios private ResponseMessage ScenariosGet(RequestMessage requestMessage) { - var scenarios = Scenarios.ToArray().Select(s => new + var scenariosStates = Scenarios.Values.Select(s => new ScenarioStateModel { - Name = s.Key, - Started = s.Value != null, - NextState = s.Value + Name = s.Name, + NextState = s.NextState, + Started = s.Started, + Finished = s.Finished }); - return ToJson(scenarios); + + return ToJson(scenariosStates, true); } private ResponseMessage ScenariosReset(RequestMessage requestMessage) @@ -706,11 +715,11 @@ namespace WireMock.Server return responseBuilder; } - private ResponseMessage ToJson(T result) + private ResponseMessage ToJson(T result, bool keepNullValues = false) { return new ResponseMessage { - Body = JsonConvert.SerializeObject(result, _settings), + Body = JsonConvert.SerializeObject(result, keepNullValues ? _settingsIncludeNullValues : _settings), StatusCode = 200, Headers = new Dictionary> { { HttpKnownHeaderNames.ContentType, new WireMockList("application/json") } } }; diff --git a/src/WireMock.Net/Server/FluentMockServer.cs b/src/WireMock.Net/Server/FluentMockServer.cs index f4df8a4c..6b43b302 100644 --- a/src/WireMock.Net/Server/FluentMockServer.cs +++ b/src/WireMock.Net/Server/FluentMockServer.cs @@ -56,7 +56,7 @@ namespace WireMock.Server /// Gets the scenarios. /// [PublicAPI] - public ConcurrentDictionary Scenarios => new ConcurrentDictionary(_options.Scenarios); + public ConcurrentDictionary Scenarios => new ConcurrentDictionary(_options.Scenarios); #region IDisposable Members /// diff --git a/src/WireMock.Net/Server/IRespondWithAProvider.cs b/src/WireMock.Net/Server/IRespondWithAProvider.cs index 5c430bae..a2d67e31 100644 --- a/src/WireMock.Net/Server/IRespondWithAProvider.cs +++ b/src/WireMock.Net/Server/IRespondWithAProvider.cs @@ -66,13 +66,13 @@ namespace WireMock.Server /// /// Any object which identifies the current state /// The . - IRespondWithAProvider WhenStateIs(object state); + IRespondWithAProvider WhenStateIs(string state); /// /// Once this mapping is executed the state will be changed to specified one. /// /// Any object which identifies the new state /// The . - IRespondWithAProvider WillSetStateTo(object state); + IRespondWithAProvider WillSetStateTo(string state); } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/RespondWithAProvider.cs b/src/WireMock.Net/Server/RespondWithAProvider.cs index 71b16072..b4325d69 100644 --- a/src/WireMock.Net/Server/RespondWithAProvider.cs +++ b/src/WireMock.Net/Server/RespondWithAProvider.cs @@ -12,8 +12,8 @@ namespace WireMock.Server private int _priority; private string _title; private string _path; - private object _executionConditionState; - private object _nextState; + private string _executionConditionState; + private string _nextState; private string _scenario; private readonly RegistrationCallback _registrationCallback; private readonly IRequestMatcher _requestMatcher; @@ -87,25 +87,20 @@ namespace WireMock.Server } /// - public IRespondWithAProvider WhenStateIs(object state) + public IRespondWithAProvider WhenStateIs(string state) { if (string.IsNullOrEmpty(_scenario)) { throw new NotSupportedException("Unable to set state condition when no scenario is defined."); } - //if (_nextState != null) - //{ - // throw new NotSupportedException("Unable to set state condition when next state is defined."); - //} - _executionConditionState = state; return this; } /// - public IRespondWithAProvider WillSetStateTo(object state) + public IRespondWithAProvider WillSetStateTo(string state) { if (string.IsNullOrEmpty(_scenario)) { diff --git a/test/WireMock.Net.Tests/StatefulBehaviorTests.cs b/test/WireMock.Net.Tests/StatefulBehaviorTests.cs index 098f0840..53738249 100644 --- a/test/WireMock.Net.Tests/StatefulBehaviorTests.cs +++ b/test/WireMock.Net.Tests/StatefulBehaviorTests.cs @@ -1,4 +1,5 @@ -using System.Net; +using System.Linq; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using NFluent; @@ -12,7 +13,7 @@ namespace WireMock.Net.Tests public class StatefulBehaviorTests { [Fact] - public async Task Should_skip_non_relevant_states() + public async Task Scenarios_Should_skip_non_relevant_states() { // given var server = FluentMockServer.Start(); @@ -33,7 +34,7 @@ namespace WireMock.Net.Tests } [Fact] - public async Task Should_process_request_if_equals_state_and_single_state_defined() + public async Task Scenarios_Should_process_request_if_equals_state_and_single_state_defined() { // given var server = FluentMockServer.Start(); @@ -62,7 +63,7 @@ namespace WireMock.Net.Tests } [Fact] - public async Task Scenario_and_State_TodoList_Example() + public async Task Scenarios_TodoList_Example() { // Assign var server = FluentMockServer.Start(); @@ -86,22 +87,39 @@ namespace WireMock.Net.Tests .WhenStateIs("Cancel newspaper item added") .RespondWith(Response.Create().WithBody("Buy milk;Cancel newspaper subscription")); + Check.That(server.Scenarios.Any()).IsFalse(); + // Act and Assert string url = "http://localhost:" + server.Ports[0]; string getResponse1 = new HttpClient().GetStringAsync(url + "/todo/items").Result; Check.That(getResponse1).Equals("Buy milk"); + Check.That(server.Scenarios["To do list"].Name).IsEqualTo("To do list"); + Check.That(server.Scenarios["To do list"].NextState).IsEqualTo("TodoList State Started"); + Check.That(server.Scenarios["To do list"].Started).IsTrue(); + Check.That(server.Scenarios["To do list"].Finished).IsFalse(); + var postResponse = await new HttpClient().PostAsync(url + "/todo/items", new StringContent("Cancel newspaper subscription")); Check.That(postResponse.StatusCode).Equals(HttpStatusCode.Created); + Check.That(server.Scenarios["To do list"].Name).IsEqualTo("To do list"); + Check.That(server.Scenarios["To do list"].NextState).IsEqualTo("Cancel newspaper item added"); + Check.That(server.Scenarios["To do list"].Started).IsTrue(); + Check.That(server.Scenarios["To do list"].Finished).IsFalse(); + string getResponse2 = await new HttpClient().GetStringAsync(url + "/todo/items"); Check.That(getResponse2).Equals("Buy milk;Cancel newspaper subscription"); + Check.That(server.Scenarios["To do list"].Name).IsEqualTo("To do list"); + Check.That(server.Scenarios["To do list"].NextState).IsNull(); + Check.That(server.Scenarios["To do list"].Started).IsTrue(); + Check.That(server.Scenarios["To do list"].Finished).IsTrue(); + server.Dispose(); } - // [Fact] - public async Task Should_process_request_if_equals_state_and_multiple_state_defined() + [Fact] + public async Task Scenarios_Should_process_request_if_equals_state_and_multiple_state_defined() { // Assign var server = FluentMockServer.Start(); @@ -113,7 +131,7 @@ namespace WireMock.Net.Tests .RespondWith(Response.Create().WithBody("No state msg 1")); server - .Given(Request.Create().WithPath("/fooX").UsingGet()) + .Given(Request.Create().WithPath("/foo1X").UsingGet()) .InScenario("s1") .WhenStateIs("Test state 1") .RespondWith(Response.Create().WithBody("Test state msg 1")); @@ -125,7 +143,7 @@ namespace WireMock.Net.Tests .RespondWith(Response.Create().WithBody("No state msg 2")); server - .Given(Request.Create().WithPath("/fooX").UsingGet()) + .Given(Request.Create().WithPath("/foo2X").UsingGet()) .InScenario("s2") .WhenStateIs("Test state 2") .RespondWith(Response.Create().WithBody("Test state msg 2")); @@ -138,10 +156,10 @@ namespace WireMock.Net.Tests var responseNoState2 = await new HttpClient().GetStringAsync(url + "/state2"); Check.That(responseNoState2).Equals("No state msg 2"); - var responseWithState1 = await new HttpClient().GetStringAsync(url + "/fooX"); + var responseWithState1 = await new HttpClient().GetStringAsync(url + "/foo1X"); Check.That(responseWithState1).Equals("Test state msg 1"); - var responseWithState2 = await new HttpClient().GetStringAsync(url + "/fooX"); + var responseWithState2 = await new HttpClient().GetStringAsync(url + "/foo2X"); Check.That(responseWithState2).Equals("Test state msg 2"); server.Dispose();