#508 more test asserts for headers, cookies

This commit is contained in:
Todd 2020-03-28 13:48:19 -05:00
parent ddc709286a
commit 4724c709dc
3 changed files with 138 additions and 63 deletions

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -24,9 +24,8 @@ namespace Flurl.Test.Http
await "https://cookies.com/2".WithCookies(cookies).GetAsync();
await "https://cookies.com/3".WithCookies(cookies).GetAsync();
HttpTest.ShouldHaveMadeACall().WithCookie("x", "foo").Times(3);
HttpTest.ShouldHaveMadeACall().WithCookie("y", "bar").Times(2);
HttpTest.ShouldHaveMadeACall().WithCookie("y", "bazz").Times(1);
HttpTest.ShouldHaveMadeACall().WithCookies(new { x = "foo", y = "bar" }).Times(2);
HttpTest.ShouldHaveMadeACall().WithCookies(new { x = "foo", y = "bazz" }).Times(1);
Assert.AreEqual(2, cookies.Count);
Assert.AreEqual("foo", cookies["x"].Value);
@ -47,9 +46,8 @@ namespace Flurl.Test.Http
await "https://cookies.com/2".WithCookies(cookies).GetAsync();
await "https://cookies.com/3".WithCookies(cookies).GetAsync();
HttpTest.ShouldHaveMadeACall().WithCookie("x", "foo").Times(4);
HttpTest.ShouldHaveMadeACall().WithCookie("y", "bar").Times(3);
HttpTest.ShouldHaveMadeACall().WithCookie("y", "bazz").Times(1);
HttpTest.ShouldHaveMadeACall().WithCookies(new { x = "foo", y = "bar" }).Times(3);
HttpTest.ShouldHaveMadeACall().WithCookies(new { x = "foo", y = "bazz" }).Times(1);
Assert.AreEqual(2, cookies.Count);
Assert.AreEqual("foo", cookies["x"].Value);
@ -71,9 +69,8 @@ namespace Flurl.Test.Http
await cs.Request("2").GetAsync();
await cs.Request("3").GetAsync();
HttpTest.ShouldHaveMadeACall().WithCookie("x", "foo").Times(3);
HttpTest.ShouldHaveMadeACall().WithCookie("y", "bar").Times(2);
HttpTest.ShouldHaveMadeACall().WithCookie("y", "bazz").Times(1);
HttpTest.ShouldHaveMadeACall().WithCookies(new { x = "foo", y = "bar" }).Times(2);
HttpTest.ShouldHaveMadeACall().WithCookies(new { x = "foo", y = "bazz" }).Times(1);
Assert.AreEqual(2, cs.Cookies.Count);
Assert.AreEqual("foo", cs.Cookies["x"].Value);

View File

@ -114,14 +114,14 @@ namespace Flurl.Http.Testing
#region query params
/// <summary>
/// Asserts whether calls were made containing the given query parameter name and (optionally) value.
/// Asserts whether calls were made containing the given query parameter name and (optionally) value. value may contain * wildcard.
/// </summary>
public HttpCallAssertion WithQueryParam(string name, object value = null) {
return With(c => c.HasQueryParam(name, value), BuildDescrip("query param", name, value));
}
/// <summary>
/// Asserts whether calls were made NOT containing the given query parameter and (optionally) value.
/// Asserts whether calls were made NOT containing the given query parameter and (optionally) value. value may contain * wildcard.
/// </summary>
public HttpCallAssertion WithoutQueryParam(string name, object value = null) {
return Without(c => c.HasQueryParam(name, value), BuildDescrip("no query param", name, value));
@ -172,39 +172,123 @@ namespace Flurl.Http.Testing
#region headers
/// <summary>
/// Asserts whether calls were made containing the given request header and (optionally) value.
/// value may contain * wildcard.
/// Asserts whether calls were made containing the given header name and (optionally) value. value may contain * wildcard.
/// </summary>
public HttpCallAssertion WithHeader(string name, string valuePattern = "*", string descrip = null) {
descrip = descrip ?? BuildDescrip("header", name, valuePattern);
return With(c => c.HasHeader(name, valuePattern), descrip);
public HttpCallAssertion WithHeader(string name, object value = null) {
return With(c => c.HasHeader(name, value), BuildDescrip("header", name, value));
}
/// <summary>
/// Asserts whether calls were made that do NOT contain the given request header and (optionally) value.
/// value may contain * wildcard.
/// Asserts whether calls were made NOT containing the given header and (optionally) value. value may contain * wildcard.
/// </summary>
public HttpCallAssertion WithoutHeader(string name, string valuePattern = "*") {
return Without(c => c.HasHeader(name, valuePattern), BuildDescrip("no header", name, valuePattern));
public HttpCallAssertion WithoutHeader(string name, object value = null) {
return Without(c => c.HasHeader(name, value), BuildDescrip("no header", name, value));
}
/// <summary>
/// Asserts whether calls were made containing ALL the given headers (regardless of their values).
/// </summary>
public HttpCallAssertion WithHeaders(params string[] names) {
return names.Select(n => WithHeader(n)).LastOrDefault() ?? this;
}
/// <summary>
/// Asserts whether calls were made NOT containing any of the given headers.
/// If no names are provided, asserts no calls were made with any headers.
/// </summary>
public HttpCallAssertion WithoutHeaders(params string[] names) {
if (!names.Any())
return With(c => !c.Request.Headers.Any(), "no headers");
return names.Select(n => WithoutHeader(n)).LastOrDefault() ?? this;
}
/// <summary>
/// Asserts whether calls were made containing ANY the given headers (regardless of their values).
/// If no names are provided, asserts that calls were made containing at least one header with any name.
/// </summary>
public HttpCallAssertion WithAnyHeader(params string[] names) {
var descrip = $"any header {string.Join(", ", names)}".Trim();
return With(call => {
if (!names.Any()) return call.Request.Headers.Any();
return call.Request.Headers.Select(h => h.Key).Intersect(names).Any();
}, descrip);
}
/// <summary>
/// Asserts whether calls were made containing all of the given header values.
/// </summary>
/// <param name="values">Object (usually anonymous) or dictionary that is parsed to name/value headers to check for. Values may contain * wildcard.</param>
public HttpCallAssertion WithHeaders(object values) {
return values.ToKeyValuePairs().Select(kv => WithHeader(kv.Key, kv.Value)).LastOrDefault() ?? this;
}
/// <summary>
/// Asserts whether calls were made NOT containing any of the given header values.
/// </summary>
/// <param name="values">Object (usually anonymous) or dictionary that is parsed to name/value headers to check for. Values may contain * wildcard.</param>
public HttpCallAssertion WithoutHeaders(object values) {
return values.ToKeyValuePairs().Select(kv => WithoutHeader(kv.Key, kv.Value)).LastOrDefault() ?? this;
}
#endregion
#region cookies
/// <summary>
/// Asserts whether calls were made containing the given cookie and (optionally) value.
/// value may contain * wildcard.
/// Asserts whether calls were made containing the given cookie name and (optionally) value. value may contain * wildcard.
/// </summary>
public HttpCallAssertion WithCookie(string name, string valuePattern = "*", string descrip = null) {
descrip = descrip ?? BuildDescrip("cookie", name, valuePattern);
return With(c => c.HasCookie(name, valuePattern), descrip);
public HttpCallAssertion WithCookie(string name, object value = null) {
return With(c => c.HasCookie(name, value), BuildDescrip("cookie", name, value));
}
/// <summary>
/// Asserts whether calls were made that do NOT contain the given cookie and (optionally) value.
/// value may contain * wildcard.
/// Asserts whether calls were made NOT containing the given cookie and (optionally) value. value may contain * wildcard.
/// </summary>
public HttpCallAssertion WithoutCookie(string name, string valuePattern = "*") {
return Without(c => c.HasCookie(name, valuePattern), BuildDescrip("no cookie", name, valuePattern));
public HttpCallAssertion WithoutCookie(string name, object value = null) {
return Without(c => c.HasCookie(name, value), BuildDescrip("no cookie", name, value));
}
/// <summary>
/// Asserts whether calls were made containing ALL the given cookies (regardless of their values).
/// </summary>
public HttpCallAssertion WithCookies(params string[] names) {
return names.Select(n => WithCookie(n)).LastOrDefault() ?? this;
}
/// <summary>
/// Asserts whether calls were made NOT containing any of the given cookies.
/// If no names are provided, asserts no calls were made with any cookies.
/// </summary>
public HttpCallAssertion WithoutCookies(params string[] names) {
if (!names.Any())
return With(c => !c.Request.Cookies.Any(), "no cookies");
return names.Select(n => WithoutCookie(n)).LastOrDefault() ?? this;
}
/// <summary>
/// Asserts whether calls were made containing ANY the given cookies (regardless of their values).
/// If no names are provided, asserts that calls were made containing at least one cookie with any name.
/// </summary>
public HttpCallAssertion WithAnyCookie(params string[] names) {
var descrip = $"any cookie {string.Join(", ", names)}".Trim();
return With(call => {
if (!names.Any()) return call.Request.Cookies.Any();
return call.Request.Cookies.Select(c => c.Key).Intersect(names).Any();
}, descrip);
}
/// <summary>
/// Asserts whether calls were made containing all of the given cookie values.
/// </summary>
/// <param name="values">Object (usually anonymous) or dictionary that is parsed to name/value cookies to check for. Values may contain * wildcard.</param>
public HttpCallAssertion WithCookies(object values) {
return values.ToKeyValuePairs().Select(kv => WithCookie(kv.Key, kv.Value)).LastOrDefault() ?? this;
}
/// <summary>
/// Asserts whether calls were made NOT containing any of the given cookie values.
/// </summary>
/// <param name="values">Object (usually anonymous) or dictionary that is parsed to name/value cookies to check for. Values may contain * wildcard.</param>
public HttpCallAssertion WithoutCookies(object values) {
return values.ToKeyValuePairs().Select(kv => WithoutCookie(kv.Key, kv.Value)).LastOrDefault() ?? this;
}
#endregion
@ -224,7 +308,7 @@ namespace Flurl.Http.Testing
/// Token can contain * wildcard.
/// </summary>
public HttpCallAssertion WithOAuthBearerToken(string token = "*") {
return WithHeader("Authorization", $"Bearer {token}", "bearer token " + token);
return WithHeader("Authorization", $"Bearer {token}");
}
/// <summary>

View File

@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Flurl.Util;
namespace Flurl.Http.Testing
@ -14,16 +13,6 @@ namespace Flurl.Http.Testing
/// </summary>
internal static class Util
{
internal static bool MatchesPattern(string textToCheck, string pattern) {
// avoid regex'ing in simple cases
if (string.IsNullOrEmpty(pattern) || pattern == "*") return true;
if (string.IsNullOrEmpty(textToCheck)) return false;
if (!pattern.Contains("*")) return textToCheck == pattern;
var regex = "^" + Regex.Escape(pattern).Replace("\\*", "(.*)") + "$";
return Regex.IsMatch(textToCheck ?? "", regex);
}
internal static bool HasAnyVerb(this FlurlCall call, HttpMethod[] verbs) {
// for good measure, check both FlurlRequest.Verb and HttpRequestMessage.Method
return verbs.Any(verb => call.Request.Verb == verb && call.HttpRequestMessage.Method == verb);
@ -44,15 +33,11 @@ namespace Flurl.Http.Testing
if (!paramVals.Any())
return false;
if (value == null)
return true;
if (value is string s)
return paramVals.Any(v => MatchesPattern(v, s));
if (value is IEnumerable en) {
if (!(value is string) && value is IEnumerable en) {
var values = en.Cast<object>().Select(o => o.ToInvariantString()).ToList();
return values.Intersect(paramVals).Count() == values.Count;
}
return paramVals.Any(v => v == value.ToInvariantString());
return paramVals.Any(v => MatchesValue(v, value));
}
internal static bool HasAllQueryParams(this FlurlCall call, string[] names) {
@ -74,23 +59,32 @@ namespace Flurl.Http.Testing
return values.ToKeyValuePairs().All(kv => call.HasQueryParam(kv.Key, kv.Value));
}
internal static bool HasHeader(this FlurlCall call, string name, string valuePattern) {
var val = call.HttpRequestMessage.GetHeaderValue(name);
return val != null && MatchesPattern(val, valuePattern);
internal static bool HasHeader(this FlurlCall call, string name, object value) {
return call.Request.Headers.TryGetValue(name, out var val) && MatchesValue(val?.ToInvariantString(), value);
}
internal static bool HasCookie(this FlurlCall call, string name, string valuePattern) {
var headerVal = call.HttpRequestMessage.GetHeaderValue("Cookie");
if (headerVal == null) return false;
return (
from kv in headerVal.Split(';')
let parts = kv.SplitOnFirstOccurence("=")
where parts.Length == 2
let key = parts[0].Trim()
where key == name
let val = parts[1].Trim()
where MatchesPattern(val, valuePattern)
select 1).Any();
internal static bool HasCookie(this FlurlCall call, string name, object value) {
return call.Request.Cookies.TryGetValue(name, out var val) && MatchesValue(val, value);
}
private static bool MatchesValue(string valueToMatch, object value) {
if (value == null)
return true;
if (valueToMatch == null)
return false;
if (value is string s)
return MatchesPattern(valueToMatch, s);
return valueToMatch == value.ToInvariantString();
}
internal static bool MatchesPattern(string textToCheck, string pattern) {
// avoid regex'ing in simple cases
if (string.IsNullOrEmpty(pattern) || pattern == "*") return true;
if (string.IsNullOrEmpty(textToCheck)) return false;
if (!pattern.Contains("*")) return textToCheck == pattern;
var regex = "^" + Regex.Escape(pattern).Replace("\\*", "(.*)") + "$";
return Regex.IsMatch(textToCheck ?? "", regex);
}
}
}