< Summary

Class:Azure.Core.TestFramework.RecordEntry
Assembly:Azure.Core.TestFramework
File(s):C:\Git\azure-sdk-for-net\sdk\core\Azure.Core.TestFramework\src\RecordEntry.cs
Covered lines:127
Uncovered lines:2
Coverable lines:129
Total lines:307
Line coverage:98.4% (127 of 129)
Covered branches:71
Total branches:72
Branch coverage:98.6% (71 of 72)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-100%100%
get_Request()-100%100%
get_Response()-100%100%
get_RequestUri()-100%100%
get_IsTrack1Recording()-0%100%
get_RequestMethod()-100%100%
get_StatusCode()-100%100%
Deserialize(...)-94.74%94.44%
DeserializeBody(...)-100%100%
DeserializeHeaders(...)-100%100%
Serialize(...)-100%100%
SerializeBody(...)-100%100%
IndexOfNewline(...)-100%100%
SerializeHeaders(...)-100%100%
TryGetContentType(...)-100%100%
IsTextContentType(...)-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\core\Azure.Core.TestFramework\src\RecordEntry.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.IO;
 7using System.Linq;
 8using System.Text;
 9using System.Text.Encodings.Web;
 10using System.Text.Json;
 11using Azure.Core.Pipeline;
 12
 13namespace Azure.Core.TestFramework
 14{
 15    public class RecordEntry
 16    {
 6617        private static readonly JsonWriterOptions RequestWriterOptions = new JsonWriterOptions();
 18        // Responses are usually formatted using Newtonsoft.Json that has more relaxed encoding rules
 19        // To enable us to store more responses as JSON instead of string in Recording files use
 20        // relaxed settings for roundrip
 6621        private static readonly JsonWriterOptions ResponseWriterOptions = new JsonWriterOptions()
 6622        {
 6623            Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
 6624        };
 25
 556650226        public RecordEntryMessage Request { get; } = new RecordEntryMessage();
 27
 335313028        public RecordEntryMessage Response { get; } = new RecordEntryMessage();
 29
 198606830        public string RequestUri { get; set; }
 31
 032        public bool IsTrack1Recording { get; set; }
 33
 133312234        public RequestMethod RequestMethod { get; set; }
 35
 67510836        public int StatusCode { get; set; }
 37
 38        public static RecordEntry Deserialize(JsonElement element)
 39        {
 34869240            var record = new RecordEntry();
 41
 34869242            if (element.TryGetProperty(nameof(RequestMethod), out JsonElement property))
 43            {
 34869244                record.RequestMethod = RequestMethod.Parse(property.GetString());
 45            }
 46
 34869247            if (element.TryGetProperty(nameof(RequestUri), out property))
 48            {
 34869249                record.RequestUri = property.GetString();
 50            }
 51
 34869252            if (element.TryGetProperty("EncodedRequestUri", out property))
 53            {
 054                record.IsTrack1Recording = true;
 55            }
 56
 34869257            if (element.TryGetProperty("RequestHeaders", out property))
 58            {
 34869259                DeserializeHeaders(record.Request.Headers, property);
 60            }
 61
 34869262            if (element.TryGetProperty("RequestBody", out property))
 63            {
 34869264                record.Request.Body = DeserializeBody(record.Request.Headers, property, RequestWriterOptions);
 65            }
 66
 34869267            if (element.TryGetProperty(nameof(StatusCode), out property) &&
 34869268                property.TryGetInt32(out var statusCode))
 69            {
 34869270                record.StatusCode = statusCode;
 71            }
 72
 34869273            if (element.TryGetProperty("ResponseHeaders", out property))
 74            {
 34869275                DeserializeHeaders(record.Response.Headers, property);
 76            }
 77
 34869278            if (element.TryGetProperty("ResponseBody", out property))
 79            {
 34869280                record.Response.Body = DeserializeBody(record.Response.Headers, property, ResponseWriterOptions);
 81            }
 82
 34869283            return record;
 84        }
 85
 86        private static byte[] DeserializeBody(IDictionary<string, string[]> headers, in JsonElement property, JsonWriter
 87        {
 69738488            if (property.ValueKind == JsonValueKind.Null)
 89            {
 32959890                return null;
 91            }
 92
 36778693            if (IsTextContentType(headers, out Encoding encoding))
 94            {
 30054195                if (property.ValueKind == JsonValueKind.Object)
 96                {
 4904297                    using var memoryStream = new MemoryStream();
 4904298                    using var writer = new Utf8JsonWriter(memoryStream, writerOptions);
 4904299                    property.WriteTo(writer);
 49042100                    writer.Flush();
 49042101                    return memoryStream.ToArray();
 102                }
 251499103                else if (property.ValueKind == JsonValueKind.Array)
 104                {
 241266105                    StringBuilder stringBuilder = new StringBuilder();
 106
 3563652107                    foreach (JsonElement item in property.EnumerateArray())
 108                    {
 1547302109                        stringBuilder.Append(item.GetString());
 110                    }
 111
 241266112                    return encoding.GetBytes(stringBuilder.ToString());
 113                }
 114                else
 115                {
 10233116                    return encoding.GetBytes(property.GetString());
 117                }
 118            }
 119
 67245120            if (property.ValueKind == JsonValueKind.Array)
 121            {
 58661122                return Array.Empty<byte>();
 123            }
 124
 8584125            return Convert.FromBase64String(property.GetString());
 49042126        }
 127
 128        private static void DeserializeHeaders(IDictionary<string, string[]> headers, in JsonElement property)
 129        {
 14475280130            foreach (JsonProperty item in property.EnumerateObject())
 131            {
 6540256132                if (item.Value.ValueKind == JsonValueKind.Array)
 133                {
 621338134                    var values = new List<string>();
 3730188135                    foreach (JsonElement headerValue in item.Value.EnumerateArray())
 136                    {
 1243756137                        values.Add(headerValue.GetString());
 138                    }
 139
 621338140                    headers[item.Name] = values.ToArray();
 141                }
 142                else
 143                {
 5918918144                    headers[item.Name] = new[] { item.Value.GetString() };
 145                }
 146            }
 697384147        }
 148
 149        public void Serialize(Utf8JsonWriter jsonWriter)
 150        {
 28151            jsonWriter.WriteStartObject();
 152
 28153            jsonWriter.WriteString(nameof(RequestUri), RequestUri);
 28154            jsonWriter.WriteString(nameof(RequestMethod), RequestMethod.Method);
 28155            jsonWriter.WriteStartObject("RequestHeaders");
 28156            SerializeHeaders(jsonWriter, Request.Headers);
 28157            jsonWriter.WriteEndObject();
 158
 28159            SerializeBody(jsonWriter, "RequestBody", Request.Body, Request.Headers, RequestWriterOptions);
 160
 28161            jsonWriter.WriteNumber(nameof(StatusCode), StatusCode);
 162
 28163            jsonWriter.WriteStartObject("ResponseHeaders");
 28164            SerializeHeaders(jsonWriter, Response.Headers);
 28165            jsonWriter.WriteEndObject();
 166
 28167            SerializeBody(jsonWriter, "ResponseBody", Response.Body, Response.Headers, ResponseWriterOptions);
 28168            jsonWriter.WriteEndObject();
 28169        }
 170
 171        private void SerializeBody(Utf8JsonWriter jsonWriter, string name, byte[] requestBody, IDictionary<string, strin
 172        {
 56173            if (requestBody == null)
 174            {
 4175                jsonWriter.WriteNull(name);
 176            }
 52177            else if (requestBody.Length == 0)
 178            {
 4179                jsonWriter.WriteStartArray(name);
 4180                jsonWriter.WriteEndArray();
 181            }
 48182            else if (IsTextContentType(headers, out Encoding encoding))
 183            {
 184                // Try parse response as JSON and write it directly if possible
 185                try
 186                {
 44187                    using JsonDocument document = JsonDocument.Parse(requestBody);
 188
 189                    // We use array as a wrapper for string based serialization
 190                    // so if the root is an array we can't write it directly
 191                    // fallback to generic string writing
 28192                    if (document.RootElement.ValueKind != JsonValueKind.Array)
 193                    {
 194                        // Make sure we can replay JSON is exactly the same as the source
 195                        // for the case where service response was pre-formatted
 196                        // fallback to generic string writing
 20197                        using var memoryStream = new MemoryStream();
 198                        // Settings of this writer should be in sync with the one used in deserialiation
 20199                        using (var reformattedWriter = new Utf8JsonWriter(memoryStream, writerOptions))
 200                        {
 20201                            document.RootElement.WriteTo(reformattedWriter);
 20202                        }
 203
 20204                        if (memoryStream.ToArray().SequenceEqual(requestBody))
 205                        {
 10206                            jsonWriter.WritePropertyName(name.AsSpan());
 10207                            document.RootElement.WriteTo(jsonWriter);
 10208                            return;
 209                        }
 210                    }
 18211                }
 16212                catch (Exception)
 213                {
 214                    // ignore
 16215                }
 216
 34217                ReadOnlySpan<char> text = encoding.GetString(requestBody).AsMemory().Span;
 218
 34219                var indexOfNewline = IndexOfNewline(text);
 34220                if (indexOfNewline == -1)
 221                {
 18222                    jsonWriter.WriteString(name, text);
 223                }
 224                else
 225                {
 16226                    jsonWriter.WriteStartArray(name);
 227                    do
 228                    {
 20229                        jsonWriter.WriteStringValue(text.Slice(0, indexOfNewline + 1));
 20230                        text = text.Slice(indexOfNewline + 1);
 20231                        indexOfNewline = IndexOfNewline(text);
 20232                    } while (indexOfNewline != -1);
 233
 16234                    if (!text.IsEmpty)
 235                    {
 12236                        jsonWriter.WriteStringValue(text);
 237                    }
 238
 16239                    jsonWriter.WriteEndArray();
 240                }
 241            }
 242            else
 243            {
 4244                jsonWriter.WriteString(name, Convert.ToBase64String(requestBody));
 245            }
 14246        }
 247
 248        private int IndexOfNewline(ReadOnlySpan<char> span)
 249        {
 54250            int indexOfNewline = span.IndexOfAny('\r', '\n');
 251
 54252            if (indexOfNewline == -1)
 253            {
 34254                return -1;
 255            }
 256
 20257            if (span.Length > indexOfNewline + 1 &&
 20258                (span[indexOfNewline + 1] == '\r' ||
 20259                span[indexOfNewline + 1] == '\n'))
 260            {
 8261                indexOfNewline++;
 262            }
 263
 20264            return indexOfNewline;
 265        }
 266
 267        private void SerializeHeaders(Utf8JsonWriter jsonWriter, IDictionary<string, string[]> header)
 268        {
 328269            foreach (KeyValuePair<string, string[]> requestHeader in header)
 270            {
 108271                if (requestHeader.Value.Length == 1)
 272                {
 56273                    jsonWriter.WriteString(requestHeader.Key, requestHeader.Value[0]);
 274                }
 275                else
 276                {
 52277                    jsonWriter.WriteStartArray(requestHeader.Key);
 312278                    foreach (var value in requestHeader.Value)
 279                    {
 104280                        jsonWriter.WriteStringValue(value);
 281                    }
 282
 52283                    jsonWriter.WriteEndArray();
 284                }
 285            }
 56286        }
 287
 288        public static bool TryGetContentType(IDictionary<string, string[]> requestHeaders, out string contentType)
 289        {
 367834290            contentType = null;
 367834291            if (requestHeaders.TryGetValue("Content-Type", out var contentTypes) &&
 367834292                contentTypes.Length == 1)
 293            {
 303697294                contentType = contentTypes[0];
 303697295                return true;
 296            }
 64137297            return false;
 298        }
 299
 300        public static bool IsTextContentType(IDictionary<string, string[]> requestHeaders, out Encoding encoding)
 301        {
 367834302            encoding = null;
 367834303            return TryGetContentType(requestHeaders, out string contentType) &&
 367834304                   ContentTypeUtilities.TryGetTextEncoding(contentType, out encoding);
 305        }
 306    }
 307}