<commit_before>﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using HealthMonitoring.Integration.PushClient.Client;
using HealthMonitoring.Integration.PushClient.Client.Models;
using HealthMonitoring.Integration.PushClient.Helpers;
using HealthMonitoring.Integration.PushClient.Monitoring;
using HealthMonitoring.Integration.PushClient.Registration;
using HealthMonitoring.Integration.PushClient.UnitTests.Helpers;
using HealthMonitoring.TestUtils.Awaitable;
using Moq;
using Xunit;

namespace HealthMonitoring.Integration.PushClient.UnitTests
{
    public class EndpointNotifierTests
    {
        private readonly Mock<IHealthMonitorClient> _mockClient = new Mock<IHealthMonitorClient>();
        private readonly AwaitableFactory _awaitableFactory = new AwaitableFactory();
        private static readonly TimeSpan TestMaxTime = TimeSpan.FromSeconds(5);
        private readonly Mock<ITimeCoordinator> _mockTimeCoordinator = new Mock<ITimeCoordinator>();
        private readonly Mock<IBackOffStategy> _mockBackOffStategy = new Mock<IBackOffStategy>();
        private const int MaxEndpointNotifierRetryDelayInSecs = 120;
        private const string AuthenticationToken = "token";

        [Fact]
        public async Task Notifier_should_send_notifications_within_specified_time_span()
        {
            var minimumChecks = 3;
            var expectedEventTimeline = new List<string>();
            for (var i = 0; i < minimumChecks; ++i)
                expectedEventTimeline.AddRange(new[] { "updateHealth_start", "delay_start", "update_finish", "update_finish" });

            var countdown = new AsyncCountdown("notifications", minimumChecks);
            var endpointId = Guid.NewGuid();
            var interval = TimeSpan.FromMilliseconds(300);

            SetupHealthCheckInterval(interval);

            SetupEndpointRegistration(endpointId);

            _mockClient
                .Setup(c => c.SendHealthUpdateAsync(endpointId, AuthenticationToken, It.IsAny<HealthUpdate>(), It.IsAny<CancellationToken>()))
                .Returns(() => _awaitableFactory.Return().WithDelay(TimeSpan.FromMilliseconds(100)).WithTimeline("updateHealth").WithCountdown(countdown).RunAsync());

            _mockTimeCoordinator
                .Setup(c => c.Delay(interval, It.IsAny<CancellationToken>()))
                .Returns(() => _awaitableFactory.Return().WithDelay(TimeSpan.FromMilliseconds(50)).WithTimeline("delay").RunAsync());

            using (CreateNotifier())
                await countdown.WaitAsync(TestMaxTime);

            var actualEventTimeline = _awaitableFactory.GetOrderedTimelineEvents()
                .Select(eventName => (eventName == "updateHealth_finish" || eventName == "delay_finish") ? "update_finish" : eventName)
                .Take(minimumChecks * 4)
                .ToArray();

            Assert.True(expectedEventTimeline.SequenceEqual(actualEventTimeline), $"Expected:\n{string.Join(",", expectedEventTimeline)}\nGot:\n{string.Join(",", actualEventTimeline)}");
        }

        [Fact]
        public async Task Notifier_should_send_current_health_to_the_API()
        {
            var endpointId = Guid.NewGuid();
            var expectedHealth = new EndpointHealth(HealthStatus.Offline, new Dictionary<string, string> { { "key", "value" } });
            HealthUpdate lastCaptured = null;
            var countdown = new AsyncCountdown("update", 5);

            SetupHealthCheckInterval(TimeSpan.FromMilliseconds(1));
            SetupEndpointRegistration(endpointId);

            _mockClient.Setup(c => c.SendHealthUpdateAsync(endpointId, AuthenticationToken, It.IsAny<HealthUpdate>(), It.IsAny<CancellationToken>()))
                .Returns((Guid id, string authToken, HealthUpdate upd, CancellationToken token) => _awaitableFactory
                    .Execute(() => lastCaptured = upd)
                    .WithCountdown(countdown)
                    .RunAsync());

            using (CreateNotifier(token => Task.FromResult(expectedHealth)))
                await countdown.WaitAsync(TestMaxTime);

            Assert.NotNull(lastCaptured);
            Assert.Equal(expectedHealth.Status, lastCaptured.Status);
            Assert.Equal(expectedHealth.Details, lastCaptured.Details);<commit_msg>EndpointHealthNotifier Task.WhenAll tasks start nondeterministically, updateHealth_finish can occur before delay_start if SynchronizeCheckIntervalAsync is not scheduled within 50ms for example<commit_after>﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using HealthMonitoring.Integration.PushClient.Client;
using HealthMonitoring.Integration.PushClient.Client.Models;
using HealthMonitoring.Integration.PushClient.Helpers;
using HealthMonitoring.Integration.PushClient.Monitoring;
using HealthMonitoring.Integration.PushClient.Registration;
using HealthMonitoring.Integration.PushClient.UnitTests.Helpers;
using HealthMonitoring.TestUtils.Awaitable;
using Moq;
using Xunit;

namespace HealthMonitoring.Integration.PushClient.UnitTests
{
    public class EndpointNotifierTests
    {
        private readonly Mock<IHealthMonitorClient> _mockClient = new Mock<IHealthMonitorClient>();
        private readonly AwaitableFactory _awaitableFactory = new AwaitableFactory();
        private static readonly TimeSpan TestMaxTime = TimeSpan.FromSeconds(5);
        private readonly Mock<ITimeCoordinator> _mockTimeCoordinator = new Mock<ITimeCoordinator>();
        private readonly Mock<IBackOffStategy> _mockBackOffStategy = new Mock<IBackOffStategy>();
        private const int MaxEndpointNotifierRetryDelayInSecs = 120;
        private const string AuthenticationToken = "token";

        [Fact]
        public async Task Notifier_should_send_notifications_within_specified_time_span()
        {
            var minimumChecks = 3;
            var expectedEventTimeline = new List<string>();
            for (var i = 0; i < minimumChecks; ++i)
                expectedEventTimeline.AddRange(new[] { "update_start", "update_finish" });

            var countdown = new AsyncCountdown("notifications", minimumChecks);
            var endpointId = Guid.NewGuid();
            var interval = TimeSpan.FromMilliseconds(300);

            SetupHealthCheckInterval(interval);

            SetupEndpointRegistration(endpointId);

            _mockClient
                .Setup(c => c.SendHealthUpdateAsync(endpointId, AuthenticationToken, It.IsAny<HealthUpdate>(), It.IsAny<CancellationToken>()))
                .Returns(() => _awaitableFactory.Return().WithDelay(TimeSpan.FromMilliseconds(100)).WithTimeline("updateHealth").WithCountdown(countdown).RunAsync());

            _mockTimeCoordinator
                .Setup(c => c.Delay(interval, It.IsAny<CancellationToken>()))
                .Returns(() => _awaitableFactory.Return().WithDelay(TimeSpan.FromMilliseconds(50)).WithTimeline("delay").RunAsync());

            using (CreateNotifier())
                await countdown.WaitAsync(TestMaxTime);

            var actualEventTimeline = GetAggregateFirstAndLastEvents(minimumChecks);

            Assert.True(expectedEventTimeline.SequenceEqual(actualEventTimeline), $"Expected:\n{string.Join(",", expectedEventTimeline)}\nGot:\n{string.Join(",", actualEventTimeline)}");
        }

        private string[] GetAggregateFirstAndLastEvents(int minimumChecks)
        {
            return _awaitableFactory.GetOrderedTimelineEvents()
                .Select(eventName => eventName == "updateHealth_start" || eventName == "delay_start" ? "update_start" : eventName)
                .Select(eventName => eventName == "updateHealth_finish" || eventName == "delay_finish" ? "update_finish" : eventName)
                .Take(minimumChecks * 4)
                .Where((e, i) => i % 4 == 0 || i % 4 == 3)
                .ToArray();
        }

        [Fact]
        public async Task Notifier_should_send_current_health_to_the_API()
        {
            var endpointId = Guid.NewGuid();
            var expectedHealth = new EndpointHealth(HealthStatus.Offline, new Dictionary<string, string> { { "key", "value" } });
            HealthUpdate lastCaptured = null;
            var countdown = new AsyncCountdown("update", 5);

            SetupHealthCheckInterval(TimeSpan.FromMilliseconds(1));
            SetupEndpointRegistration(endpointId);

            _mockClient.Setup(c => c.SendHealthUpdateAsync(endpointId, AuthenticationToken, It.IsAny<HealthUpdate>(), It.IsAny<CancellationToken>()))
                .Returns((Guid id, string authToken, HealthUpdate upd, CancellationToken token) => _awaitableFactory
                    .Execute(() => lastCaptured = upd)
                    .WithCountdown(countdown)
                    .RunAsync());

            using (CreateNotifier(token => Task.FromResult(expectedHealth)))
                await countdown.WaitAsync(TestMaxTime);

            Assert.NotNull(lastCaptured);
            Assert.Equal(expectedHealth.Status, lastCaptured.Status);
            Assert.Equal(expectedHealth.Details, lastCaptured.Details);
{'fraction_non_alphanumeric': 0.09992510966085374, 'fraction_numerical': 0.0042794479512142936, 'mean_word_length': 3.388732394366197, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6649471', 'n_tokens_mistral': 2451, 'n_tokens_neox': 2390, 'n_words': 450}