public async Task RequestLogger_LogRequest_TraceWritesStatusAndHeadersAndBody()
        {
            // Arrange
            Mock <ILogger> loggerMock = CreateFor(LogLevel.Trace);
            HttpContext    context    = new FakeHttpContextBuilder()
                                        .SetResponseBody("conflict response content")
                                        .SetResponseHeaders(new() { { "foo", new(new[] { "bar", "baz" }) } })
                                        .SetStatus(HttpStatusCode.Conflict)
                                        .Create();
            Mock <IStopwatch> stopwatchMock = new();
            var            request          = RequestDetails.From(context.Request);
            var            response         = ResponseDetails.From(context.Response, stopwatchMock.Object);
            ResponseLogger sut = new(new(null), new(null));

            // Act
            await sut.LogResponse(loggerMock.Object, request, response);

            // Assert
            loggerMock.VerifyLogging(
                $"HTTP/1.1 409 Conflict{Environment.NewLine}" +
                $"foo: bar,baz{Environment.NewLine}" +
                $"{Environment.NewLine}" +
                $"conflict response content{Environment.NewLine}",
                LogLevel.Trace);
        }
示例#2
0
        public void BasicInfoLogger_LogBasicInfo_WritesSuccessfulPostRequestDetails()
        {
            // Arrange
            Mock <ILogger>    loggerMock    = CreateFor(LogLevel.Trace);
            Mock <IStopwatch> stopwatchMock = new();

            stopwatchMock.SetupGet(stopwatch => stopwatch.Elapsed).Returns(TimeSpan.FromSeconds(2));

            HttpContext context = new FakeHttpContextBuilder()
                                  .SetRequest(
                HttpMethod.Post,
                HttpScheme.Https,
                new() { { "cat", "221" } })
                                  .Create();

            var             request  = RequestDetails.From(context.Request);
            var             response = ResponseDetails.From(context.Response, stopwatchMock.Object);
            BasicInfoLogger sut      = new();

            // Act
            sut.LogBasicInfo(loggerMock.Object, request, response);

            // Assert
            loggerMock.VerifyLogging(
                "POST https://localhost/master/slave?cat=221 at 00:00:02:000 with 200 OK",
                LogLevel.Information);
        }
示例#3
0
        public void BasicInfoLogger_LogBasicInfo_WritesErrorPostRequestDetails()
        {
            // Arrange
            Mock <ILogger>    loggerMock    = CreateFor(LogLevel.Trace);
            Mock <IStopwatch> stopwatchMock = new();

            stopwatchMock.SetupGet(stopwatch => stopwatch.Elapsed).Returns(TimeSpan.FromSeconds(3));

            HttpContext context = new FakeHttpContextBuilder()
                                  .SetMethod(HttpMethod.Post)
                                  .SetScheme(HttpScheme.Http)
                                  .SetHost(new("example.com"))
                                  .SetPathBase("/primary")
                                  .SetPath("/secondary")
                                  .SetQuery(new() { { "cats", "1" } })
                                  .SetStatus(HttpStatusCode.InternalServerError)
                                  .Create();

            var             request  = RequestDetails.From(context.Request);
            var             response = ResponseDetails.From(context.Response, stopwatchMock.Object);
            BasicInfoLogger sut      = new();

            // Act
            sut.LogBasicInfo(loggerMock.Object, request, response);

            // Assert
            loggerMock.VerifyLogging(
                "POST http://example.com/primary/secondary?cats=1 at 00:00:03:000 with 500 InternalServerError",
                LogLevel.Information);
        }
        public void RequestDetails_From_CreatesFromHttpRequest()
        {
            // Arrange
            HttpRequest request = new FakeHttpContextBuilder()
                                  .SetMethod(HttpMethod.Put)
                                  .SetScheme(HttpScheme.Http)
                                  .SetPathBase("/api")
                                  .SetPath("/v1")
                                  .SetQuery(new() { { "cat", "1" } })
                                  .SetHost(new("localhost"))
                                  .Create()
                                  .Request;

            // Act
            var result = RequestDetails.From(request);

            // Assert
            result.Should().NotBeNull()
            .And.BeEquivalentTo(new RequestDetails
            {
                Method      = "PUT",
                Scheme      = "http",
                Host        = "localhost",
                Path        = "/api/v1",
                QueryString = "?cat=1",
                Url         = "http://localhost/api/v1?cat=1",
                Protocol    = "HTTP/1.1",
                Headers     = new Dictionary <string, StringValues> {
                    { "Host", "localhost" }
                }
            }, options => options.Excluding(r => r.Content));
        }
        public void RequestDetails_From_CreatesFromHttpRequestEmptyBasePath()
        {
            // Arrange
            HttpRequest request = new FakeHttpContextBuilder()
                                  .SetMethod(HttpMethod.Post)
                                  .SetScheme(HttpScheme.Https)
                                  .SetPath("/api/v2")
                                  .SetHost(new("example.com"))
                                  .Create()
                                  .Request;

            // Act
            var result = RequestDetails.From(request);

            // Assert
            result.Should().NotBeNull()
            .And.BeEquivalentTo(new RequestDetails
            {
                Method      = "POST",
                Scheme      = "https",
                Host        = "example.com",
                Path        = "/api/v2",
                QueryString = "",
                Url         = "https://example.com/api/v2",
                Protocol    = "HTTP/1.1",
                Headers     = new Dictionary <string, StringValues> {
                    { "Host", "example.com" }
                }
            }, options => options.Excluding(r => r.Content));
        }
        public async Task HttpLogger_LogRequest_CallsLoggerWithRequestScope()
        {
            // Arrange
            var(httpLogger, _, loggerMock, requestLoggerMock, _, _) = Mock();
            HttpContext context = new FakeHttpContextBuilder()
                                  .SetMethod(HttpMethod.Head)
                                  .SetScheme(HttpScheme.Https)
                                  .SetHost(new("example.com"))
                                  .Create();

            var request = RequestDetails.From(context.Request);

            // Act
            await httpLogger.LogRequest(request);

            // Assert
            requestLoggerMock.Verify(
                requestLogger => requestLogger.LogRequest(loggerMock.Object, request),
                Times.Once);

            loggerMock.Verify(
                MatchBeginScope(new()
            {
                { "EventName", "HttpRequest" },
                { "Endpoint", "https://example.com" },
示例#7
0
        public async Task RequestLogger_LogRequest_TraceWritesUrlAndHeadersAndBody()
        {
            // Arrange
            Mock <ILogger> loggerMock = CreateFor(LogLevel.Trace);
            HttpContext    context    = new FakeHttpContextBuilder()
                                        .SetRequestBody("request content string")
                                        .SetRequestHeaders(new() { { "foo", new(new[] { "bar", "baz" }) } })
                                        .SetRequestContentType("text/plain")
                                        .SetMethod(HttpMethod.Post)
                                        .SetScheme(HttpScheme.Https)
                                        .SetHost(new("example.org"))
                                        .SetQuery(new() { { "foo", "bar" } })
                                        .Create();
            RequestLogger sut = new(new(null), new(null));

            // Act
            await sut.LogRequest(loggerMock.Object, RequestDetails.From(context.Request));

            // Assert
            loggerMock.VerifyLogging(
                $"POST https://example.org?foo=bar HTTP/1.1{Environment.NewLine}" +
                $"foo: bar,baz{Environment.NewLine}" +
                $"Content-Type: text/plain{Environment.NewLine}" +
                $"Host: example.org{Environment.NewLine}" +
                $"{Environment.NewLine}" +
                $"request content string{Environment.NewLine}",
                LogLevel.Trace);
        }
示例#8
0
        public async Task RequestLogger_LogRequest_AppliesHeaderMiddleware()
        {
            // Arrange
            Mock <ILogger> loggerMock = CreateFor(LogLevel.Debug);
            Mock <IHeaderLogMiddleware> headerMiddlewareMock = new();
            var headerMiddlewares = new List <IHeaderLogMiddleware> {
                headerMiddlewareMock.Object
            };
            LogHeaderFactory headerFactory = new(headerMiddlewares);
            HttpContext      context       = new FakeHttpContextBuilder()
                                             .SetRequest(headers: new() { { "foo", new(new[] { "bar", "baz" }) } })
                                             .Create();

            RequestLogger sut = new(new(null), headerFactory);

            // Mock
            headerMiddlewareMock
            .Setup(middleware => middleware.Modify(It.IsAny <string>(), It.IsAny <string>()))
            .Returns <string, string>((key, value) => value);

            headerMiddlewareMock
            .Setup(middleware => middleware.Modify("foo", It.IsAny <string>()))
            .Returns("*modified value*");

            // Act
            await sut.LogRequest(loggerMock.Object, RequestDetails.From(context.Request));

            // Assert
            loggerMock.VerifyLogging(
                $"GET http://localhost/master/slave HTTP/1.1{Environment.NewLine}" +
                $"Host: localhost{Environment.NewLine}" +
                $"foo: *modified value*{Environment.NewLine}",
                LogLevel.Debug);
        }
示例#9
0
        public void EndpointPredicate_Filter_MultipleIncludePatterns()
        {
            // Arrange
            Mock <HttpRequest> requestMock = new();

            // Mock
            requestMock.SetupGet(r => r.Path).Returns(new PathString("/api/123/foo"));
            var request = RequestDetails.From(requestMock.Object);
            EndpointPredicate predicate = new(false, new[] { "/api", "/health", "/ping", "/" });

            // Act
            bool skip = predicate.Filter(request);

            // Assert
            skip.Should().BeTrue();
        }
示例#10
0
        public void EndpointPredicate_Filter_IncludePatterns(string pattern, string path, bool expected)
        {
            // Arrange
            Mock <HttpRequest> requestMock = new();

            // Mock
            requestMock.SetupGet(r => r.Path).Returns(new PathString(path));
            var request = RequestDetails.From(requestMock.Object);
            EndpointPredicate predicate = new(false, new[] { pattern });

            // Act
            bool exclude = predicate.Filter(request);

            // Assert
            exclude.Should().Be(expected);
        }
示例#11
0
        public async Task RequestLogger_LogRequest_WritesLogIfLevelIsSufficient(LogLevel level)
        {
            // Arrange
            Mock <ILogger> loggerMock = CreateFor(level);
            HttpContext    context    = new FakeHttpContextBuilder().Create();
            RequestLogger  sut        = new(new(null), new(null));

            // Act
            await sut.LogRequest(loggerMock.Object, RequestDetails.From(context.Request));

            // Assert
            loggerMock.Verify(
                logger => logger.Log(
                    It.IsAny <LogLevel>(),
                    It.IsAny <EventId>(),
                    It.Is <It.IsAnyType>((v, t) => true),
                    It.IsAny <Exception>(),
                    It.Is <Func <It.IsAnyType, Exception, string> >((v, t) => true)),
                Times.Once);
        }
        public async Task RequestLogger_LogRequest_DebugWritesOnlyStatusAndHeaders()
        {
            // Arrange
            Mock <ILogger> loggerMock = CreateFor(LogLevel.Debug);
            HttpContext    context    = new FakeHttpContextBuilder()
                                        .SetResponseHeaders(new() { { "foo", new(new[] { "bar", "baz" }) } })
                                        .Create();
            Mock <IStopwatch> stopwatchMock = new();
            var            request          = RequestDetails.From(context.Request);
            var            response         = ResponseDetails.From(context.Response, stopwatchMock.Object);
            ResponseLogger sut = new(new(null), new(null));

            // Act
            await sut.LogResponse(loggerMock.Object, request, response);

            // Assert
            loggerMock.VerifyLogging(
                $"HTTP/1.1 200 OK{Environment.NewLine}" +
                $"foo: bar,baz{Environment.NewLine}",
                LogLevel.Debug);
        }
        public async Task RequestLogger_LogRequest_AppliesContentMiddleware()
        {
            // Arrange
            Mock <ILogger> loggerMock = CreateFor(LogLevel.Trace);
            Mock <IRequestContentLogMiddleware> contentMiddleware = new();
            var contentMiddlewares = new List <IRequestContentLogMiddleware> {
                contentMiddleware.Object
            };
            HttpContext context = new FakeHttpContextBuilder()
                                  .SetResponseBody("response")
                                  .SetResponseContentType("text/plain")
                                  .Create();
            Mock <IStopwatch> stopwatchMock = new();
            var request  = RequestDetails.From(context.Request);
            var response = ResponseDetails.From(context.Response, stopwatchMock.Object);
            LogContentFactory contentFactory  = new(contentMiddlewares);
            Stream            modifiedContent = new MemoryStream(Encoding.UTF8.GetBytes("*modified*"));
            ResponseLogger    sut             = new(contentFactory, new(null));

            // Mock
            contentMiddleware
            .SetupGet(middleware => middleware.ContentType)
            .Returns("text/plain");

            contentMiddleware
            .Setup(middleware => middleware.Modify(It.IsAny <Stream>(), It.IsAny <Stream>()))
            .Callback <Stream, Stream>((input, output) => modifiedContent.CopyTo(output));

            // Act
            await sut.LogResponse(loggerMock.Object, request, response);

            // Assert
            loggerMock.VerifyLogging(
                $"HTTP/1.1 200 OK{Environment.NewLine}" +
                $"Content-Type: text/plain{Environment.NewLine}" +
                $"{Environment.NewLine}" +
                $"*modified*{Environment.NewLine}",
                LogLevel.Trace);
        }
示例#14
0
        public void BasicInfoLogger_LogBasicInfo_WritesLogIfLevelIsSufficient(LogLevel level)
        {
            // Arrange
            Mock <ILogger>    loggerMock    = CreateFor(level);
            Mock <IStopwatch> stopwatchMock = new();
            HttpContext       context       = new FakeHttpContextBuilder().Create();
            var             request         = RequestDetails.From(context.Request);
            var             response        = ResponseDetails.From(context.Response, stopwatchMock.Object);
            BasicInfoLogger sut             = new();

            // Act
            sut.LogBasicInfo(loggerMock.Object, request, response);

            // Assert
            loggerMock.Verify(
                logger => logger.Log(
                    LogLevel.Information,
                    It.IsAny <EventId>(),
                    It.Is <It.IsAnyType>((v, t) => true),
                    It.IsAny <Exception>(),
                    It.Is <Func <It.IsAnyType, Exception, string> >((v, t) => true)),
                Times.Once);
        }
        public void RequestDetails_From_CreatesFromHttpRequestMessage()
        {
            // Arrange
            Uri uri = new("http://localhost/api/v1?cat=1");
            HttpRequestMessage request = new(System.Net.Http.HttpMethod.Put, uri);

            // Act
            var result = RequestDetails.From(request);

            // Assert
            result.Should().NotBeNull()
            .And.BeEquivalentTo(new RequestDetails
            {
                Method      = "PUT",
                Scheme      = "http",
                Host        = "localhost",
                Path        = "/api/v1",
                QueryString = "?cat=1",
                Url         = "http://localhost/api/v1?cat=1",
                Protocol    = "HTTP/1.1",
                Headers     = new Dictionary <string, StringValues>(),
            }, options => options.Excluding(r => r.Content));
        }
示例#16
0
        public async Task RequestLogger_LogRequest_DebugWritesOnlyUrlAndHeaders()
        {
            // Arrange
            Mock <ILogger> loggerMock = CreateFor(LogLevel.Debug);
            HttpContext    context    = new FakeHttpContextBuilder()
                                        .SetRequest(
                HttpMethod.Post,
                HttpScheme.Https,
                new() { { "foo", "bar" } },
                new() { { "foo", new(new[] { "bar", "baz" }) } })
                                        .Create();
            RequestLogger sut = new(new(null), new(null));

            // Act
            await sut.LogRequest(loggerMock.Object, RequestDetails.From(context.Request));

            // Assert
            loggerMock.VerifyLogging(
                $"POST https://localhost/master/slave?foo=bar HTTP/1.1{Environment.NewLine}" +
                $"Host: localhost{Environment.NewLine}" +
                $"foo: bar,baz{Environment.NewLine}",
                LogLevel.Debug);
        }