Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public ConfigurationRecordMatcher(RecordedTestSanitizer sanitizer) : base(saniti

protected override bool IsBodyEquivalent(RecordEntry record, RecordEntry otherRecord)
{
byte[] body = record.ResponseBody ?? Array.Empty<byte>();
byte[] otherBody = record.ResponseBody ?? Array.Empty<byte>();
byte[] body = record.Response.Body ?? Array.Empty<byte>();
byte[] otherBody = record.Response.Body ?? Array.Empty<byte>();

if (body.SequenceEqual(otherBody))
return true;
Expand Down
40 changes: 23 additions & 17 deletions sdk/core/Azure.Core/tests/RecordSessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ public void CanRoundtripSessionRecord(string body, string contentType)
session.Variables["b"] = "value b";

RecordEntry recordEntry = new RecordEntry();
recordEntry.RequestHeaders.Add("Content-Type", new[] { contentType });
recordEntry.RequestHeaders.Add("Other-Header", new[] { "multi", "value" });
recordEntry.RequestBody = bodyBytes;
recordEntry.Request.Headers.Add("Content-Type", new[] { contentType });
recordEntry.Request.Headers.Add("Other-Header", new[] { "multi", "value" });
recordEntry.Request.Body = bodyBytes;
recordEntry.RequestUri = "url";
recordEntry.RequestMethod = RequestMethod.Delete;

recordEntry.ResponseHeaders.Add("Content-Type", new[] { contentType });
recordEntry.ResponseHeaders.Add("Other-Response-Header", new[] { "multi", "value" });
recordEntry.Response.Headers.Add("Content-Type", new[] { contentType });
recordEntry.Response.Headers.Add("Other-Response-Header", new[] { "multi", "value" });

recordEntry.ResponseBody = bodyBytes;
recordEntry.Response.Body = bodyBytes;
recordEntry.StatusCode = 202;

session.Entries.Add(recordEntry);
Expand All @@ -62,14 +62,14 @@ public void CanRoundtripSessionRecord(string body, string contentType)
Assert.AreEqual("url", recordEntry.RequestUri);
Assert.AreEqual(202, recordEntry.StatusCode);

CollectionAssert.AreEqual(new[] { contentType }, deserializedRecord.RequestHeaders["content-type"]);
CollectionAssert.AreEqual(new[] { "multi", "value" }, deserializedRecord.RequestHeaders["other-header"]);
CollectionAssert.AreEqual(new[] { contentType }, deserializedRecord.Request.Headers["content-type"]);
CollectionAssert.AreEqual(new[] { "multi", "value" }, deserializedRecord.Request.Headers["other-header"]);

CollectionAssert.AreEqual(new[] { contentType }, deserializedRecord.ResponseHeaders["content-type"]);
CollectionAssert.AreEqual(new[] { "multi", "value" }, deserializedRecord.ResponseHeaders["other-response-header"]);
CollectionAssert.AreEqual(new[] { contentType }, deserializedRecord.Response.Headers["content-type"]);
CollectionAssert.AreEqual(new[] { "multi", "value" }, deserializedRecord.Response.Headers["other-response-header"]);

CollectionAssert.AreEqual(bodyBytes, deserializedRecord.RequestBody);
CollectionAssert.AreEqual(bodyBytes, deserializedRecord.ResponseBody);
CollectionAssert.AreEqual(bodyBytes, deserializedRecord.Request.Body);
CollectionAssert.AreEqual(bodyBytes, deserializedRecord.Response.Body);
}

[Test]
Expand All @@ -91,10 +91,13 @@ public void RecordMatcherThrowsExceptionsWithDetails()
{
RequestUri = "http://remote-host",
RequestMethod = RequestMethod.Put,
RequestHeaders =
Request =
{
{ "Some-Header", new[] { "Non-Random value"}},
{ "Extra-Header", new[] { "Extra-Value" }}
Headers =
{
{ "Some-Header", new[] { "Non-Random value"}},
{ "Extra-Header", new[] { "Extra-Value" }}
}
}
}
};
Expand Down Expand Up @@ -130,9 +133,12 @@ public void RecordMatcherIgnoresIgnoredHeaders()
{
RequestUri = "http://localhost",
RequestMethod = RequestMethod.Put,
RequestHeaders =
Request =
{
{ "Request-Id", new[] { "Non-Random value"}},
Headers =
{
{ "Request-Id", new[] { "Non-Random value"}},
}
}
}
};
Expand Down
6 changes: 3 additions & 3 deletions sdk/core/Azure.Core/tests/TestFramework/PlaybackTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ public Response GetResponse(RecordEntry recordEntry)
{
var response = new MockResponse(recordEntry.StatusCode);
// TODO: Use non-seekable stream
if (recordEntry.ResponseBody != null)
if (recordEntry.Response.Body != null)
{
response.ContentStream = new MemoryStream(recordEntry.ResponseBody);
response.ContentStream = new MemoryStream(recordEntry.Response.Body);
}

foreach (KeyValuePair<string, string[]> responseHeader in recordEntry.ResponseHeaders)
foreach (KeyValuePair<string, string[]> responseHeader in recordEntry.Response.Headers)
{
foreach (string value in responseHeader.Value)
{
Expand Down
104 changes: 20 additions & 84 deletions sdk/core/Azure.Core/tests/TestFramework/RecordEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@ namespace Azure.Core.Testing
{
public class RecordEntry
{
public string RequestUri { get; set; }

public RequestMethod RequestMethod { get; set; }

public byte[] RequestBody { get; set; }
public RecordEntryMessage Request { get; } = new RecordEntryMessage();

public SortedDictionary<string, string[]> RequestHeaders { get; set; } = new SortedDictionary<string, string[]>(StringComparer.InvariantCultureIgnoreCase);
public RecordEntryMessage Response { get; } = new RecordEntryMessage();

public SortedDictionary<string, string[]> ResponseHeaders { get; set; } = new SortedDictionary<string, string[]>(StringComparer.InvariantCultureIgnoreCase);
public string RequestUri { get; set; }

public byte[] ResponseBody { get; set; }
public RequestMethod RequestMethod { get; set; }

public int StatusCode { get; set; }

Expand All @@ -39,14 +35,14 @@ public static RecordEntry Deserialize(JsonElement element)
record.RequestUri = property.GetString();
}

if (element.TryGetProperty(nameof(RequestHeaders), out property))
if (element.TryGetProperty("RequestHeaders", out property))
{
DeserializeHeaders(record.RequestHeaders, property);
DeserializeHeaders(record.Request.Headers, property);
}

if (element.TryGetProperty(nameof(RequestBody), out property))
if (element.TryGetProperty("RequestBody", out property))
{
record.RequestBody = DeserializeBody(record.RequestHeaders, property);
record.Request.Body = DeserializeBody(record.Request.Headers, property);
}

if (element.TryGetProperty(nameof(StatusCode), out property) &&
Expand All @@ -55,14 +51,14 @@ public static RecordEntry Deserialize(JsonElement element)
record.StatusCode = statusCode;
}

if (element.TryGetProperty(nameof(ResponseHeaders), out property))
if (element.TryGetProperty("ResponseHeaders", out property))
{
DeserializeHeaders(record.ResponseHeaders, property);
DeserializeHeaders(record.Response.Headers, property);
}

if (element.TryGetProperty(nameof(ResponseBody), out property))
if (element.TryGetProperty("ResponseBody", out property))
{
record.ResponseBody = DeserializeBody(record.ResponseHeaders, property);
record.Response.Body = DeserializeBody(record.Response.Headers, property);
}

return record;
Expand Down Expand Up @@ -137,19 +133,19 @@ public void Serialize(Utf8JsonWriter jsonWriter)

jsonWriter.WriteString(nameof(RequestUri), RequestUri);
jsonWriter.WriteString(nameof(RequestMethod), RequestMethod.Method);
jsonWriter.WriteStartObject(nameof(RequestHeaders));
SerializeHeaders(jsonWriter, RequestHeaders);
jsonWriter.WriteStartObject("RequestHeaders");
SerializeHeaders(jsonWriter, Request.Headers);
jsonWriter.WriteEndObject();

SerializeBody(jsonWriter, nameof(RequestBody), RequestBody, RequestHeaders);
SerializeBody(jsonWriter, "RequestBody", Request.Body, Request.Headers);

jsonWriter.WriteNumber(nameof(StatusCode), StatusCode);

jsonWriter.WriteStartObject(nameof(ResponseHeaders));
SerializeHeaders(jsonWriter, ResponseHeaders);
jsonWriter.WriteStartObject("ResponseHeaders");
SerializeHeaders(jsonWriter, Response.Headers);
jsonWriter.WriteEndObject();

SerializeBody(jsonWriter, nameof(ResponseBody), ResponseBody, ResponseHeaders);
SerializeBody(jsonWriter, "ResponseBody", Response.Body, Response.Headers);
jsonWriter.WriteEndObject();
}

Expand Down Expand Up @@ -250,7 +246,7 @@ private void SerializeHeaders(Utf8JsonWriter jsonWriter, IDictionary<string, str
}
}

private static bool TryGetContentType(IDictionary<string, string[]> requestHeaders, out string contentType)
public static bool TryGetContentType(IDictionary<string, string[]> requestHeaders, out string contentType)
{
contentType = null;
if (requestHeaders.TryGetValue("Content-Type", out var contentTypes) &&
Expand All @@ -262,71 +258,11 @@ private static bool TryGetContentType(IDictionary<string, string[]> requestHeade
return false;
}

private static bool IsTextContentType(IDictionary<string, string[]> requestHeaders, out Encoding encoding)
public static bool IsTextContentType(IDictionary<string, string[]> requestHeaders, out Encoding encoding)
{
encoding = null;
return TryGetContentType(requestHeaders, out string contentType) &&
TestFrameworkContentTypeUtilities.TryGetTextEncoding(contentType, out encoding);
}

public void Sanitize(RecordedTestSanitizer sanitizer)
{
RequestUri = sanitizer.SanitizeUri(RequestUri);
if (RequestBody != null)
{
int contentLength = RequestBody.Length;
TryGetContentType(RequestHeaders, out string contentType);
if (IsTextContentType(RequestHeaders, out Encoding encoding))
{
RequestBody = Encoding.UTF8.GetBytes(sanitizer.SanitizeTextBody(contentType, encoding.GetString(RequestBody)));
}
else
{
RequestBody = sanitizer.SanitizeBody(contentType, RequestBody);
}
UpdateSanitizedContentLength(RequestHeaders, contentLength, RequestBody?.Length ?? 0);
}

sanitizer.SanitizeHeaders(RequestHeaders);

if (ResponseBody != null)
{
int contentLength = ResponseBody.Length;
TryGetContentType(ResponseHeaders, out string contentType);
if (IsTextContentType(ResponseHeaders, out Encoding encoding))
{
ResponseBody = Encoding.UTF8.GetBytes(sanitizer.SanitizeTextBody(contentType, encoding.GetString(ResponseBody)));
}
else
{
ResponseBody = sanitizer.SanitizeBody(contentType, ResponseBody);
}
UpdateSanitizedContentLength(ResponseHeaders, contentLength, ResponseBody?.Length ?? 0);
}

sanitizer.SanitizeHeaders(ResponseHeaders);
}

/// <summary>
/// Optionally update the Content-Length header if we've sanitized it
/// and the new value is a different length from the original
/// Content-Length header. We don't add a Content-Length header if it
/// wasn't already present.
/// </summary>
/// <param name="headers">The Request or Response headers</param>
/// <param name="originalLength">THe original Content-Length</param>
/// <param name="sanitizedLength">The sanitized Content-Length</param>
private static void UpdateSanitizedContentLength(IDictionary<string, string[]> headers, int originalLength, int sanitizedLength)
{
// Note: If the RequestBody/ResponseBody was set to null by our
// sanitizer, we'll pass 0 as the sanitizedLength and use that as
// our new Content-Length. That's fine for all current scenarios
// (i.e., we never do that), but it's possible we may want to
// remove the Content-Length header in the future.
if (originalLength != sanitizedLength && headers.ContainsKey("Content-Length"))
{
headers["Content-Length"] = new string[] { sanitizedLength.ToString() };
}
}
}
}
51 changes: 51 additions & 0 deletions sdk/core/Azure.Core/tests/TestFramework/RecordEntryMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Text;
using Azure.Core.Pipeline;

namespace Azure.Core.Testing
{
public class RecordEntryMessage
{
public SortedDictionary<string, string[]> Headers { get; set; } = new SortedDictionary<string, string[]>(StringComparer.InvariantCultureIgnoreCase);

public byte[] Body { get; set; }

public bool TryGetContentType(out string contentType)
{
contentType = null;
if (Headers.TryGetValue("Content-Type", out var contentTypes) &&
contentTypes.Length == 1)
{
contentType = contentTypes[0];
return true;
}
return false;
}

public bool IsTextContentType(out Encoding encoding)
{
encoding = null;
return TryGetContentType(out string contentType) &&
TestFrameworkContentTypeUtilities.TryGetTextEncoding(contentType, out encoding);
}

public bool TryGetBodyAsText(out string text)
{
text = null;

if (IsTextContentType(out Encoding encoding))
{
text = encoding.GetString(Body);

return true;
}

return false;
}

}
}
14 changes: 7 additions & 7 deletions sdk/core/Azure.Core/tests/TestFramework/RecordMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public virtual RecordEntry FindMatch(Request request, IList<RecordEntry> entries
score++;
}

score += CompareHeaderDictionaries(headers, entry.RequestHeaders, ExcludeHeaders);
score += CompareHeaderDictionaries(headers, entry.Request.Headers, ExcludeHeaders);

if (score == 0)
{
Expand All @@ -108,7 +108,7 @@ public virtual bool IsEquivalentRecord(RecordEntry entry, RecordEntry otherEntry
protected virtual bool IsEquivalentRequest(RecordEntry entry, RecordEntry otherEntry) =>
entry.RequestMethod == otherEntry.RequestMethod &&
IsEquivalentUri(entry.RequestUri, otherEntry.RequestUri) &&
CompareHeaderDictionaries(entry.RequestHeaders, otherEntry.RequestHeaders, VolatileHeaders) == 0;
CompareHeaderDictionaries(entry.Request.Headers, otherEntry.Request.Headers, VolatileHeaders) == 0;

private static bool AreUrisSame(string entryUri, string otherEntryUri) =>
// Some versions of .NET behave differently when calling new Uri("...")
Expand All @@ -121,8 +121,8 @@ protected virtual bool IsEquivalentUri(string entryUri, string otherEntryUri) =>

protected virtual bool IsEquivalentResponse(RecordEntry entry, RecordEntry otherEntry)
{
IEnumerable<KeyValuePair<string, string[]>> entryHeaders = entry.ResponseHeaders.Where(h => !VolatileResponseHeaders.Contains(h.Key));
IEnumerable<KeyValuePair<string, string[]>> otherEntryHeaders = otherEntry.ResponseHeaders.Where(h => !VolatileResponseHeaders.Contains(h.Key));
IEnumerable<KeyValuePair<string, string[]>> entryHeaders = entry.Response.Headers.Where(h => !VolatileResponseHeaders.Contains(h.Key));
IEnumerable<KeyValuePair<string, string[]>> otherEntryHeaders = otherEntry.Response.Headers.Where(h => !VolatileResponseHeaders.Contains(h.Key));

return
entry.StatusCode == otherEntry.StatusCode &&
Expand All @@ -132,8 +132,8 @@ protected virtual bool IsEquivalentResponse(RecordEntry entry, RecordEntry other

protected virtual bool IsBodyEquivalent(RecordEntry record, RecordEntry otherRecord)
{
return (record.ResponseBody ?? Array.Empty<byte>()).AsSpan()
.SequenceEqual((otherRecord.ResponseBody ?? Array.Empty<byte>()));
return (record.Response.Body ?? Array.Empty<byte>()).AsSpan()
.SequenceEqual((otherRecord.Response.Body ?? Array.Empty<byte>()));
}

private string GenerateException(RequestMethod requestMethod, string uri, SortedDictionary<string, string[]> headers, RecordEntry bestScoreEntry)
Expand Down Expand Up @@ -161,7 +161,7 @@ private string GenerateException(RequestMethod requestMethod, string uri, Sorted

builder.AppendLine("Header differences:");

CompareHeaderDictionaries(headers, bestScoreEntry.RequestHeaders, ExcludeHeaders, builder);
CompareHeaderDictionaries(headers, bestScoreEntry.Request.Headers, ExcludeHeaders, builder);

return builder.ToString();
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/Azure.Core/tests/TestFramework/RecordSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public void Sanitize(RecordedTestSanitizer sanitizer)
{
foreach (RecordEntry entry in Entries)
{
entry.Sanitize(sanitizer);
sanitizer.Sanitize(entry);
}
}
}
Expand Down
Loading