File: Model\TraceDetailPageViewModelTests.cs
Web Access
Project: src\tests\Aspire.Dashboard.Tests\Aspire.Dashboard.Tests.csproj (Aspire.Dashboard.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Aspire.Dashboard.Model;
using Aspire.Dashboard.Model.Otlp;
using Aspire.Dashboard.Otlp.Model;
using Aspire.Tests.Shared.Telemetry;
using Google.Protobuf.Collections;
using Microsoft.Extensions.Logging.Abstractions;
using OpenTelemetry.Proto.Common.V1;
using Xunit;
using static Aspire.Dashboard.Components.Pages.TraceDetail;
 
namespace Aspire.Dashboard.Tests.Model;
 
public sealed class TraceDetailPageViewModelTests
{
    [Fact]
    public void NoSelectedData_ReturnsNotExcluded()
    {
        var vm = new TraceDetailPageViewModel
        {
            SelectedData = null,
            SelectedSpanType = new SelectViewModel<SpanType> { Name = "All", Id = default }
        };
 
        var result = vm.IsSelectedDataExcludedByFilters([]);
 
        Assert.False(result);
    }
 
    [Fact]
    public void SelectedSpanPresentInVisible_ReturnsNotExcluded()
    {
        var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() };
        var resource = new OtlpResource("app1", "instance", uninstrumentedPeer: false, context);
        var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue);
        var scope = TelemetryTestHelpers.CreateOtlpScope(context);
        var span = TelemetryTestHelpers.CreateOtlpSpan(resource, trace, scope, spanId: "span1", parentSpanId: null, startDate: DateTime.UtcNow);
 
        var spanVm = CreateSpanWaterfallViewModel(span);
 
        var vm = new TraceDetailPageViewModel
        {
            SelectedData = new TraceDetailSelectedDataViewModel
            {
                SpanViewModel = new SpanDetailsViewModel
                {
                    Span = span,
                    Properties = [],
                    Links = [],
                    Backlinks = [],
                    Title = "Test",
                    Resources = []
                }
            },
            SelectedSpanType = new SelectViewModel<SpanType> { Name = "All", Id = default }
        };
 
        var result = vm.IsSelectedDataExcludedByFilters([spanVm]);
 
        Assert.False(result);
    }
 
    [Fact]
    public void SelectedSpanNotInVisible_ReturnsExcluded()
    {
        var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() };
        var resource = new OtlpResource("app1", "instance", uninstrumentedPeer: false, context);
        var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue);
        var scope = TelemetryTestHelpers.CreateOtlpScope(context);
        var selectedSpan = TelemetryTestHelpers.CreateOtlpSpan(resource, trace, scope, spanId: "span1", parentSpanId: null, startDate: DateTime.UtcNow);
        var otherSpan = TelemetryTestHelpers.CreateOtlpSpan(resource, trace, scope, spanId: "span2", parentSpanId: null, startDate: DateTime.UtcNow);
 
        var otherVm = CreateSpanWaterfallViewModel(otherSpan);
 
        var vm = new TraceDetailPageViewModel
        {
            SelectedData = new TraceDetailSelectedDataViewModel
            {
                SpanViewModel = new SpanDetailsViewModel
                {
                    Span = selectedSpan,
                    Properties = [],
                    Links = [],
                    Backlinks = [],
                    Title = "Test",
                    Resources = []
                }
            },
            SelectedSpanType = new SelectViewModel<SpanType> { Name = "All", Id = default }
        };
 
        var result = vm.IsSelectedDataExcludedByFilters([otherVm]);
 
        Assert.True(result);
    }
 
    [Fact]
    public void SelectedLogPresentInSpanLogs_ReturnsNotExcluded()
    {
        var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() };
        var resource = new OtlpResource("app1", "instance", uninstrumentedPeer: false, context);
        var resourceView = new OtlpResourceView(resource, new RepeatedField<KeyValue>());
        var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue);
        var scope = TelemetryTestHelpers.CreateOtlpScope(context);
        var span = TelemetryTestHelpers.CreateOtlpSpan(resource, trace, scope, spanId: "span1", parentSpanId: null, startDate: DateTime.UtcNow);
        var logEntry = new OtlpLogEntry(TelemetryTestHelpers.CreateLogRecord(message: "test log"), resourceView, scope, context);
 
        var spanVm = CreateSpanWaterfallViewModel(span, [new SpanLogEntryViewModel { Index = 0, LogEntry = logEntry, LeftOffset = 0 }]);
 
        var vm = new TraceDetailPageViewModel
        {
            SelectedData = new TraceDetailSelectedDataViewModel
            {
                LogEntryViewModel = new StructureLogsDetailsViewModel { LogEntry = logEntry }
            },
            SelectedSpanType = new SelectViewModel<SpanType> { Name = "All", Id = default }
        };
 
        var result = vm.IsSelectedDataExcludedByFilters([spanVm]);
 
        Assert.False(result);
    }
 
    [Fact]
    public void SelectedLogNotInAnySpanLogs_ReturnsExcluded()
    {
        var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() };
        var resource = new OtlpResource("app1", "instance", uninstrumentedPeer: false, context);
        var resourceView = new OtlpResourceView(resource, new RepeatedField<KeyValue>());
        var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue);
        var scope = TelemetryTestHelpers.CreateOtlpScope(context);
        var span = TelemetryTestHelpers.CreateOtlpSpan(resource, trace, scope, spanId: "span1", parentSpanId: null, startDate: DateTime.UtcNow);
        var logEntry = new OtlpLogEntry(TelemetryTestHelpers.CreateLogRecord(message: "test log"), resourceView, scope, context);
 
        // Span with no logs
        var spanVm = CreateSpanWaterfallViewModel(span);
 
        var vm = new TraceDetailPageViewModel
        {
            SelectedData = new TraceDetailSelectedDataViewModel
            {
                LogEntryViewModel = new StructureLogsDetailsViewModel { LogEntry = logEntry }
            },
            SelectedSpanType = new SelectViewModel<SpanType> { Name = "All", Id = default }
        };
 
        var result = vm.IsSelectedDataExcludedByFilters([spanVm]);
 
        Assert.True(result);
    }
 
    private static SpanWaterfallViewModel CreateSpanWaterfallViewModel(OtlpSpan span, List<SpanLogEntryViewModel>? spanLogs = null)
    {
        return new SpanWaterfallViewModel
        {
            Span = span,
            Children = [],
            LeftOffset = 0,
            Width = 100,
            Depth = 0,
            LabelIsRight = true,
            UninstrumentedPeer = null,
            SpanLogs = spanLogs ?? [],
            IsHidden = false
        };
    }
}