aspnetcore/test/Microsoft.AspNetCore.Hostin.../HostingApplicationTests.cs

330 lines
12 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Hosting.Tests
{
public class HostingApplicationTests
{
[Fact]
public void DisposeContextDoesNotThrowWhenContextScopeIsNull()
{
// Arrange
var hostingApplication = CreateApplication(out var features);
var context = hostingApplication.CreateContext(features);
// Act/Assert
hostingApplication.DisposeContext(context, null);
}
[Fact]
public void ActivityIsNotCreatedWhenIsEnabledForActivityIsFalse()
{
var diagnosticSource = new DiagnosticListener("DummySource");
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
bool eventsFired = false;
bool isEnabledActivityFired = false;
bool isEnabledStartFired = false;
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair =>
{
eventsFired |= pair.Key.StartsWith("Microsoft.AspNetCore.Hosting.HttpRequestIn");
}), (s, o, arg3) =>
{
if (s == "Microsoft.AspNetCore.Hosting.HttpRequestIn")
{
Assert.IsAssignableFrom<HttpContext>(o);
isEnabledActivityFired = true;
}
if (s == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start")
{
isEnabledStartFired = true;
}
return false;
});
hostingApplication.CreateContext(features);
Assert.Null(Activity.Current);
Assert.True(isEnabledActivityFired);
Assert.False (isEnabledStartFired);
Assert.False(eventsFired);
}
[Fact]
public void ActivityIsCreatedButNotLoggedWhenIsEnabledForActivityStartIsFalse()
{
var diagnosticSource = new DiagnosticListener("DummySource");
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
bool eventsFired = false;
bool isEnabledStartFired = false;
bool isEnabledActivityFired = false;
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair =>
{
eventsFired |= pair.Key.StartsWith("Microsoft.AspNetCore.Hosting.HttpRequestIn");
}), (s, o, arg3) =>
{
if (s == "Microsoft.AspNetCore.Hosting.HttpRequestIn")
{
Assert.IsAssignableFrom<HttpContext>(o);
isEnabledActivityFired = true;
return true;
}
if (s == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start")
{
isEnabledStartFired = true;
return false;
}
return true;
});
hostingApplication.CreateContext(features);
Assert.NotNull(Activity.Current);
Assert.True(isEnabledActivityFired);
Assert.True(isEnabledStartFired);
Assert.False(eventsFired);
}
[Fact]
public void ActivityIsCreatedAndLogged()
{
var diagnosticSource = new DiagnosticListener("DummySource");
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
bool startCalled = false;
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair =>
{
if (pair.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start")
{
startCalled = true;
Assert.NotNull(pair.Value);
Assert.NotNull(Activity.Current);
Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", Activity.Current.OperationName);
AssertProperty<HttpContext>(pair.Value, "HttpContext");
}
}));
hostingApplication.CreateContext(features);
Assert.NotNull(Activity.Current);
Assert.True(startCalled);
}
[Fact]
public void ActivityIsStoppedDuringStopCall()
{
var diagnosticSource = new DiagnosticListener("DummySource");
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
bool endCalled = false;
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair =>
{
if (pair.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop")
{
endCalled = true;
Assert.NotNull(Activity.Current);
Assert.True(Activity.Current.Duration > TimeSpan.Zero);
Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", Activity.Current.OperationName);
AssertProperty<HttpContext>(pair.Value, "HttpContext");
}
}));
var context = hostingApplication.CreateContext(features);
hostingApplication.DisposeContext(context, null);
Assert.True(endCalled);
}
[Fact]
public void ActivityIsStoppedDuringUnhandledExceptionCall()
{
var diagnosticSource = new DiagnosticListener("DummySource");
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
bool endCalled = false;
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair =>
{
if (pair.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop")
{
endCalled = true;
Assert.NotNull(Activity.Current);
Assert.True(Activity.Current.Duration > TimeSpan.Zero);
Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", Activity.Current.OperationName);
AssertProperty<HttpContext>(pair.Value, "HttpContext");
}
}));
var context = hostingApplication.CreateContext(features);
hostingApplication.DisposeContext(context, new Exception());
Assert.True(endCalled);
}
[Fact]
public void ActivityIsAvailableDuringUnhandledExceptionCall()
{
var diagnosticSource = new DiagnosticListener("DummySource");
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
bool endCalled = false;
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair =>
{
if (pair.Key == "Microsoft.AspNetCore.Hosting.UnhandledException")
{
endCalled = true;
Assert.NotNull(Activity.Current);
Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", Activity.Current.OperationName);
}
}));
var context = hostingApplication.CreateContext(features);
hostingApplication.DisposeContext(context, new Exception());
Assert.True(endCalled);
}
[Fact]
public void ActivityIsAvailibleDuringRequest()
{
var diagnosticSource = new DiagnosticListener("DummySource");
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair => { }),
s =>
{
if (s.StartsWith("Microsoft.AspNetCore.Hosting.HttpRequestIn"))
{
return true;
}
return false;
});
hostingApplication.CreateContext(features);
Assert.NotNull(Activity.Current);
Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", Activity.Current.OperationName);
}
[Fact]
public void ActivityParentIdAndBaggeReadFromHeaders()
{
var diagnosticSource = new DiagnosticListener("DummySource");
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair => { }),
s =>
{
if (s.StartsWith("Microsoft.AspNetCore.Hosting.HttpRequestIn"))
{
return true;
}
return false;
});
features.Set<IHttpRequestFeature>(new HttpRequestFeature()
{
Headers = new HeaderDictionary()
{
{"Request-Id", "ParentId1"},
{"Correlation-Context", "Key1=value1, Key2=value2"}
}
});
hostingApplication.CreateContext(features);
Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", Activity.Current.OperationName);
Assert.Equal("ParentId1", Activity.Current.ParentId);
Assert.Contains(Activity.Current.Baggage, pair => pair.Key == "Key1" && pair.Value == "value1");
Assert.Contains(Activity.Current.Baggage, pair => pair.Key == "Key2" && pair.Value == "value2");
}
private static void AssertProperty<T>(object o, string name)
{
Assert.NotNull(o);
var property = o.GetType().GetTypeInfo().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
Assert.NotNull(property);
var value = property.GetValue(o);
Assert.NotNull(value);
Assert.IsAssignableFrom<T>(value);
}
private static HostingApplication CreateApplication(out FeatureCollection features,
DiagnosticListener diagnosticSource = null)
{
var httpContextFactory = new HttpContextFactory(
new DefaultObjectPoolProvider(),
Options.Create(new FormOptions()),
new HttpContextAccessor());
var hostingApplication = new HostingApplication(
ctx => Task.FromResult(0),
new NullScopeLogger(),
diagnosticSource ?? new NoopDiagnosticSource(),
httpContextFactory);
features = new FeatureCollection();
features.Set<IHttpRequestFeature>(new HttpRequestFeature());
return hostingApplication;
}
private class NullScopeLogger : ILogger
{
public IDisposable BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
}
}
private class NoopDiagnosticSource : DiagnosticListener
{
public NoopDiagnosticSource() : base("DummyListener")
{
}
public override bool IsEnabled(string name) => true;
public override void Write(string name, object value)
{
}
}
private class CallbackDiagnosticListener : IObserver<KeyValuePair<string, object>>
{
private readonly Action<KeyValuePair<string, object>> _callback;
public CallbackDiagnosticListener(Action<KeyValuePair<string, object>> callback)
{
_callback = callback;
}
public void OnNext(KeyValuePair<string, object> value)
{
_callback(value);
}
public void OnError(Exception error)
{
}
public void OnCompleted()
{
}
}
}
}