using Bit.Api.Models.Public.Request; using Bit.Api.Models.Public.Response; using Bit.Api.Utilities.DiagnosticTools; using Bit.Core; using Bit.Core.Models.Data; using Bit.Core.Services; using Bit.Test.Common.AutoFixture.Attributes; using Microsoft.Extensions.Logging; using NSubstitute; using Xunit; namespace Bit.Api.Test.Utilities.DiagnosticTools; public class EventDiagnosticLoggerTests { [Theory, BitAutoData] public void LogAggregateData_WithPublicResponse_FeatureFlagEnabled_LogsInformation( Guid organizationId) { // Arrange var logger = Substitute.For(); var featureService = Substitute.For(); featureService.IsEnabled(FeatureFlagKeys.EventDiagnosticLogging).Returns(true); var request = new EventFilterRequestModel() { Start = DateTime.UtcNow.AddMinutes(-3), End = DateTime.UtcNow, ActingUserId = Guid.NewGuid(), ItemId = Guid.NewGuid(), }; var newestEvent = Substitute.For(); newestEvent.Date.Returns(DateTime.UtcNow); var middleEvent = Substitute.For(); middleEvent.Date.Returns(DateTime.UtcNow.AddDays(-1)); var oldestEvent = Substitute.For(); oldestEvent.Date.Returns(DateTime.UtcNow.AddDays(-3)); var eventResponses = new List { new (newestEvent), new (middleEvent), new (oldestEvent) }; var response = new PagedListResponseModel(eventResponses, "continuation-token"); // Act logger.LogAggregateData(featureService, organizationId, response, request); // Assert logger.Received(1).Log( LogLevel.Information, Arg.Any(), Arg.Is(o => o.ToString().Contains(organizationId.ToString()) && o.ToString().Contains($"Event count:{eventResponses.Count}") && o.ToString().Contains($"newest record:{newestEvent.Date:O}") && o.ToString().Contains($"oldest record:{oldestEvent.Date:O}") && o.ToString().Contains("HasMore:True") && o.ToString().Contains($"Start:{request.Start:o}") && o.ToString().Contains($"End:{request.End:o}") && o.ToString().Contains($"ActingUserId:{request.ActingUserId}") && o.ToString().Contains($"ItemId:{request.ItemId}")) , null, Arg.Any>()); } [Theory, BitAutoData] public void LogAggregateData_WithPublicResponse_FeatureFlagDisabled_DoesNotLog( Guid organizationId, EventFilterRequestModel request) { // Arrange var logger = Substitute.For(); var featureService = Substitute.For(); featureService.IsEnabled(FeatureFlagKeys.EventDiagnosticLogging).Returns(false); PagedListResponseModel dummy = null; // Act logger.LogAggregateData(featureService, organizationId, dummy, request); // Assert logger.DidNotReceive().Log( LogLevel.Information, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any>()); } [Theory, BitAutoData] public void LogAggregateData_WithPublicResponse_EmptyData_LogsZeroCount( Guid organizationId) { // Arrange var logger = Substitute.For(); var featureService = Substitute.For(); featureService.IsEnabled(FeatureFlagKeys.EventDiagnosticLogging).Returns(true); var request = new EventFilterRequestModel() { Start = null, End = null, ActingUserId = null, ItemId = null, ContinuationToken = null, }; var response = new PagedListResponseModel(new List(), null); // Act logger.LogAggregateData(featureService, organizationId, response, request); // Assert logger.Received(1).Log( LogLevel.Information, Arg.Any(), Arg.Is(o => o.ToString().Contains(organizationId.ToString()) && o.ToString().Contains("Event count:0") && o.ToString().Contains("HasMore:False")), null, Arg.Any>()); } [Theory, BitAutoData] public void LogAggregateData_WithInternalResponse_FeatureFlagDisabled_DoesNotLog(Guid organizationId) { // Arrange var logger = Substitute.For(); var featureService = Substitute.For(); featureService.IsEnabled(FeatureFlagKeys.EventDiagnosticLogging).Returns(false); // Act logger.LogAggregateData(featureService, organizationId, null, null, null, null); // Assert logger.DidNotReceive().Log( LogLevel.Information, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any>()); } [Theory, BitAutoData] public void LogAggregateData_WithInternalResponse_EmptyData_LogsZeroCount( Guid organizationId) { // Arrange var logger = Substitute.For(); var featureService = Substitute.For(); featureService.IsEnabled(FeatureFlagKeys.EventDiagnosticLogging).Returns(true); Bit.Api.Models.Response.EventResponseModel[] emptyEvents = []; // Act logger.LogAggregateData(featureService, organizationId, emptyEvents, null, null, null); // Assert logger.Received(1).Log( LogLevel.Information, Arg.Any(), Arg.Is(o => o.ToString().Contains(organizationId.ToString()) && o.ToString().Contains("Event count:0") && o.ToString().Contains("HasMore:False")), null, Arg.Any>()); } [Theory, BitAutoData] public void LogAggregateData_WithInternalResponse_FeatureFlagEnabled_LogsInformation( Guid organizationId) { // Arrange var logger = Substitute.For(); var featureService = Substitute.For(); featureService.IsEnabled(FeatureFlagKeys.EventDiagnosticLogging).Returns(true); var newestEvent = Substitute.For(); newestEvent.Date.Returns(DateTime.UtcNow); var middleEvent = Substitute.For(); middleEvent.Date.Returns(DateTime.UtcNow.AddDays(-1)); var oldestEvent = Substitute.For(); oldestEvent.Date.Returns(DateTime.UtcNow.AddDays(-2)); var events = new List { new (newestEvent), new (middleEvent), new (oldestEvent) }; var queryStart = DateTime.UtcNow.AddMinutes(-3); var queryEnd = DateTime.UtcNow; const string continuationToken = "continuation-token"; // Act logger.LogAggregateData(featureService, organizationId, events, continuationToken, queryStart, queryEnd); // Assert logger.Received(1).Log( LogLevel.Information, Arg.Any(), Arg.Is(o => o.ToString().Contains(organizationId.ToString()) && o.ToString().Contains($"Event count:{events.Count}") && o.ToString().Contains($"newest record:{newestEvent.Date:O}") && o.ToString().Contains($"oldest record:{oldestEvent.Date:O}") && o.ToString().Contains("HasMore:True") && o.ToString().Contains($"Start:{queryStart:o}") && o.ToString().Contains($"End:{queryEnd:o}")) , null, Arg.Any>()); } }