예제 #1
0
        public async Task When_UserAsksForMovieWithMostFields_Then_ReturnMovieProperly_Incomplete()
        {
            // arrange
            var client = Fixture.Server.CreateClient();

            client.CreateSession();
            var response       = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/OmdbApi/Real_Responses/Happy/200_ContainsMostFields_TheMatrix.txt");
            var matrixMovieUrl = $"{MovieUrl}&t=matrix";

            client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(matrixMovieUrl), response);

            // act
            var httpResponse = await client.GetAsync("/api/movie/matrix");

            // assert
            httpResponse.Should().NotBeNull();
            httpResponse.StatusCode.Should().Be(HttpStatusCode.OK);

            var movie = JsonSerializer.Deserialize <MovieProject.Logic.DTO.Media>(await httpResponse.Content.ReadAsStringAsync());

            movie.Should().NotBeNull();

            movie.Id.Should().Be("tt0133093");
            movie.Name.Should().Be("The Matrix");
            movie.Year.Should().Be("1999");
            movie.Runtime.Should().Be("136 min");
            movie.Plot.Should().Be("A computer hacker learns from mysterious rebels about the true nature of his reality and his role in the war against its controllers.");
        }
        public async Task When_UserAsksForMovieThatDoesntExist_Then_Return400Status()
        {
            // arrange
            var client = Fixture.Server.CreateClient();

            client.CreateSession();
            var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/OmdbApi/Real_Responses/Happy/200_MovieNotFound.txt");

            client.AppendHttpCallStub(HttpMethod.Get, new System.Uri($"{MovieUrl}&t=some_weird_title"), response);

            // act
            var httpResponse = await client.GetAsync("/api/movie/some_weird_title");

            // assert logs
            var logs = client.GetSessionLogs();

            logs.ShouldBeEmpty();

            // assert outgoing
            var outgoingRequests = client.GetSessionOutgoingRequests();

            outgoingRequests.Count.ShouldBe(1);
            outgoingRequests[0].GetEndpoint().ShouldBe($"GET {MovieUrl}&t=some_weird_title");
            outgoingRequests[0].GetHeaderValue("Referer").ShouldBe(MovieProject.Logic.Constants.Website);

            // assert return
            httpResponse.StatusCode.ShouldBe(HttpStatusCode.NotFound);
            var message = await httpResponse.ReadBody();

            message.ShouldBe("Search terms didn't match any movie");
        }
예제 #3
0
        public async Task When_DownstreamSystemReturnsInvalidJson_Then_LogError_And_ReturnDefaultErrorMessage()
        {
            // arrange
            var client = Fixture.Server.CreateClient();

            client.CreateSession();
            var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/OmdbApi/Fake_Responses/Unhappy/200_Unexpected_Json.txt");

            // we add it twice to account for the recall attempt
            client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(MatrixMovieUrl), response);

            // act
            var httpResponse = await client.GetAsync("/api/movie/matrix");

            using (new AssertionScope())
            {
                // assert logs
                var logs = client.GetSessionLogs();
                logs.Count.Should().Be(1);
                // we check that we log downstream errors specifically with extra details so we can easily debug, the format should be
                // Critical: URL returned invalid response: http status=XXX and body [FULL RESPONSE BODY HERE]
                logs[0].ToString().Should().StartWith($"Critical: GET {MatrixMovieUrl} had exception [DTO is invalid] while [processing response], response HttpStatus=200 and body=[");
                logs[0].Message.Should().Contain(@"""weirdRoot"":");

                // assert outgoing
                var outgoingRequests = client.GetSessionOutgoingRequests();
                AssertMatrixEndpointCalled(outgoingRequests[0]);

                // assert return
                httpResponse.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
                var message = await httpResponse.ReadBody();

                message.Should().Be(Constants.DownstreamErrorMessage);
            }
        }
예제 #4
0
        public async Task When_Creating_Post_Success()
        {
            // arrange
            var client = Fixture.Server.CreateClient();

            client.CreateSession();
            var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/PostApi/Real_Responses/Happy/200_Post.txt");

            client.AppendHttpCallStub(HttpMethod.Post, new System.Uri(Url), response);

            var request = new Post
            {
                Body   = "testPost",
                Title  = "testTitle",
                UserId = 12345678
            };

            // act
            var post = await client.PostAsync("/api/post",
                                              new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"));

            // asserts
            post.StatusCode.Should().Be(HttpStatusCode.Created);
            client.GetSessionLogs().Should().BeEmpty();

            var outgoingRequests = client.GetSessionOutgoingRequests();

            outgoingRequests.Should().HaveCount(1);
            outgoingRequests[0].GetEndpoint().Should().Be("POST https://jsonplaceholder.typicode.com/post");

            var requestBody = await outgoingRequests[0].ReadJsonBody <Post>();

            requestBody.Should().BeEquivalentTo(request);
        }
예제 #5
0
        public async Task Read200File()
        {
            var sut = ResponseFactory.FromFiddlerLikeResponseFile(FilesFolder + @"happy/200_MovieNotFound.txt");

            sut.StatusCode.Should().Be(HttpStatusCode.OK);

            sut.Content.Headers.ShouldContainHeader("Content-Type", "application/json; charset=utf-8");
            sut.Content.Headers.ShouldContainHeader("Expires", "Thu, 28 Feb 2019 10:42:23 GMT");
            sut.Content.Headers.ShouldContainHeader("Last-Modified", "Wed, 27 Feb 2019 10:42:23 GMT");

            sut.Headers.ShouldContainHeader("Date", "Wed, 27 Feb 2019 10:42:23 GMT");
            sut.Headers.ShouldContainHeader("Set-Cookie", "__cfduid=d336dbbaa07967596b7d935784ddf84471551264143; expires=Thu, 27-Feb-20 10:42:23 GMT; path=/; domain=.omdbapi.com; HttpOnly");
            sut.Headers.ShouldContainHeader("Cache-Control", "public, max-age=86400");
            sut.Headers.ShouldContainHeader("Vary", "*");
            sut.Headers.ShouldContainHeader("X-AspNet-Version", "4.0.30319");
            sut.Headers.ShouldContainHeader("X-Powered-By", "ASP.NET");
            sut.Headers.ShouldContainHeader("Access-Control-Allow-Origin", "*");
            sut.Headers.ShouldContainHeader("CF-Cache-Status", "MISS");
            sut.Headers.ShouldContainHeader("CF-RAY", "4afa0b5f1b756563-SYD");
            sut.Headers.ShouldContainHeader("Server", "cloudflare");

            var body = await sut.GetResponseString();

            body.Should().Be(@"{
  ""Response"": ""False"",
  ""Error"": ""Movie not found!""
}");
        }
예제 #6
0
        public void NoBody()
        {
            var fullFileName = FilesFolder + @"unhappy/401_NoBody.txt";

            var response = ResponseFactory.FromFiddlerLikeResponseFile(fullFileName);

            response.StatusCode.Should().Be(System.Net.HttpStatusCode.Unauthorized);
        }
예제 #7
0
        private void SetupGlobalStubs()
        {
            // this will be the default response, returned if it can't find a match for the session
            var response          = ResponseFactory.FromFiddlerLikeResponseFile($"{StubsFolder}/OmdbApi/Fake_Responses/Happy/200_NoRunTime_NoPlot_YearTooOld.txt");
            var comeAlongMovieUrl = $"{MovieUrl}&t=fantastika";

            UnsessionedData.AppendGlobalHttpCallStub(HttpMethod.Get, new System.Uri(comeAlongMovieUrl), response);
        }
예제 #8
0
        public void NoHttpStatusCode()
        {
            var fullFileName = FilesFolder + @"unhappy/NoHttpStatusCode.txt";

            Action act = () => ResponseFactory.FromFiddlerLikeResponseFile(fullFileName);

            var exception = Assert.Throws <ArgumentException>(act);

            exception.Message.Should().Be($"File is not in the right format, please consult {Constants.Website}");
        }
예제 #9
0
        public void FileIsEmpty()
        {
            var fullFileName = FilesFolder + @"unhappy/Empty.txt";

            Action act = () => ResponseFactory.FromFiddlerLikeResponseFile(fullFileName);

            var exception = Assert.Throws <ArgumentException>(act);

            exception.Message.Should().Be($"Content of {fullFileName} is empty");
        }
예제 #10
0
        public void FileDoesntExist()
        {
            var fullFileName = FilesFolder + "ThisFileDoesntExist.txt";

            Action act = () => ResponseFactory.FromFiddlerLikeResponseFile(fullFileName);

            var exception = Assert.Throws <ArgumentException>(act);

            exception.Message.Should().Be($"Could not find file '{fullFileName}'");
        }
예제 #11
0
        public void InvalidHttpStatus()
        {
            var fullFileName = FilesFolder + @"unhappy/InvalidHttpStatus.txt";

            Action act = () => ResponseFactory.FromFiddlerLikeResponseFile(fullFileName);

            var exception = Assert.Throws <ArgumentException>(act);

            exception.Message.Should().Be($"Not a valid Http Status code: 800");
        }
예제 #12
0
        public async Task Read424File()
        {
            var sut = ResponseFactory.FromFiddlerLikeResponseFile(FilesFolder + @"happy/424_NoHeaders.txt");

            sut.StatusCode.Should().Be(HttpStatusCode.FailedDependency);

            var body = await sut.GetResponseString();

            body.Should().Be(@"{""Content"":""No headers found here""}");
        }
예제 #13
0
        public async Task When_CallingManyWcfMethods_Then_CanPerformMath_Successfully()
        {
            // arrange
            var client = Fixture.Server.CreateClient();

            client.CreateSession();

            var addResponse = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/MathWcf/Real_Responses/Happy/200_Add.txt");

            client.AppendHttpCallStub(HttpMethod.Post, new System.Uri(Url), addResponse, new Dictionary <string, string>()
            {
                { "SOAPAction", @"""http://tempuri.org/Add""" }
            });

            var minusResponse = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/MathWcf/Real_Responses/Happy/200_Minus.txt");

            client.AppendHttpCallStub(HttpMethod.Post, new System.Uri(Url), minusResponse, new Dictionary <string, string>()
            {
                { "SOAPAction", @"""http://tempuri.org/Subtract""" }
            });

            // act
            var httpResponse = await client.GetAsync("/api/math/minus?firstNumber=7");

            // asserts
            await Asserts("Subtract", "4");

            // act
            httpResponse = await client.GetAsync("/api/math/add?firstNumber=7");

            // asserts
            await Asserts("Add", "10");

            async Task Asserts(string soapMethodName, string correctReturnedValue)
            {
                using (new AssertionScope())
                {
                    // assert logs
                    var logs = client.GetSessionLogs();
                    logs.Should().BeEmpty();

                    // assert outgoing
                    var outgoingRequests = client.GetSessionOutgoingRequests();
                    outgoingRequests.Last().GetEndpoint().Should().Be($"POST {Url}"); // only the last one matters, as they accumulate over the 2 requests
                    outgoingRequests.Last().GetHeaderValue("SOAPAction").Should().Be(string.Format(@"""http://tempuri.org/{0}""", soapMethodName));

                    // assert return
                    httpResponse.StatusCode.Should().Be(HttpStatusCode.OK);

                    var returnedValue = await httpResponse.ReadBody();

                    returnedValue.Should().Be(correctReturnedValue);
                }
            }
        }
예제 #14
0
        public void OnlyBody()
        {
            // should throw exception if we try to read with method FromFiddlerLikeResponseFile, but not FromBodyOnlyFile
            var fullFileName = FilesFolder + @"unhappy/OnlyBody.txt";

            Action act = () => ResponseFactory.FromFiddlerLikeResponseFile(fullFileName);

            var exception = Assert.Throws <ArgumentException>(act);

            exception.Message.Should().Be($"File is not in the right format, please consult {Constants.Website}");
        }
예제 #15
0
        public async Task Read401File()
        {
            var sut = ResponseFactory.FromFiddlerLikeResponseFile(FilesFolder + @"happy/401_InvalidKey.txt");

            sut.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
            sut.Headers.ShouldContainHeader("Server", "cloudflare");
            sut.Headers.ShouldContainHeader("CF-RAY", "4afa0cb0efb66563-SYD");

            var body = await sut.GetResponseString();

            body.Should().Be(@"{""Response"":""False"",""Error"":""Invalid API key!""}");
        }
예제 #16
0
        public async Task When_DownstreamSystemReturnsError_Then_LogError_And_ReturnDefaultErrorMessage(HttpStatusCode httpStatus, string fileName, string logContent)
        {
            // arrange
            // errors that are worth retrying
            bool isTransientDownstreamError = (int)httpStatus >= 500 || httpStatus == HttpStatusCode.RequestTimeout;

            var client = Fixture.Server.CreateClient();

            client.CreateSession();
            var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/OmdbApi/{fileName}");

            // we add it twice to account for the recall attempt
            client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(MatrixMovieUrl), response);
            if (isTransientDownstreamError)
            {
                client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(MatrixMovieUrl), response);
            }

            // act
            var httpResponse = await client.GetAsync("/api/movie/matrix");

            using (new AssertionScope())
            {
                // assert logs
                var logs = client.GetSessionLogs();
                logs.Count.Should().Be(1);
                // we check that we log downstream errors specifically with extra details so we can easily debug, the format should be
                // Critical: URL returned invalid response: http status=XXX and body [FULL RESPONSE BODY HERE]
                logs[0].ToString().Should().StartWith($"Critical: GET {MatrixMovieUrl} had exception");
                logs[0].Message.Should().Contain(logContent);
                logs[0].Message.Should().Contain($" response HttpStatus={(int)httpStatus} and body=[");

                // assert outgoing
                var outgoingRequests = client.GetSessionOutgoingRequests();
                outgoingRequests.Count.Should().Be(isTransientDownstreamError ? 2 : 1); // 2 calls because we configured a retry
                AssertMatrixEndpointCalled(outgoingRequests[0]);

                if (isTransientDownstreamError)
                {
                    AssertMatrixEndpointCalled(outgoingRequests[1]);
                }

                // assert return
                httpResponse.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
                var message = await httpResponse.ReadBody();

                message.Should().Be(Constants.DownstreamErrorMessage);
            }
        }
예제 #17
0
        public async Task When_UserAsksForUserList_Then_ReturnListProperly()
        {
            // arrange
            var client = Fixture.Server.CreateClient();

            client.CreateSession();
            var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/UserApi/Real_Responses/Happy/200_ListUsers.txt");

            response.ModifyJsonBody <MovieProject.Logic.Proxy.DTO.User[]>(dto =>
            {
                dto[0].Name = "Changed in code";
            });

            client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(Url), response);

            // act
            var httpResponse = await client.GetAsync("/api/users");

            using (new AssertionScope())
            {
                // assert logs
                var logs = client.GetSessionLogs();
                logs.Should().BeEmpty();

                // assert outgoing
                var outgoingRequests = client.GetSessionOutgoingRequests();
                outgoingRequests.Count.Should().Be(1);
                outgoingRequests[0].GetEndpoint().Should().Be($"GET {Url}");
                outgoingRequests[0].GetHeaderValue("Referer").Should().Be(MovieProject.Logic.Constants.Website);

                // assert return
                httpResponse.StatusCode.Should().Be(HttpStatusCode.OK);

                var list = await httpResponse.ReadJsonBody <List <string> >();

                list.Count.Should().Be(10);
                list[0].Should().Be("Changed in code");
                list[1].Should().Be("Chelsey Dietrich");
                list[2].Should().Be("Clementina DuBuque");
                list[3].Should().Be("Clementine Bauch");
                list[4].Should().Be("Ervin Howell");
                list[5].Should().Be("Glenna Reichert");
                list[6].Should().Be("Kurtis Weissnat");
                list[7].Should().Be("Mrs. Dennis Schulist");
                list[8].Should().Be("Nicholas Runolfsdottir V");
                list[9].Should().Be("Patricia Lebsack");
            }
        }
예제 #18
0
        public async Task Read401File_WithoutBody()
        {
            var sut = ResponseFactory.FromFiddlerLikeResponseFile(FilesFolder + @"happy/401_Unauthorized_WithoutBody.txt");

            sut.StatusCode.Should().Be(HttpStatusCode.Unauthorized);

            sut.Content.Headers.ShouldContainHeader("Expires", "Sat, 02 Mar 2019 02:53:56 GMT");

            sut.Headers.ShouldContainHeader("Server", "Kestrel");
            sut.Headers.ShouldContainHeader("Request-Context", "appId=cid-v1:494f2efb-2457-4434-a651-e820e8fa4933");
            sut.Headers.ShouldContainHeader("Strict-Transport-Security", "max-age=2592000");
            sut.Headers.ShouldContainHeader("Request-Id", "|27bb47e5-4fd78bc240436a6d.");
            sut.Headers.ShouldContainHeader("X-Powered-By", "ASP.NET");
            sut.Headers.ShouldContainHeader("Date", "Sat, 02 Mar 2019 02:53:56 GMT");
            sut.Headers.ShouldContainHeader("Connection", "keep-alive");
            sut.Headers.ShouldContainHeader("x-cache-hit", "false");
            sut.Headers.ShouldContainHeader("Cache-Control", "no-store, must-revalidate, no-cache"); // this seems to be resorted for some weird reason, but all the elements are still here :)

            var body = await sut.GetResponseString();

            body.Should().BeNullOrEmpty();
        }
예제 #19
0
        public async Task When_UserAsksForMovieWithSomeInvalidValues_Then_ReturnMovieProperly()
        {
            // arrange
            var client = Fixture.Server.CreateClient();

            client.CreateSession();
            var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/OmdbApi/Fake_Responses/Happy/200_NoRunTime_NoPlot_YearTooOld.txt");
            var url      = $"{MovieUrl}&t=fantastika";

            client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(url), response);

            // act
            var httpResponse = await client.GetAsync("/api/movie/fantastika");

            using (new AssertionScope())
            {
                // assert logs
                var logs = client.GetSessionLogs();
                logs.Should().BeEmpty();

                // assert outgoing
                var outgoingRequests = client.GetSessionOutgoingRequests();
                outgoingRequests.Count.Should().Be(1);
                outgoingRequests[0].GetEndpoint().Should().Be($"GET {url}");
                outgoingRequests[0].GetHeaderValue("Referer").Should().Be(Web.Constants.Referer);

                // assert return
                httpResponse.StatusCode.Should().Be(HttpStatusCode.OK);

                var movie = await httpResponse.ReadJsonBody <Web.DTO.Media>();

                movie.Id.Should().Be("tt1185643");
                movie.Name.Should().Be("Fantastika vs. Wonderwoman");
                movie.Runtime.Should().Be("Unknown");
                movie.Year.Should().Be("Unknown");
                movie.Plot.Should().Be("");
            }
        }
        public async Task When_UserAsksForMovieWithMostFields_Then_ReturnMovieProperly()
        {
            // arrange
            var client = Fixture.Server.CreateClient();

            client.CreateSession();
            var response       = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/OmdbApi/Real_Responses/Happy/200_ContainsMostFields_TheMatrix.txt");
            var matrixMovieUrl = $"{MovieUrl}&t=matrix";

            client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(matrixMovieUrl), response);

            // act
            var httpResponse = await client.GetAsync("/api/movie/matrix");

            // assert logs
            var logs = client.GetSessionLogs();

            logs.ShouldBeEmpty();

            // assert outgoing
            var outgoingRequests = client.GetSessionOutgoingRequests();

            outgoingRequests.Count.ShouldBe(1);
            outgoingRequests[0].GetEndpoint().ShouldBe($"GET {matrixMovieUrl}");
            outgoingRequests[0].GetHeaderValue("Referer").ShouldBe(MovieProject.Logic.Constants.Website);

            // assert return
            httpResponse.StatusCode.ShouldBe(HttpStatusCode.OK);

            var movie = await httpResponse.ReadJsonBody <MovieProject.Logic.DTO.Media>();

            movie.Id.ShouldBe("tt0133093");
            movie.Name.ShouldBe("The Matrix");
            movie.Year.ShouldBe("1999");
            movie.Runtime.ShouldBe("2.3h");
            movie.Plot.ShouldBe("A computer hacker learns from mysterious rebels about the true nature of his reality and his role in the war against its controllers.");
            movie.Language.ShouldBe(MovieProject.Logic.DTO.Media.Languages.English);
        }
예제 #21
0
        public async Task When_UserAsksForMoviFewFields_Then_ReturnMovieProperly()
        {
            // arrange
            var client = Fixture.Server.CreateClient();

            client.CreateSession();
            var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/OmdbApi/Real_Responses/Happy/200_FewFields_OldMovie.txt");
            var url      = $"{MovieUrl}&t=come along, do";

            client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(url), response);

            // act
            var httpResponse = await client.GetAsync("/api/movie/come along, do");

            using (new AssertionScope())
            {
                // assert logs
                var logs = client.GetSessionLogs();
                logs.Should().BeEmpty();

                // assert outgoing
                var outgoingRequests = client.GetSessionOutgoingRequests();
                outgoingRequests.Count.Should().Be(1);
                outgoingRequests[0].GetEndpoint().Should().Be($"GET {url}");
                outgoingRequests[0].GetHeaderValue("Referer").Should().Be(Web.Constants.Referer);

                // assert return
                httpResponse.StatusCode.Should().Be(HttpStatusCode.OK);

                var movie = await httpResponse.ReadJsonBody <Web.DTO.Media>();

                movie.Id.Should().Be("tt0000182");
                movie.Name.Should().Be("Come Along, Do!");
                movie.Year.Should().Be("1898");
                movie.Runtime.Should().Be("1 min");
                movie.Plot.Should().Be("A couple look at a statue while eating in an art gallery.");
            }
        }
예제 #22
0
        public async Task Read200WithComments()
        {
            var sut = ResponseFactory.FromFiddlerLikeResponseFile(FilesFolder + @"happy/200_WithComments.txt");

            sut.StatusCode.Should().Be(HttpStatusCode.OK);

            var body = await sut.GetResponseString();

            body.Should().Be(@"{""Content"":""testing comments""}");

            sut.Content.Headers.ShouldContainHeader("Content-Type", "application/json; charset=utf-8");
            sut.Content.Headers.ShouldContainHeader("Expires", "Mon, 04 Mar 2019 07:17:49 GMT");
            sut.Content.Headers.ShouldContainHeader("Last-Modified", "Sun, 03 Mar 2019 07:17:48 GMT");

            sut.Headers.ShouldContainHeader("Date", "Sun, 03 Mar 2019 07:17:49 GMT");
            sut.Headers.ShouldContainHeader("Cache-Control", "public, max-age=86400");
            sut.Headers.ShouldContainHeader("Vary", "*");
            sut.Headers.ShouldContainHeader("X-AspNet-Version", "4.0.30319");
            sut.Headers.ShouldContainHeader("X-Powered-By", "ASP.NET");
            sut.Headers.ShouldContainHeader("Access-Control-Allow-Origin", "*");
            sut.Headers.ShouldContainHeader("CF-Cache-Status", "MISS");
            sut.Headers.ShouldContainHeader("CF-RAY", "4b19d532ce5419aa-SYD");
            sut.Headers.ShouldContainHeader("Server", "cloudflare");
        }
예제 #23
0
        public static IServiceCollection AddInterceptionAndStubs(this IServiceCollection serviceCollection, ILogger logger)
        {
            // the bellow call won't be used if IWebHostBuilder.InterceptHttpCallsBeforeSending() is used (which happens in test projects)
            return(serviceCollection.InterceptHttpCallsAfterSending(async(intercept) =>
            {
                bool IsHappyPath = false;

                // we check content length because downstream returns a 200 for not found, we can tell by the size it's likely a bad response
                if (intercept.Response?.IsSuccessStatusCode ?? false && intercept.Response.Content.Headers.ContentLength > 100)
                {
                    IsHappyPath = true;
                    if (intercept.Request.RequestUri.ToString().Contains("omdb"))
                    {
                        var movieName = intercept.Request.GetQueryValue("t");
                        await intercept.SaveAsRecording("omdb/new/happy", movieName.Replace(" ", "_"), 1);
                    }
                    else
                    {
                        var action = intercept.Request.GetSoapAction();
                        action = action.Split("/").LastOrDefault();
                        await intercept.SaveAsRecording("math/new/happy", action, howManyFilesToKeep: 50);
                    }
                }

                var returnStubInstruction = intercept.HttpContextAccessor.GetRequestHeaderValue("SystemTestingTools_ReturnStub");
                if (!string.IsNullOrEmpty(returnStubInstruction)) // someone is testing edge cases, we return the requested stub
                {
                    var stub = ResponseFactory.FromFiddlerLikeResponseFile(intercept.RootStubsFolder.AppendPath(returnStubInstruction));
                    return intercept.ReturnStub(stub, "instructions from header");
                }

                if (IsHappyPath)
                {
                    return intercept.KeepResultUnchanged();
                }

                await intercept.SaveAsRecording("new/unhappy");

                var message = intercept.Summarize();

                logger.LogError(intercept.Exception, message);

                // get the most recent recording (stub), so we can be sure to be testing against the latest if possible
                var recentRecording = RecordingCollection.Recordings.FirstOrDefault(
                    recording => recording.File.Contains(@"new/happy") &&
                    recording.Request.RequestUri.PathAndQuery == intercept.Request.RequestUri.PathAndQuery &&
                    recording.Request.GetSoapAction() == intercept.Request.GetSoapAction());

                if (recentRecording != null)
                {
                    return intercept.ReturnRecording(recentRecording, message);
                }

                // fall back #1, return a recording from the pre-approved folder, stored in github and vouched by a developer; might not be the latest
                // but returns a good response to unblock developers
                var oldRecording = RecordingCollection.Recordings.FirstOrDefault(
                    recording => recording.File.Contains(@"pre-approved/happy") &&
                    recording.Request.RequestUri.PathAndQuery == intercept.Request.RequestUri.PathAndQuery &&
                    recording.Request.GetSoapAction() == intercept.Request.GetSoapAction());

                if (oldRecording != null)
                {
                    return intercept.ReturnRecording(oldRecording, message);
                }

                // fall back #2, we return a dummy response
                var fallBackRecording = RecordingCollection.Recordings.FirstOrDefault(
                    recording => recording.File.Contains("last_fallback"));

                if (fallBackRecording != null)
                {
                    return intercept.ReturnRecording(fallBackRecording, message + " and could not find better match");
                }

                return intercept.KeepResultUnchanged();
            }));
        }
예제 #24
0
        public async Task When_FilesAlreadyExistInFolder_And_ValidRequestResponse_Then_CreateTextFileInRightFormat_And_CanLoadFile()
        {
            // this test relies on usage of the FileSystem, it's unusual, but considered best balance
            // of efficient + realist testing vs potential downsides, using the temporary folder of the machine
            // should be ok

            // arrange
            if (Directory.Exists(folder))
            {
                Directory.Delete(folder, true);                          // if folder exists, it was the result of previous tests
            }
            // create directory and some files in it, so we make sure our code creates the next file correctly
            Directory.CreateDirectory(folder);
            File.CreateText(Path.Combine(folder, "1_OK.txt"));
            File.CreateText(Path.Combine(folder, "2_Forbidden.txt"));
            File.CreateText(Path.Combine(folder, "28_Whatever.txt"));
            File.CreateText(Path.Combine(folder, "some_random_file.txt"));

            var input = new RequestResponse()
            {
                Metadata = new RequestResponse.MetadataInfo()
                {
                    DateTime           = System.DateTime.Parse("2019-03-17 15:44:37.597"),
                    Timezone           = "my_time_zone",
                    LocalMachine       = "Machine001",
                    User               = "******",
                    RecordedFrom       = @"MovieProject.Web 1.2.0.1 (htts://localhost/api/whatever?param=2)",
                    ToolUrl            = "http://www.whatever.com",
                    ToolNameAndVersion = "SystemTestingTools 0.1.0.0"
                },
                Request = new RequestResponse.RequestInfo()
                {
                    Method  = HttpMethod.Post,
                    Url     = "https://www.whatever.com/someendpoint",
                    Body    = @"{""user"":""Alan"", ""trickyField"":""--!?@Divider:"", ""trickyField2"":""HTTP/1.1 200 OK""}",
                    Headers = new Dictionary <string, string>()
                    {
                        { "User-Agent", "MyApp" }
                    }
                },
                Response = new RequestResponse.ResponseInfo()
                {
                    Status      = HttpStatusCode.OK,
                    Body        = @"{""value"":""whatever"", ""trickyField"":""--!?@Divider:"", ""trickyField2"":""HTTP/1.1 200 OK""}",
                    HttpVersion = new System.Version(1, 1),
                    Headers     = new Dictionary <string, string>()
                    {
                        { "Server", "Kestrel" }
                    }
                }
            };

            var sut = new FileWriter(folder);

            // act
            var fileName = sut.Write(input);

            // asserts
            fileName.ShouldBe("29_OK");
            var createdFile = Path.Combine(folder, "29_OK.txt");

            File.Exists(createdFile).ShouldBeTrue();

            var content = File.ReadAllText(createdFile);

            content.ShouldBe(expectedFileContent);

            var deserializedResponse = ResponseFactory.FromFiddlerLikeResponseFile(createdFile);

            deserializedResponse.StatusCode.ShouldBe(input.Response.Status);
            (await deserializedResponse.Content.ReadAsStringAsync()).ShouldBe(input.Response.Body);

            foreach (var item in input.Response.Headers)
            {
                deserializedResponse.Headers.ShouldContainHeader(item.Key, item.Value);
            }
        }