| File: Logging\RequestHeadersEnricherTests.cs | Web Access |
| Project: src\test\Libraries\Microsoft.AspNetCore.Diagnostics.Middleware.Tests\Microsoft.AspNetCore.Diagnostics.Middleware.Tests.csproj (Microsoft.AspNetCore.Diagnostics.Middleware.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; using System.Collections.Generic; using Microsoft.AspNetCore.Diagnostics.Logging; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Compliance.Classification; using Microsoft.Extensions.Compliance.Redaction; using Microsoft.Extensions.Compliance.Testing; using Moq; using Xunit; namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; public class RequestHeadersEnricherTests { private const string HeaderKey1 = "X-RequestID"; private const string HeaderKey2 = "Host"; private const string HeaderKey3 = "NullHeader"; private const string HeaderKey4 = "X-Platform"; private const string RequestId = "RequestIdTestValue"; private const string TestValue = "TestValue"; private const string NormalizedHeaderKey1 = HttpLoggingTagNames.RequestHeaderPrefix + "x-requestid"; private const string NormalizedHeaderKey2 = HttpLoggingTagNames.RequestHeaderPrefix + "host"; private const string NormalizedHeaderKey3 = HttpLoggingTagNames.RequestHeaderPrefix + "nullheader"; private const string NormalizedHeaderKey4 = HttpLoggingTagNames.RequestHeaderPrefix + "x-platform"; private readonly Mock<IHttpContextAccessor> _accessorMock; private readonly Mock<IRedactorProvider> _redactorProviderMock; public RequestHeadersEnricherTests() { var headers = new HeaderDictionary { { HeaderKey1, RequestId }, { HeaderKey2, string.Empty }, { HeaderKey3, (string)null! }, { HeaderKey4, TestValue }, }; var featureCollection = new FeatureCollection(); var httpContextMock = new Mock<HttpContext>(MockBehavior.Strict); httpContextMock.SetupGet(c => c.Request.Headers).Returns(headers); httpContextMock.SetupGet(c => c.Request.HttpContext).Returns(httpContextMock.Object); httpContextMock.SetupGet(c => c.Features).Returns(featureCollection); _accessorMock = new Mock<IHttpContextAccessor>(MockBehavior.Strict); _accessorMock.SetupGet(r => r.HttpContext).Returns(httpContextMock.Object); var redactor = FakeRedactor.Create(); _redactorProviderMock = new Mock<IRedactorProvider>(MockBehavior.Default); _redactorProviderMock.SetReturnsDefault<Redactor>(redactor); } [Fact] public void RequestHeadersEnricher_GivenDisabledEnricherOptions_HeaderKeysDataClasses_DoesNotEnrich() { // Arrange var options = new RequestHeadersLogEnricherOptions(); var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object); var enrichedProperties = new TestLogEnrichmentTagCollector(); // Act enricher.Enrich(enrichedProperties); var enrichedState = enrichedProperties.Properties; // Assert Assert.Empty(enrichedState); } [Fact] public void RequestHeadersEnricher_GivenEnricherOptions_HeaderKeysDataClasses_Enriches() { // Arrange var options = new RequestHeadersLogEnricherOptions { HeadersDataClasses = { { HeaderKey1, FakeTaxonomy.PrivateData }, { HeaderKey4, FakeTaxonomy.PublicData } } }; Mock<IRedactorProvider> redactorProviderMock = new Mock<IRedactorProvider>(); redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PublicData)) .Returns(new FakeRedactor()); redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PrivateData)) .Returns(FakeRedactor.Create(new FakeRedactorOptions { RedactionFormat = "redacted:{0}" })); var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), redactorProviderMock.Object); var enrichedProperties = new TestLogEnrichmentTagCollector(); // Act enricher.Enrich(enrichedProperties); var enrichedState = enrichedProperties.Properties; // Assert Assert.True(enrichedState.Count == 2); Assert.Equal($"redacted:{RequestId}", enrichedState[NormalizedHeaderKey1].ToString()); Assert.Equal(TestValue, enrichedState[NormalizedHeaderKey4].ToString()); } [Fact] public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsEmpty_HeaderKeysDataClasses_PartiallyEnriches() { // Arrange var options = new RequestHeadersLogEnricherOptions { HeadersDataClasses = { { HeaderKey1, FakeTaxonomy.PrivateData }, { HeaderKey2, FakeTaxonomy.PublicData } } }; Mock<IRedactorProvider> redactorProviderMock = new Mock<IRedactorProvider>(); redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PrivateData)) .Returns(FakeRedactor.Create(new FakeRedactorOptions { RedactionFormat = "REDACTED:{0}" })); var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), redactorProviderMock.Object); var enrichedProperties = new TestLogEnrichmentTagCollector(); // Act enricher.Enrich(enrichedProperties); var enrichedState = enrichedProperties.Properties; // Assert Assert.Single(enrichedState); Assert.Equal($"REDACTED:{RequestId}", enrichedState[NormalizedHeaderKey1].ToString()); Assert.False(enrichedState.ContainsKey(NormalizedHeaderKey2)); } [Fact] public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsNull_HeaderKeysDataClasses_PartiallyEnriches() { // Arrange var options = new RequestHeadersLogEnricherOptions { HeadersDataClasses = { { HeaderKey1, FakeTaxonomy.PublicData }, { HeaderKey3, FakeTaxonomy.PublicData } } }; var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object); var enrichedProperties = new TestLogEnrichmentTagCollector(); // Act enricher.Enrich(enrichedProperties); var enrichedState = enrichedProperties.Properties; // Assert Assert.Single(enrichedState); Assert.Equal(RequestId, enrichedState[NormalizedHeaderKey1].ToString()); Assert.False(enrichedState.ContainsKey(NormalizedHeaderKey3)); } [Fact] public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsMissing_HeaderKeysDataClasses_PartiallyEnriches() { // Arrange var headerKey2 = "header_does_not_exist"; var options = new RequestHeadersLogEnricherOptions { HeadersDataClasses = { { HeaderKey1, FakeTaxonomy.PublicData }, { headerKey2, FakeTaxonomy.PublicData } } }; var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object); var enrichedProperties = new TestLogEnrichmentTagCollector(); // Act enricher.Enrich(enrichedProperties); var enrichedState = enrichedProperties.Properties; // Assert Assert.Equal(RequestId, enrichedState[NormalizedHeaderKey1].ToString()); Assert.False(enrichedState.ContainsKey(HttpLoggingTagNames.RequestHeaderPrefix + headerKey2)); } [Fact] public void RequestHeadersEnricher_GivenNullHttpContext_HeaderKeysDataClasses_DoesNotEnrich() { // Arrange var options = new RequestHeadersLogEnricherOptions { HeadersDataClasses = { { HeaderKey1, FakeTaxonomy.PublicData } } }; var accessorMock = new Mock<IHttpContextAccessor>(MockBehavior.Strict); accessorMock.SetupGet(r => r.HttpContext).Returns((HttpContext)null!); var enricher = new RequestHeadersLogEnricher(accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object); var enrichedProperties = new TestLogEnrichmentTagCollector(); // Act enricher.Enrich(enrichedProperties); var enrichedState = enrichedProperties.Properties; // Assert Assert.Empty(enrichedState); } [Fact] public void RequestHeadersEnricher_GivenNullRequest_HeaderKeysDataClasses_DoesNotEnrich() { // Arrange var options = new RequestHeadersLogEnricherOptions { HeadersDataClasses = { { HeaderKey1, FakeTaxonomy.PublicData } } }; var featureCollection = new FeatureCollection(); var httpContextMock = new Mock<HttpContext>(MockBehavior.Strict); httpContextMock.SetupGet(c => c.Request).Returns((HttpRequest)null!); httpContextMock.SetupGet(c => c.Features).Returns(featureCollection); var accessorMock = new Mock<IHttpContextAccessor>(MockBehavior.Strict); accessorMock.SetupGet(r => r.HttpContext).Returns(httpContextMock.Object); var enricher = new RequestHeadersLogEnricher(accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object); var enrichedProperties = new TestLogEnrichmentTagCollector(); // Act enricher.Enrich(enrichedProperties); var enrichedState = enrichedProperties.Properties; // Assert Assert.Empty(enrichedState); } [Fact] public void RequestHeadersEnricher_ObjectDisposedExceptionIsHandled_DoesNotEnrich() { // Arrange var httpContextMock = new Mock<HttpContext>(MockBehavior.Strict); httpContextMock.SetupGet(c => c.Request.Headers).Throws(new ObjectDisposedException("")); httpContextMock.SetupGet(c => c.Request.HttpContext).Returns(httpContextMock.Object); httpContextMock.SetupGet(c => c.Features).Throws(new ObjectDisposedException("")); var accessorMock = new Mock<IHttpContextAccessor>(MockBehavior.Strict); accessorMock.SetupGet(r => r.HttpContext).Returns(httpContextMock.Object); var options = new RequestHeadersLogEnricherOptions { HeadersDataClasses = new Dictionary<string, DataClassification> { { HeaderKey1, FakeTaxonomy.PublicData } } }; Mock<IRedactorProvider> redactorProviderMock = new Mock<IRedactorProvider>(); redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PublicData)) .Returns(new FakeRedactor()); redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PrivateData)) .Returns(FakeRedactor.Create(new FakeRedactorOptions { RedactionFormat = "redacted:{0}" })); var enricher = new RequestHeadersLogEnricher(accessorMock.Object, options.ToOptions(), redactorProviderMock.Object); var enrichedProperties = new TestLogEnrichmentTagCollector(); // Act enricher.Enrich(enrichedProperties); var enrichedState = enrichedProperties.Properties; // Assert Assert.True(enrichedState.Count == 0); } }