Skip to content

Commit 3c759d6

Browse files
committed
Request matching: option to only match specific HTTP methods
1 parent 2244684 commit 3c759d6

File tree

8 files changed

+160
-15
lines changed

8 files changed

+160
-15
lines changed

UnitTests/TestEndpoint.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public void ResolveMoreThanOneMatcher()
2424
endpoint.Add(new RegexMatcher("foo"), new LiteralResponse("foobar"));
2525
endpoint.Add(new AnyMatcher(), new LiteralResponse("foobar"));
2626

27-
var firstmatch = endpoint.Resolve(new Microsoft.AspNetCore.Http.PathString(""), new Microsoft.AspNetCore.Http.QueryString(""), "foo", null);
27+
var firstmatch = endpoint.Resolve("GET", new Microsoft.AspNetCore.Http.PathString(""), new Microsoft.AspNetCore.Http.QueryString(""), "foo", null);
2828
Assert.False(firstmatch.SingleMatch);
2929
Assert.IsType(typeof(RegexMatcher), firstmatch.RequestMatcher);
3030
}
@@ -36,7 +36,7 @@ public void ResolveOnlyOne()
3636
endpoint.Add(new RegexMatcher("foo"), new LiteralResponse("foobar"));
3737
endpoint.Add(new AnyMatcher(), new LiteralResponse("foobar"));
3838

39-
var firstmatch = endpoint.Resolve(new Microsoft.AspNetCore.Http.PathString(""), new Microsoft.AspNetCore.Http.QueryString(), "bar", null);
39+
var firstmatch = endpoint.Resolve("GET", new Microsoft.AspNetCore.Http.PathString(""), new Microsoft.AspNetCore.Http.QueryString(), "bar", null);
4040
Assert.True(firstmatch.SingleMatch);
4141
Assert.IsType(typeof(AnyMatcher), firstmatch.RequestMatcher);
4242

UnitTests/TestMatchHttpMethod.cs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Xunit;
6+
using netmockery;
7+
8+
namespace UnitTests
9+
{
10+
public class TestMatchHttpMethod
11+
{
12+
[Fact]
13+
public void MatchAnyMethod()
14+
{
15+
var matcher = new JSONRequestMatcher().CreateRequestMatcher() as AnyMatcher;
16+
Assert.NotNull(matcher);
17+
Assert.True(matcher.MatchesHttpMethod("POST"));
18+
Assert.True(matcher.MatchesHttpMethod("GET"));
19+
Assert.True(matcher.MatchesHttpMethod("PUT"));
20+
Assert.True(matcher.MatchesHttpMethod("HEAD"));
21+
}
22+
23+
[Fact]
24+
public void MatchOnlySpecific()
25+
{
26+
var jsonMatcher = new JSONRequestMatcher
27+
{
28+
methods = "POST PUT"
29+
};
30+
31+
var matcher = jsonMatcher.CreateRequestMatcher() as AnyMatcher;
32+
Assert.NotNull(matcher);
33+
Assert.True(matcher.MatchesHttpMethod("POST"));
34+
Assert.False(matcher.MatchesHttpMethod("GET"));
35+
Assert.True(matcher.MatchesHttpMethod("PUT"));
36+
Assert.False(matcher.MatchesHttpMethod("HEAD"));
37+
}
38+
39+
[Fact]
40+
public void IsCaseInsensitive()
41+
{
42+
var jsonMatcher = new JSONRequestMatcher
43+
{
44+
methods = "poST Put"
45+
};
46+
47+
var matcher = jsonMatcher.CreateRequestMatcher() as AnyMatcher;
48+
Assert.NotNull(matcher);
49+
Assert.True(matcher.MatchesHttpMethod("post"));
50+
Assert.True(matcher.MatchesHttpMethod("PUT"));
51+
}
52+
53+
[Fact]
54+
public void RegexMatcher()
55+
{
56+
var matcher = (new JSONRequestMatcher { methods = "post", regex = "foobar" }).CreateRequestMatcher() as RegexMatcher;
57+
Assert.NotNull(matcher);
58+
Assert.True(matcher.MatchesHttpMethod("post"));
59+
Assert.False(matcher.MatchesHttpMethod("get"));
60+
}
61+
62+
[Fact]
63+
public void TestCaseDefaultMethodIsGet()
64+
{
65+
var testCase = (new JSONTest { requestpath = "/foo/bar" }).CreateTestCase(".");
66+
Assert.Equal("GET", testCase.Method);
67+
}
68+
69+
[Fact]
70+
public void CanSpecifyMethodInJson()
71+
{
72+
var testCase = (new JSONTest { requestpath = "/foo/bar", method = "POST" }).CreateTestCase(".");
73+
Assert.Equal("POST", testCase.Method);
74+
}
75+
76+
[Fact]
77+
public void Foo()
78+
{
79+
var endpoint = (new JSONEndpoint
80+
{
81+
name = "endpoint",
82+
pathregex = "/",
83+
responses = new[]
84+
{
85+
new JSONResponse { match = new JSONRequestMatcher { methods = "POST" }, literal = "Response from POST" },
86+
new JSONResponse { match = new JSONRequestMatcher {methods ="GET" }, literal = "Response from GET" }
87+
}
88+
}).CreateEndpoint(".", null);
89+
Assert.NotNull(endpoint);
90+
Assert.Equal(2, endpoint.Responses.Count());
91+
92+
var getTestCase = new NetmockeryTestCase { Method = "GET", RequestPath = "/", ExpectedResponseBody = "Response from GET" };
93+
Assert.True(getTestCase.Execute(EndpointCollection.WithEndpoints(endpoint)).OK);
94+
95+
var postTestCase = new NetmockeryTestCase { Method = "POST", RequestPath = "/", ExpectedResponseBody = "Response from POST" };
96+
Assert.True(postTestCase.Execute(EndpointCollection.WithEndpoints(endpoint)).OK);
97+
}
98+
}
99+
}

netmockery/Endpoint.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class Endpoint
2121
private string _name;
2222
private string _pathregex;
2323
private List<Tuple<RequestMatcher, ResponseCreator>> _responses = new List<Tuple<RequestMatcher, ResponseCreator>>();
24-
private bool _anyHasBeenAdded = false;
24+
private bool _ruleThatCatchesEveryThingHasBeenAdded = false;
2525

2626
public Endpoint(string name, string pathregex)
2727
{
@@ -57,22 +57,27 @@ public void Add(RequestMatcher requestMatcher, ResponseCreator responseCreator)
5757
Debug.Assert(responseCreator != null);
5858
Debug.Assert(requestMatcher.Index == -1);
5959

60-
if (_anyHasBeenAdded)
60+
if (_ruleThatCatchesEveryThingHasBeenAdded)
6161
{
62-
throw new ArgumentException("The endpoint contains a response matching any request, you cannot add more responses");
62+
throw new ArgumentException("The endpoint contains a response matching any request/method, you cannot add more responses");
6363
}
6464

6565
requestMatcher.Index = _responses.Count;
6666
_responses.Add(Tuple.Create(requestMatcher, responseCreator));
67-
if (requestMatcher is AnyMatcher)
67+
if (requestMatcher is AnyMatcher && requestMatcher.MatchesAnyHttpMethod)
6868
{
69-
_anyHasBeenAdded = true;
69+
_ruleThatCatchesEveryThingHasBeenAdded = true;
7070
}
7171
}
7272

73-
public ResolutionResult Resolve(PathString path, QueryString queryString, string body, IHeaderDictionary headers)
73+
public ResolutionResult Resolve(string httpMethod, PathString path, QueryString queryString, string body, IHeaderDictionary headers)
7474
{
75-
var candidates = (from t in _responses where t.Item1.Matches(path, queryString, body, headers) select t).Take(2);
75+
var candidates = (
76+
from t in _responses
77+
where t.Item1.MatchesHttpMethod(httpMethod) && t.Item1.Matches(path, queryString, body, headers)
78+
select t
79+
).Take(2);
80+
7681
if (! candidates.Any())
7782
{
7883
return null;

netmockery/EndpointCollection.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,16 @@ public Endpoint Resolve(string path)
3636
{
3737
return (from endpoint in _endpoints where endpoint.Matches(path) select endpoint).SingleOrDefault();
3838
}
39+
40+
static public EndpointCollection WithEndpoints(params Endpoint[] endpoints)
41+
{
42+
var retval = new EndpointCollection();
43+
foreach (var endpoint in endpoints)
44+
{
45+
retval.Add(endpoint);
46+
}
47+
48+
return retval;
49+
}
3950
}
4051
}

netmockery/JSONReader.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public static Endpoint ReadEndpoint(string jsonString, string rootDir, JSONDefau
2121
public class JSONTest
2222
{
2323
public string name;
24+
public string method;
2425
public string requestpath;
2526
public string querystring;
2627
public string requestbody;
@@ -50,6 +51,7 @@ public NetmockeryTestCase CreateTestCase(string directory)
5051
{
5152
return new NetmockeryTestCase {
5253
Name = name,
54+
Method = method ?? "GET",
5355
RequestPath = requestpath,
5456
QueryString = querystring,
5557
RequestBody =
@@ -189,12 +191,14 @@ from jsonreplacement in replacements
189191

190192
public class JSONRequestMatcher
191193
{
194+
public string methods;
192195
public string xpath;
193196
public string regex;
194197
public JSONXPathNamespace[] namespaces;
195198

196199
public RequestMatcher CreateRequestMatcher()
197200
{
201+
RequestMatcher retval;
198202
if (xpath != null)
199203
{
200204
var xpathMatcher = new XPathMatcher(xpath);
@@ -205,13 +209,21 @@ public RequestMatcher CreateRequestMatcher()
205209
xpathMatcher.AddNamespace(jsonNs.prefix, jsonNs.ns);
206210
}
207211
}
208-
return xpathMatcher;
212+
retval = xpathMatcher;
209213
}
210214
else if (regex != null)
211215
{
212-
return new RegexMatcher(regex);
216+
retval = new RegexMatcher(regex);
217+
}
218+
else
219+
{
220+
retval = new AnyMatcher();
221+
}
222+
if (methods != null)
223+
{
224+
retval.SetMatchingHttpMethods(methods);
213225
}
214-
return new AnyMatcher();
226+
return retval;
215227
}
216228
}
217229

netmockery/NetmockeryTestCase.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ public string GetWrittenResponseAsString()
8383
public class NetmockeryTestCase
8484
{
8585
public string Name;
86+
87+
public string Method = "GET";
8688
public string RequestPath;
8789
public string QueryString;
8890
public string RequestBody;
@@ -235,7 +237,7 @@ public NetmockeryTestCaseResult Execute(EndpointCollection endpointCollection, b
235237
}
236238
testResult.EndpointName = endpoint.Name;
237239

238-
var matcher_and_creator = endpoint.Resolve(new PathString(RequestPath), new QueryString(QueryString), RequestBody ?? "", null);
240+
var matcher_and_creator = endpoint.Resolve(Method, new PathString(RequestPath), new QueryString(QueryString), RequestBody ?? "", null);
239241
if (matcher_and_creator == null)
240242
{
241243
return testResult.SetFailure(ERROR_ENDPOINT_HAS_NO_MATCH);
@@ -299,7 +301,7 @@ public Tuple<string, string> GetResponse(EndpointCollection endpointCollection,
299301
{
300302
return Tuple.Create((string)null, ERROR_NOMATCHING_ENDPOINT);
301303
}
302-
var matcher_and_creator = endpoint.Resolve(new PathString(RequestPath), new QueryString(QueryString), RequestBody ?? "", null);
304+
var matcher_and_creator = endpoint.Resolve(Method, new PathString(RequestPath), new QueryString(QueryString), RequestBody ?? "", null);
303305
if (matcher_and_creator != null)
304306
{
305307
var responseCreator = matcher_and_creator.ResponseCreator as SimpleResponseCreator;

netmockery/RequestMatcher.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,23 @@ namespace netmockery
1616
public abstract class RequestMatcher
1717
{
1818
public int Index = -1;
19+
20+
private string[] _matchingHttpMethods;
21+
22+
public void SetMatchingHttpMethods(string methods) {
23+
Debug.Assert(methods != null);
24+
_matchingHttpMethods = (from part in methods.Split(' ') where part.Length > 0 select part.ToLower()).ToArray();
25+
}
26+
27+
public bool MatchesAnyHttpMethod => _matchingHttpMethods == null || _matchingHttpMethods.Length == 0;
28+
1929
public abstract bool Matches(PathString path, QueryString queryString, string body, IHeaderDictionary headers);
30+
31+
public bool MatchesHttpMethod(string method)
32+
{
33+
Debug.Assert(method != null);
34+
return MatchesAnyHttpMethod || _matchingHttpMethods.Contains(method.ToLower());
35+
}
2036
}
2137

2238
public class AnyMatcher : RequestMatcher

netmockery/Startup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public async Task HandleRequestInner(ResponseRegistryItem responseRegistryItem,
7878
responseRegistryItem.Endpoint = endpoint;
7979
if (endpoint != null)
8080
{
81-
var matcher_and_creator = endpoint.Resolve(context.Request.Path, context.Request.QueryString, requestBody, context.Request.Headers);
81+
var matcher_and_creator = endpoint.Resolve(context.Request.Method, context.Request.Path, context.Request.QueryString, requestBody, context.Request.Headers);
8282
if (matcher_and_creator != null)
8383
{
8484
var responseCreator = matcher_and_creator.ResponseCreator;

0 commit comments

Comments
 (0)