File: FakeHttpClient.cs
Web Access
Project: src\src\Common\Microsoft.Arcade.Test.Common\Microsoft.Arcade.Test.Common.csproj (Microsoft.Arcade.Test.Common)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
 
namespace Microsoft.DotNet.Arcade.Test.Common
{
    public static class FakeHttpClient
    {
        
        public static HttpClient WithResponses(params HttpResponseMessage[] responses)
            => new HttpClient( // lgtm [cs/httpclient-checkcertrevlist-disabled] HttpClient used in unit tests
                new FakeResponseOnlyHttpMessageHandler(responses));
 
        public static HttpClient WithResponsesGivenUris(Dictionary<string, IEnumerable<HttpResponseMessage>> FakeHttpMessageHandler)
            => new HttpClient( // lgtm [cs/httpclient-checkcertrevlist-disabled] HttpClient used in unit tests
                new FakeHttpMessageHandler(FakeHttpMessageHandler));
 
        public class FakeResponseOnlyHttpMessageHandler : HttpMessageHandler
        {
            private readonly IEnumerator<HttpResponseMessage> _responseEnumerator;
 
            public FakeResponseOnlyHttpMessageHandler(IEnumerable<HttpResponseMessage> responses)
            {
                _responseEnumerator = responses.GetEnumerator();
            }
 
            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    _responseEnumerator.Dispose();
                }
            }
 
            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                if (!_responseEnumerator.MoveNext())
                    throw new InvalidOperationException($"Unexpected end of response sequence. Number of predefined responses should be at least equal to number of requests invoked by HttpClient.");
 
                return Task.FromResult(_responseEnumerator.Current);
            }
        }
 
        public class FakeHttpMessageHandler : HttpMessageHandler
        {
            private readonly Dictionary<string, IEnumerator<HttpResponseMessage>> _responseEnumerators;
 
            public FakeHttpMessageHandler(Dictionary<string, IEnumerable<HttpResponseMessage>> responses)
            {
                _responseEnumerators = responses.Select(kvp =>
                    new KeyValuePair<string, IEnumerator<HttpResponseMessage>>(kvp.Key, kvp.Value.GetEnumerator()))
                    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase);
            }
 
            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    foreach (var e in _responseEnumerators.Values)
                    {
                        e.Dispose();
                    }
                }
            }
 
            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                if (!_responseEnumerators.TryGetValue(request.RequestUri.ToString(), out var responseEnumerator))
                {
                    if (request.RequestUri != null)
                        throw new InvalidOperationException($"Unexpected request URI: {request.RequestUri}.");
                }
                if (!responseEnumerator.MoveNext())
                    throw new InvalidOperationException($"Unexpected end of response sequence for uri '{request.RequestUri}'. " +
                        $"Number of predefined responses should be at least equal to number of requests invoked by HttpClient.");
 
                return Task.FromResult(responseEnumerator.Current);
            }
        }
    }
}