async public Task SendDataAsyncThrowsHttpException()
        {
            const int expectedNumSendBatchAsyncCall = 1;

            var dataSender = new SpanDataSender(new TelemetryConfiguration().WithApiKey("123456"));

            dataSender.WithDelayFunction(async(uint milliSecondsDelay) =>
            {
                await Task.Delay(0);
                return;
            });

            var actualCountCallsSendData = 0;

            dataSender.WithCaptureSendDataAsyncDelegate((spanBatch, retryNum) =>
            {
                actualCountCallsSendData++;
            });

            dataSender.WithHttpHandlerImpl((json) =>
            {
                throw new Exception("Server Error", new Exception("Inner exception message"));
            });

            var spanBatch = SpanBatchBuilder.Create()
                            .WithSpan(SpanBuilder.Create("Test Span").Build())
                            .Build();

            var result = await dataSender.SendDataAsync(spanBatch);

            Assert.AreEqual(NewRelicResponseStatus.Failure, result.ResponseStatus);
            Assert.AreEqual("Inner exception message", result.Message);
            Assert.IsNull(result.HttpStatusCode);
            Assert.AreEqual(expectedNumSendBatchAsyncCall, actualCountCallsSendData, "Unexpected Number of SendDataAsync calls");
        }
        async public Task RetryOn429WithSpecificDate_429HappensOnce()
        {
            const int delayMs = 10000;
            // The actual retry delay will be slightly less than delayMs since UtcNow is recalculated in RetryWithServerDelay()
            var errorMargin = TimeSpan.FromMilliseconds(50).TotalMilliseconds;
            var actualResponseFromTestRun = new List <Response>();

            uint actualDelayFromTestRun = 0;

            var dataSender = new SpanDataSender(new TelemetryConfiguration().WithApiKey("123456").WithMaxRetryAttempts(1));

            dataSender.WithDelayFunction(async(uint milliSecondsDelay) =>
            {
                actualDelayFromTestRun = milliSecondsDelay;
                await Task.Delay(0);
                return;
            });

            dataSender.WithHttpHandlerImpl((json) =>
            {
                var httpResponse                = new HttpResponseMessage((HttpStatusCode)429);
                var retryOnSpecificTime         = DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(delayMs);
                httpResponse.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(retryOnSpecificTime);

                return(Task.FromResult(httpResponse));
            });

            var spanBatch = SpanBatchBuilder.Create()
                            .WithSpan(SpanBuilder.Create("Test Span").Build())
                            .Build();

            var response = await dataSender.SendDataAsync(spanBatch);

            Assert.IsTrue(actualDelayFromTestRun >= delayMs - errorMargin && actualDelayFromTestRun <= delayMs + errorMargin, $"Expected delay: {delayMs}, margin: +/-{errorMargin}, actual delay: {actualDelayFromTestRun}");
        }
        async public Task RetryOn429_RetriesExceeded()
        {
            const int delayMS = 10000;
            const int expectedNumSendBatchAsyncCall      = 9; // 1 first call + 3 calls from retries
            var       expectedBackoffSequenceFromTestRun = new List <int>()
            {
                delayMS,
                delayMS,
                delayMS,
                delayMS,
                delayMS,
                delayMS,
                delayMS,
                delayMS
            };

            var actualBackoffSequenceFromTestRun = new List <uint>();

            var dataSender = new SpanDataSender(new TelemetryConfiguration().WithApiKey("123456"));

            dataSender.WithDelayFunction(async(uint milliSecondsDelay) =>
            {
                actualBackoffSequenceFromTestRun.Add(milliSecondsDelay);
                await Task.Delay(0);
                return;
            });

            var actualCountCallsSendData = 0;

            dataSender.WithCaptureSendDataAsyncDelegate((spanBatch, retryNum) =>
            {
                actualCountCallsSendData++;
            });

            dataSender.WithHttpHandlerImpl((json) =>
            {
                var httpResponse = new HttpResponseMessage((HttpStatusCode)429);
                httpResponse.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(TimeSpan.FromMilliseconds(delayMS));

                return(Task.FromResult(httpResponse));
            });

            var spanBatch = SpanBatchBuilder.Create()
                            .WithSpan(SpanBuilder.Create("Test Span").Build())
                            .Build();

            var result = await dataSender.SendDataAsync(spanBatch);

            Assert.AreEqual(NewRelicResponseStatus.Failure, result.ResponseStatus);
            Assert.AreEqual(expectedNumSendBatchAsyncCall, actualCountCallsSendData, "Unexpected Number of SendDataAsync calls");
            CollectionAssert.AreEqual(expectedBackoffSequenceFromTestRun, actualBackoffSequenceFromTestRun);
        }
        async public Task RetryBackoffSequence_IntermittentTimeoutEventuallySucceeds()
        {
            var expectedNumSendBatchAsyncCall      = 4; // 1 first call + 3 calls from retries
            var expectedBackoffSequenceFromTestRun = new List <int>()
            {
                5000,
                10000,
                20000,
            };

            var actualBackoffSequenceFromTestRun = new List <uint>();

            var dataSender = new SpanDataSender(new TelemetryConfiguration().WithApiKey("123456"));

            dataSender.WithDelayFunction(async(uint milliSecondsDelay) =>
            {
                actualBackoffSequenceFromTestRun.Add(milliSecondsDelay);
                await Task.Delay(0);
                return;
            });

            var actualCountCallsSendData = 0;

            dataSender.WithCaptureSendDataAsyncDelegate((spanBatch, retryNum) =>
            {
                actualCountCallsSendData++;
            });

            var callCount = 0;

            dataSender.WithHttpHandlerImpl((json) =>
            {
                callCount++;
                if (callCount < 4)
                {
                    return(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.RequestTimeout)));
                }

                return(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)));
            });

            var spanBatch = SpanBatchBuilder.Create()
                            .WithSpan(SpanBuilder.Create("Test Span").Build())
                            .Build();

            var result = await dataSender.SendDataAsync(spanBatch);

            Assert.AreEqual(NewRelicResponseStatus.Success, result.ResponseStatus);
            Assert.AreEqual(expectedNumSendBatchAsyncCall, actualCountCallsSendData, "Unexpected Number of SendDataAsync calls");
            CollectionAssert.AreEqual(expectedBackoffSequenceFromTestRun, actualBackoffSequenceFromTestRun);
        }
        async public Task SendDataAsyncThrowsNonHttpException()
        {
            const int expectedNumSendBatchAsyncCall = 1;
            const int expectedNumHttpHandlerCall    = 0;

            var dataSender = new SpanDataSender(new TelemetryConfiguration().WithAPIKey("123456"));

            dataSender.WithDelayFunction(async(int milliSecondsDelay) =>
            {
                await Task.Delay(0);
                return;
            });

            var actualCountCallsSendData = 0;

            dataSender.WithCaptureSendDataAsyncDelegate((sb, retry) =>
            {
                actualCountCallsSendData++;
                throw new Exception("Test Exception");
            });

            var actualCallsHttpHandler = 0;

            dataSender.WithHttpHandlerImpl((json) =>
            {
                actualCallsHttpHandler++;
                return(Task.FromResult(new HttpResponseMessage()
                {
                    StatusCode = HttpStatusCode.OK
                }));
            });


            var spanBatch = SpanBatchBuilder.Create()
                            .WithSpan(SpanBuilder.Create("Test Span").Build())
                            .Build();

            var result = await dataSender.SendDataAsync(spanBatch);

            Assert.AreEqual(NewRelicResponseStatus.Failure, result.ResponseStatus);
            Assert.AreEqual("Test Exception", result.Message);
            Assert.IsNull(result.HttpStatusCode);

            Assert.AreEqual(expectedNumSendBatchAsyncCall, actualCountCallsSendData, "Unexpected Number of SendDataAsync calls");
            Assert.AreEqual(expectedNumHttpHandlerCall, actualCallsHttpHandler, "Unexpected Number of Http Handler calls");
        }
        public void DateSpecificRetry_CorrectDelayDuration()
        {
            var traceId         = "123";
            var retryDuration   = TimeSpan.FromSeconds(10);
            var retryDurationMs = retryDuration.TotalMilliseconds;
            var errorMargin     = TimeSpan.FromMilliseconds(50).TotalMilliseconds;

            var spanBatch = SpanBatchBuilder.Create()
                            .WithTraceId(traceId)
                            .WithSpan(SpanBuilder.Create("TestSpan").Build())
                            .Build();

            var config = new TelemetryConfiguration().WithAPIKey("12345");

            var dataSender = new SpanDataSender(config);

            //Mock out the communication layer
            dataSender.WithHttpHandlerImpl((serializedJson) =>
            {
                var response                = new HttpResponseMessage((System.Net.HttpStatusCode) 429);
                var retryOnSpecificTime     = DateTimeOffset.Now + retryDuration;
                response.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(retryOnSpecificTime);
                return(Task.FromResult(response));
            });

            var capturedDelays = new List <int>();

            dataSender.WithDelayFunction((delay) =>
            {
                capturedDelays.Add(delay);
                return(Task.Delay(0));
            });

            var response = dataSender.SendDataAsync(spanBatch).Result;

            Assert.AreEqual(NewRelicResponseStatus.Failure, response.ResponseStatus);
            Assert.AreEqual(config.MaxRetryAttempts, capturedDelays.Count);
            Assert.AreEqual(System.Net.HttpStatusCode.RequestTimeout, response.HttpStatusCode);
            Assert.IsTrue(capturedDelays.All(x => x >= retryDurationMs - errorMargin && x <= retryDurationMs + errorMargin),
                          "Expected duration out of range");
        }