File: Circuits\CircuitMetricsTest.cs
Web Access
Project: src\src\Components\Server\test\Microsoft.AspNetCore.Components.Server.Tests.csproj (Microsoft.AspNetCore.Components.Server.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Diagnostics.Metrics;
using Microsoft.Extensions.Diagnostics.Metrics.Testing;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.InternalTesting;
using Moq;
 
namespace Microsoft.AspNetCore.Components.Server.Circuits;
 
public class CircuitMetricsTest
{
    private readonly TestMeterFactory _meterFactory;
 
    public CircuitMetricsTest()
    {
        _meterFactory = new TestMeterFactory();
    }
 
    [Fact]
    public void Constructor_CreatesMetersCorrectly()
    {
        // Arrange & Act
        var circuitMetrics = new CircuitMetrics(_meterFactory);
 
        // Assert
        Assert.Single(_meterFactory.Meters);
        Assert.Equal(CircuitMetrics.MeterName, _meterFactory.Meters[0].Name);
    }
 
    [Fact]
    public void OnCircuitOpened_IncreasesCounters()
    {
        // Arrange
        var circuitMetrics = new CircuitMetrics(_meterFactory);
        using var activeTotalCounter = new MetricCollector<long>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.count");
        using var activeCircuitCounter = new MetricCollector<long>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.active_circuits");
 
        // Act
        circuitMetrics.OnCircuitOpened();
 
        // Assert
        var totalMeasurements = activeTotalCounter.GetMeasurementSnapshot();
        var activeMeasurements = activeCircuitCounter.GetMeasurementSnapshot();
 
        Assert.Single(totalMeasurements);
        Assert.Equal(1, totalMeasurements[0].Value);
 
        Assert.Single(activeMeasurements);
        Assert.Equal(1, activeMeasurements[0].Value);
    }
 
    [Fact]
    public void OnConnectionUp_IncreasesConnectedCounter()
    {
        // Arrange
        var circuitMetrics = new CircuitMetrics(_meterFactory);
        using var connectedCircuitCounter = new MetricCollector<long>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.connected_circuits");
 
        // Act
        circuitMetrics.OnConnectionUp();
 
        // Assert
        var measurements = connectedCircuitCounter.GetMeasurementSnapshot();
 
        Assert.Single(measurements);
        Assert.Equal(1, measurements[0].Value);
    }
 
    [Fact]
    public void OnConnectionDown_DecreasesConnectedCounter()
    {
        // Arrange
        var circuitMetrics = new CircuitMetrics(_meterFactory);
        using var connectedCircuitCounter = new MetricCollector<long>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.connected_circuits");
 
        // Act
        circuitMetrics.OnConnectionDown();
 
        // Assert
        var measurements = connectedCircuitCounter.GetMeasurementSnapshot();
 
        Assert.Single(measurements);
        Assert.Equal(-1, measurements[0].Value);
    }
 
    [Fact]
    public void OnCircuitDown_UpdatesCountersAndRecordsDuration()
    {
        // Arrange
        var circuitMetrics = new CircuitMetrics(_meterFactory);
        using var activeCircuitCounter = new MetricCollector<long>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.active_circuits");
        using var connectedCircuitCounter = new MetricCollector<long>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.connected_circuits");
        using var circuitDurationCollector = new MetricCollector<double>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.duration");
 
        // Act
        var startTime = Stopwatch.GetTimestamp();
        Thread.Sleep(10); // Add a small delay to ensure a measurable duration
        var endTime = Stopwatch.GetTimestamp();
        circuitMetrics.OnCircuitDown(startTime, endTime);
 
        // Assert
        var activeMeasurements = activeCircuitCounter.GetMeasurementSnapshot();
        var connectedMeasurements = connectedCircuitCounter.GetMeasurementSnapshot();
        var durationMeasurements = circuitDurationCollector.GetMeasurementSnapshot();
 
        Assert.Single(activeMeasurements);
        Assert.Equal(-1, activeMeasurements[0].Value);
 
        Assert.Single(connectedMeasurements);
        Assert.Equal(-1, connectedMeasurements[0].Value);
 
        Assert.Single(durationMeasurements);
        Assert.True(durationMeasurements[0].Value > 0);
    }
 
    [Fact]
    public void IsDurationEnabled_ReturnsMeterEnabledState()
    {
        // Arrange
        var circuitMetrics = new CircuitMetrics(_meterFactory);
 
        // Create a collector to ensure the meter is enabled
        using var circuitDurationCollector = new MetricCollector<double>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.duration");
 
        // Act & Assert
        Assert.True(circuitMetrics.IsDurationEnabled());
    }
 
    [Fact]
    public void FullCircuitLifecycle_RecordsAllMetricsCorrectly()
    {
        // Arrange
        var circuitMetrics = new CircuitMetrics(_meterFactory);
        using var totalCounter = new MetricCollector<long>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.count");
        using var activeCircuitCounter = new MetricCollector<long>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.active_circuits");
        using var connectedCircuitCounter = new MetricCollector<long>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.connected_circuits");
        using var circuitDurationCollector = new MetricCollector<double>(_meterFactory,
            CircuitMetrics.MeterName, "aspnetcore.components.circuits.duration");
 
        // Act - Simulating a full circuit lifecycle
        var startTime = Stopwatch.GetTimestamp();
 
        // 1. Circuit opens
        circuitMetrics.OnCircuitOpened();
 
        // 2. Connection established
        circuitMetrics.OnConnectionUp();
 
        // 3. Connection drops
        circuitMetrics.OnConnectionDown();
 
        // 4. Connection re-established
        circuitMetrics.OnConnectionUp();
 
        // 5. Circuit closes
        Thread.Sleep(10); // Add a small delay to ensure a measurable duration
        var endTime = Stopwatch.GetTimestamp();
        circuitMetrics.OnCircuitDown(startTime, endTime);
 
        // Assert
        var totalMeasurements = totalCounter.GetMeasurementSnapshot();
        var activeMeasurements = activeCircuitCounter.GetMeasurementSnapshot();
        var connectedMeasurements = connectedCircuitCounter.GetMeasurementSnapshot();
        var durationMeasurements = circuitDurationCollector.GetMeasurementSnapshot();
 
        // Total circuit count should have 1 measurement with value 1
        Assert.Single(totalMeasurements);
        Assert.Equal(1, totalMeasurements[0].Value);
 
        // Active circuit count should have 2 measurements (1 for open, -1 for close)
        Assert.Equal(2, activeMeasurements.Count);
        Assert.Equal(1, activeMeasurements[0].Value);
        Assert.Equal(-1, activeMeasurements[1].Value);
 
        // Connected circuit count should have 4 measurements (connecting, disconnecting, reconnecting, closing)
        Assert.Equal(4, connectedMeasurements.Count);
        Assert.Equal(1, connectedMeasurements[0].Value);
        Assert.Equal(-1, connectedMeasurements[1].Value);
        Assert.Equal(1, connectedMeasurements[2].Value);
        Assert.Equal(-1, connectedMeasurements[3].Value);
 
        // Duration should have 1 measurement with a positive value
        Assert.Single(durationMeasurements);
        Assert.True(durationMeasurements[0].Value > 0);
    }
}