I am trying to trace a chain of redirects (for an online ad pixel) programmatically, but with a timeout of 2 seconds (in other words, if the redirect chain takes more than 2 seconds to resolve, I want it to abort and return null).
My code is (more or less) running synchronously, so I had to do some acrobatics to do what I wanted, but functionally speaking, it seems to work... except for the timeout part.
I have some asynchronous helpers like so:
    public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
    {
        using (var timeoutCancellationTokenSource = new CancellationTokenSource())
        {
            var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
            if (completedTask != task)
            {
                throw new TimeoutException();
            }
            timeoutCancellationTokenSource.Cancel();
            return await task; 
        }
    }
    public static T ToSynchronousResult<T>(this Task<T> task)
    {
        return Task.Run(async () => await task).Result;
    }
The TimeoutAfter() helper method was adapted from the SO article that can be found here.  In my service I have a method that resembles this:
    public string GetFinalUrl(string url)
    {
        string finalUrl;
        try
        {
            finalUrl = FollowDestinationUrl(url).TimeoutAfter(TimeSpan.FromSeconds(2)).ToSynchronousResult();
        }
        catch (TimeoutException)
        {
            finalUrl = null;
        }
        return finalUrl;
    }
    private async Task<string> FollowDestinationUrl(string url)
    {
        var request = _webRequestFactory.CreateGet(url);
        var payload = await request.GetResponseAsync();
        return payload.ResponseUri.ToString();
    }
The _webRequestFactory here returns an HttpWebRequest abstraction that was written as an IHttpRequest.
In my success case unit test (response under 2 seconds), I get back the result I expect:
    private class TestWebResponse : WebResponse
    {
        public override Uri ResponseUri => new Uri("https://www.mytest.com/responseIsGood");
    }
    [TestMethod]
    public void RedirectUriUnderTimeout()
    {
        //arrange
        var service = GetService();
        A.CallTo(() => _httpRequest.GetResponseAsync()).ReturnsLazily(() => new TestWebResponse());
        A.CallTo(() => _httpRequest.GetResponseString())
            .ReturnsLazily(() => VALID_REQUEST_PAYLOAD);
        //act
        var url = service.GetFinalUrl("https://someplace.com/testurl");
        //assert
        Assert.IsNotNull(url);
    }
...however, when I try to implement a delay to verify the timeout is working correctly, it's not aborting as I would expect:
    [TestMethod]
    public void RedirectUriUnderTimeout()
    {
        //arrange
        var service = GetService();
        A.CallTo(() => _httpRequest.GetResponseAsync()).ReturnsLazily(() => {
            Thread.Sleep(TimeSpan.FromSeconds(3));
            return new TestWebResponse();
        });
        A.CallTo(() => _httpRequest.GetResponseString())
            .ReturnsLazily(() => VALID_REQUEST_PAYLOAD);
        //act
        var url = service.GetFinalUrl("https://someplace.com/testurl");
        //assert
        Assert.IsNull(url);
    }
It seems like it waits for the full three seconds, before returning the TestWebResponse that has a non-null ResponseUri.
I don't know if there's something fundamentally wrong with my implementation, or wrong with my test, but obviously I'm blocking an async call in a way I'm not expecting to.
Can someone help me identify what I've done wrong?
 
    