/// <summary> /// Asserts that the client generated request matches the example from the docs /// </summary> public static void MatchesExample(this IResponse response, string content, Func <Example, Example> clientChanges = null) { var example = Example.Create(content); // a specific example might use notation that is not supported by the client, because it only // supports the long form. Allow a function to be passed to make modifications to suit. if (clientChanges != null) { example = clientChanges(example); } // apply global changes after local ones example = Example.ApplyGlobalChanges(example); response.ApiCall.HttpMethod.Should().Be(example.Method); // the client encodes characters such as commas, so decode to compare var decodedAbsolutePath = HttpUtility.UrlDecode(response.ApiCall.Uri.AbsolutePath); var expectedUri = example.Uri.Uri; var expectedPath = expectedUri.AbsolutePath.Length > 1 ? expectedUri.AbsolutePath.TrimEnd('/') : expectedUri.AbsolutePath; // A lot of the examples are not constrained to an index which is not necessarily what users typically do. // in NEST you have to be explicit and call AllIndices() which explicitly puts _all on the path // So if we expect `/_search` but `/_all/_search` is passed we feel they are equivalent if (expectedPath != "/_search" || decodedAbsolutePath != "/_all/_search") { decodedAbsolutePath.Should().Be(expectedPath); } // check expected query string params. Rather that _all_ keys match, // only check that the ones in reference doc example are present, because // the client may append more key/values such as "typed_keys" // Because the tests remove keys with string replace the following bad query string can appear example.Uri.Query = example.Uri.Query.Replace("?&", "?").Replace("&&", "&"); var expectedQueryParams = HttpUtility.ParseQueryString(example.Uri.Query); var actualQueryParams = HttpUtility.ParseQueryString(response.ApiCall.Uri.Query); if (expectedQueryParams.HasKeys()) { foreach (var key in expectedQueryParams.AllKeys) { if (string.IsNullOrWhiteSpace(key)) { continue; } actualQueryParams.AllKeys.Should().Contain(key); var expectedValue = expectedQueryParams.Get(key); var actualValue = actualQueryParams.Get(key); // The client always sends x=true while the docs document just sending ?x if (string.IsNullOrEmpty(expectedValue) && actualValue == "true") { continue; } actualQueryParams.Get(key).Should().Be(expectedValue); } } if (example.Body != null) { var expected = ParseJObjects(example.Body); var actual = ParseJObjects(Encoding.UTF8.GetString(response.ApiCall.RequestBodyInBytes)); expected.Count.Should().Be(actual.Count); foreach (var(e, a) in expected.Zip(actual)) { var matches = JToken.DeepEquals(e, a); if (!matches) { e.DeepSort(); a.DeepSort(); var sortedExpected = string.Join(Environment.NewLine, expected.Select(x => x.ToString())); var sortedActual = string.Join(Environment.NewLine, actual.Select(x => x.ToString())); var diff = sortedExpected.Diff(sortedActual); if (!string.IsNullOrWhiteSpace(diff)) { Assert.True(false, $"body diff: {diff}"); } } } } }
/// <summary> /// Asserts that the client generated request matches the example from the docs /// </summary> public static void MatchesExample(this IResponse response, string content, Func <Example, Example> clientChanges = null) { var example = Example.Create(content); // a specific example might use notation that is not supported by the client, because it only // supports the long form. Allow a function to be passed to make modifications to suit. if (clientChanges != null) { example = clientChanges(example); } // apply global changes after local ones example = Example.ApplyGlobalChanges(example); response.ApiCall.HttpMethod.Should().Be(example.Method); // the client encodes characters such as commas, so decode to compare var decodedAbsolutePath = HttpUtility.UrlDecode(response.ApiCall.Uri.AbsolutePath); decodedAbsolutePath.Should().Be(example.Uri.AbsolutePath.Length > 1 ? example.Uri.AbsolutePath.TrimEnd('/') : example.Uri.AbsolutePath); // check expected query string params. Rather that _all_ keys match, // only check that the ones in reference doc example are present, because // the client may append more key/values such as "typed_keys" var expectedQueryParams = HttpUtility.ParseQueryString(example.Uri.Query); var actualQueryParams = HttpUtility.ParseQueryString(response.ApiCall.Uri.Query); if (expectedQueryParams.HasKeys()) { foreach (var key in expectedQueryParams.AllKeys) { actualQueryParams.AllKeys.Should().Contain(key); var value = expectedQueryParams.Get(key); actualQueryParams.Get(key).Should().Be(value); } } if (example.Body != null) { var expected = ParseJObjects(example.Body); var actual = ParseJObjects(Encoding.UTF8.GetString(response.ApiCall.RequestBodyInBytes)); expected.Count.Should().Be(actual.Count); foreach (var(e, a) in expected.Zip(actual)) { var matches = JToken.DeepEquals(e, a); if (!matches) { e.DeepSort(); a.DeepSort(); var sortedExpected = string.Join(Environment.NewLine, expected.Select(x => x.ToString())); var sortedActual = string.Join(Environment.NewLine, actual.Select(x => x.ToString())); var diff = sortedExpected.Diff(sortedActual); if (!string.IsNullOrWhiteSpace(diff)) { Assert.True(false, $"body diff: {diff}"); } } } } }