Пример #1
0
        public async Task When_Json_Body_Then_Populates(Labelled <Action <BlueprintHttpBuilder> > configureHttp)
        {
            // Arrange
            var expected = new JsonOperation
            {
                IntegerProperty  = 761,
                EnumProperty     = OperationEnum.EnumOne,
                GuidProperty     = Guid.NewGuid(),
                StringArray      = new[] { "arr1", "arr2" },
                StringEnumerable = new[] { "arr3", "arr4" },
                StringList       = new List <string> {
                    "arr5", "arr6"
                },
                StringProperty          = "a string",
                NullableIntegerProperty = null,
                StringIntegerDictionary = new Dictionary <string, int> {
                    { "one", 1 }, { "twice", 12 }
                }
            };

            var handler  = new TestApiOperationHandler <JsonOperation>(null);
            var executor = TestApiOperationExecutor.CreateHttp(
                o => o.WithHandler(handler),
                configureHttp: configureHttp);
            var context = GetContext(executor, expected);

            // Act
            await executor.ExecuteAsync(context);

            // Assert
            handler.OperationPassed.Should().BeEquivalentTo(expected);
        }
        public async Task When_multiple_child_operations_finds_correct_one()
        {
            // Arrange
            var baseHandler   = new TestApiOperationHandler <OperationBase>("ignored");
            var child1Handler = new TestApiOperationHandler <OperationChild1>("ignored");
            var child2Handler = new TestApiOperationHandler <OperationChild2>("ignored");

            var executor = TestApiOperationExecutor
                           .CreateStandalone(o => o
                                             .WithHandler(baseHandler)
                                             .WithHandler(child1Handler)
                                             .WithHandler(child2Handler)
                                             .WithOperation <OperationBase>(c => c.RequiresReturnValue   = false)
                                             .WithOperation <OperationChild1>(c => c.RequiresReturnValue = false)
                                             .WithOperation <OperationChild2>(c => c.RequiresReturnValue = false));

            // Act
            var result = await executor.ExecuteWithNewScopeAsync(new OperationChild2());

            // Assert
            result.Should().BeOfType <NoResultOperationResult>();

            baseHandler.WasCalled.Should().BeTrue();
            child2Handler.WasCalled.Should().BeTrue();

            child1Handler.WasCalled.Should().BeFalse();
        }
        public async Task When_Executed_Then_Activity_Tags_Set()
        {
            // Arrange
            var expected = new SupportedTypesOperation
            {
                IntegerProperty         = 761,
                EnumProperty            = OperationEnum.EnumOne,
                GuidProperty            = Guid.NewGuid(),
                StringProperty          = "a string",
                NullableIntegerProperty = null,
            };

            var handler  = new TestApiOperationHandler <SupportedTypesOperation>(null);
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            using var activity = Activity.Current = new Activity("PopulationTest").Start();

            // Act
            await executor.ExecuteAsync(expected);

            // Assert
            var tags = activity.TagObjects.ToDictionary(k => k.Key, v => v.Value);

            tags.Should().Contain($"{nameof(SupportedTypesOperation)}.{nameof(expected.IntegerProperty)}", expected.IntegerProperty);
            tags.Should().Contain($"{nameof(SupportedTypesOperation)}.{nameof(expected.EnumProperty)}", expected.EnumProperty);
            tags.Should().Contain($"{nameof(SupportedTypesOperation)}.{nameof(expected.GuidProperty)}", expected.GuidProperty);
            tags.Should().Contain($"{nameof(SupportedTypesOperation)}.{nameof(expected.StringProperty)}", expected.StringProperty);

            // Nulls are ignored / removed
            tags.Should().NotContain($"{nameof(SupportedTypesOperation)}.{nameof(expected.NullableIntegerProperty)}", expected.NullableIntegerProperty);
        }
        public async Task When_Executed_Then_Sensitive_Properties_Excluded_From_Activity_Tags()
        {
            // Arrange
            var expected = new SensitiveOperation
            {
                NotSensitiveProperty = "NotSensitiveProperty",
                Password             = "******",
                PasswordOne          = "PasswordOne",
                ASensitiveProperty   = "ASensitiveProperty",
                ADoNotAuditProperty  = "ADoNotAuditProperty",
            };

            var handler  = new TestApiOperationHandler <SensitiveOperation>(null);
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            using var activity = Activity.Current = new Activity("PopulationTest").Start();

            // Act
            await executor.ExecuteAsync(expected);

            // Assert
            var tags = activity.TagObjects.ToDictionary(k => k.Key, v => v.Value);

            tags.Should().HaveCount(1);
            tags.Should().Contain($"{nameof(SensitiveOperation)}.{nameof(expected.NotSensitiveProperty)}", expected.NotSensitiveProperty);
        }
        public void When_Single_Custom_Exception_Handler_Registered_Then_Compiles()
        {
            // Arrange
            var handler = new TestApiOperationHandler <TestApiCommand>(12345);

            // Act
            var middleware = new ExceptionHandlingRegisteringMiddleware(
                typeof(NotFoundException), (e) =>
            {
                return(new Frame[]
                {
                    LogFrame.Critical("Exception happened, oops"),
                    new ReturnFrame(new Variable(typeof(object), "null")),
                });
            });

            var executor = TestApiOperationExecutor.CreateStandalone(o => o
                                                                     .WithHandler(handler)
                                                                     .Pipeline(p => p.AddMiddleware(middleware, MiddlewareStage.Execution)));

            // Assert
            var code = executor.WhatCodeDidIGenerateFor <TestApiCommand>();

            code.Should().Contain("catch (Blueprint.Errors.NotFoundException");
            code.Should().Contain("Exception happened, oops");
        }
Пример #6
0
        public void When_Array_Like_Then_Exception()
        {
            // Arrange
            var handler = new TestApiOperationHandler <InvalidArrayCookieOperation>(null);

            // Act
            Action executor = () => TestApiOperationExecutor.CreateHttp(o => o.WithHandler(handler));

            // Assert
            executor.Should().ThrowExactly <InvalidOperationException>()
            .WithMessage("Cannot create decoder for property InvalidArrayCookieOperation.Lengths as it is array-like and FromCookie does not support multiple values.");
        }
        public async Task When_Unhandled_Exception_Activity_Status_Set_To_Error(Type exceptionType)
        {
            // Arrange
            var handler  = new TestApiOperationHandler <TestOperation>((Exception)Activator.CreateInstance(exceptionType));
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            using var activity = Activity.Current = new Activity("ExceptionTest").Start();

            // Act
            await executor.ExecuteWithNoUnwrapAsync(new TestOperation());

            // Assert
            activity.GetStatus().StatusCode.Should().Be(StatusCode.Error);
        }
Пример #8
0
        private static async Task AssertPopulatedFromQueryString <TOperation>(TOperation expected, string queryString = null)
        {
            // Arrange
            var handler  = new TestApiOperationHandler <TOperation>(null);
            var executor = TestApiOperationExecutor.CreateHttp(o => o.WithHandler(handler));

            var context = GetContext <TOperation>(executor, queryString);

            // Act
            await executor.ExecuteAsync(context);

            // Assert
            handler.OperationPassed.Should().BeEquivalentTo(expected);
        }
        public async Task When_Blueprint_ApiException_Activity_Status_Set_Based_On_Http_Status_Code(int status, StatusCode expected)
        {
            // Arrange
            var handler  = new TestApiOperationHandler <TestOperation>(new ApiException("Failed", "test_failure", "Test failure for " + status, status));
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            using var activity = Activity.Current = new Activity("ExceptionTest").Start();

            // Act
            await executor.ExecuteWithNoUnwrapAsync(new TestOperation());

            // Assert
            activity.GetStatus().StatusCode.Should().Be(expected);
        }
        public async Task When_Operation_Does_Not_Pass_Validation_Then_Handler_Not_Executed()
        {
            // Arrange
            var handler  = new TestApiOperationHandler <HasRequiredPropertyOperation>(12345);
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            // Act
            await executor.ExecuteWithNewScopeAsync(new HasRequiredPropertyOperation
            {
                TheProperty = null
            });

            // Assert
            handler.WasCalled.Should().BeFalse();
        }
Пример #11
0
        private static async Task AssertPopulatedFromRoute <TOperation, TPropertyType>(TOperation expected, string routeDataKeyOverride = null) where TOperation : RouteableOperation <TPropertyType>
        {
            // Arrange
            var handler  = new TestApiOperationHandler <TOperation>(null);
            var executor = TestApiOperationExecutor.CreateHttp(o => o.WithHandler(handler));

            // These tests are checking whether a conversion happens, so we will always put the route data as the ToString() value of the object
            var context = GetContext <TOperation>(executor, routeDataKeyOverride ?? nameof(expected.RouteProperty), expected.RouteProperty.ToString());

            // Act
            await executor.ExecuteAsync(context);

            // Assert
            handler.OperationPassed.Should().BeEquivalentTo(expected);
        }
Пример #12
0
        public void When_Scoped_Then_GetRequiredService_At_Runtime()
        {
            // Arrange
            var handler = new TestApiOperationHandler <OperationWithInjectable>(12345);

            // Act
            var executor = TestApiOperationExecutor.CreateStandalone(o => o
                                                                     .WithHandler(handler)
                                                                     .Pipeline(p => p.AddMiddlewareBefore <MiddlewareWithDependencyInjectionVariable <IInjectable> >(MiddlewareStage.Execution)),
                                                                     s => s.AddScoped(typeof(IInjectable), typeof(Injectable)));

            // Assert
            var code = executor.WhatCodeDidIGenerateFor <OperationWithInjectable>();

            code.Should().Contain("context.ServiceProvider.GetRequiredService<Blueprint.Tests.IoC.Given_DependencyInjection_Container.IInjectable>();");
        }
Пример #13
0
        public async Task When_Unhandled_Exception_Activity_Exception_Recorded(Type exceptionType)
        {
            // Arrange
            var handler  = new TestApiOperationHandler <TestOperation>((Exception)Activator.CreateInstance(exceptionType));
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            using var activity = Activity.Current = new Activity("ExceptionTest").Start();

            // Act
            await executor.ExecuteWithNoUnwrapAsync(new TestOperation());

            // Assert
            activity.Events.Should().Contain(e =>
                                             e.Name == "exception" &&
                                             e.Tags.Any(t => t.Key == "exception.type"));
        }
Пример #14
0
        public async Task When_DataAnnotations_ValidationException_Thrown_Then_Activity_Status_OK()
        {
            // Arrange
            var expected = new TestOperation();

            var handler  = new TestApiOperationHandler <TestOperation>(new System.ComponentModel.DataAnnotations.ValidationException("Validation failed"));
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            using var activity = Activity.Current = new Activity("ExceptionTest").Start();

            // Act
            await executor.ExecuteWithNoUnwrapAsync(expected);

            // Assert
            activity.GetStatus().StatusCode.Should().Be(StatusCode.Ok);
        }
        private static async Task AssertHeaders <TOperation>(
            TOperation expected,
            Dictionary <string, string> headers = null)
        {
            // Arrange
            var handler  = new TestApiOperationHandler <TOperation>(null);
            var executor = TestApiOperationExecutor.CreateHttp(o => o.WithHandler(handler));

            var context = GetContext <TOperation>(executor, headers);

            // Act
            await executor.ExecuteAsync(context);

            // Assert
            handler.OperationPassed.Should().BeEquivalentTo(expected);
        }
Пример #16
0
        public async Task When_Blueprint_ValidationException_Thrown_Then_No_Exception_Recorded()
        {
            // Arrange
            var expected = new TestOperation();

            var handler  = new TestApiOperationHandler <TestOperation>(new Blueprint.Validation.ValidationException("Validation failed"));
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            using var activity = Activity.Current = new Activity("ExceptionTest").Start();

            // Act
            await executor.ExecuteWithNoUnwrapAsync(expected);

            // Assert
            activity.Events.Should().BeEmpty();
        }
        public async Task When_Empty_Operation_Then_Result_Executed()
        {
            // Arrange
            var toReturn = 12345;

            var handler  = new TestApiOperationHandler <EmptyOperation>(toReturn);
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            // Act
            var result = await executor.ExecuteWithNewScopeAsync(new EmptyOperation());

            // Assert
            var okResult = result.ShouldBeOperationResultType <OkResult>();

            okResult.Content.Should().Be(toReturn);
            handler.WasCalled.Should().BeTrue();
        }
        public async Task When_Handler_Already_Registered_Then_Used()
        {
            // Arrange
            var handler  = new TestApiOperationHandler <TestApiCommand>("1234");
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            // Act
            var result = await executor.ExecuteWithNewScopeAsync(new TestApiCommand
            {
                AStringProperty          = "the string value",
                ASensitiveStringProperty = "the sensitive value"
            });

            // Assert
            var okResult = result.ShouldBeOperationResultType <OkResult>();

            okResult.Content.Should().Be("1234");
        }
Пример #19
0
        public async Task When_Generic_Operation_Type_Can_Compile()
        {
            // Arrange
            var handler  = new TestApiOperationHandler <GenericOperation <TestApiCommand> >("12345");
            var executor = TestApiOperationExecutor.CreateStandalone(o =>
                                                                     o
                                                                     .WithHandler(handler)
                                                                     .AddLogging());

            // Act
            var result = await executor.ExecuteWithNewScopeAsync(new GenericOperation <TestApiCommand>
            {
                Operation = new TestApiCommand(),
            });

            // Assert
            result.Should().NotBeNull();
        }
Пример #20
0
        public async Task When_Middleware_Requests_Variable_Fulfilled_By_Open_Generic_DI_Registration()
        {
            // Arrange
            var toReturn = 12345;

            var handler  = new TestApiOperationHandler <OperationWithInjectable>(toReturn);
            var executor = TestApiOperationExecutor.CreateStandalone(
                o => o
                .WithHandler(handler)
                .Pipeline(p => p.AddMiddlewareBefore <MiddlewareWithDependencyInjectionVariable <IOptions <MyOptions> > >(MiddlewareStage.Execution)),
                s => s.AddOptions <MyOptions>());

            // Act
            await executor.ExecuteWithNewScopeAsync(new OperationWithInjectable());

            // Assert
            handler.OperationPassed.InjectableProperty.Should().NotBeNull();
        }
Пример #21
0
        public async Task When_No_Exception_Then_Status_Unset()
        {
            // Arrange
            var expected = new TestOperation();

            var handler  = new TestApiOperationHandler <TestOperation>(new StatusCodeResult(HttpStatusCode.Accepted));
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            using var activity = Activity.Current = new Activity("ExceptionTest").Start();

            // Act
            await executor.ExecuteAsync(expected);

            // Assert
            // We set to Unset, so that hosts can make decision based on the result. For example ASP.NET will look at the
            // final status code set. We will lean on that to allow returning error-related status codes (i.e. a 500 StatusCodeResult).
            activity.GetStatus().StatusCode.Should().Be(StatusCode.Unset);
        }
        public async Task When_Operation_Does_Not_Pass_Validation_Then_ValidationResult_Returned()
        {
            // Arrange
            var handler  = new TestApiOperationHandler <HasRequiredPropertyOperation>(12345);
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            // Act
            var result = await executor.ExecuteWithNewScopeAsync(new HasRequiredPropertyOperation
            {
                TheProperty       = null,
                TheStringProperty = "too-short"
            });

            // Assert
            var validationResult = result.Should().BeOfType <ValidationFailedOperationResult>().Subject;

            validationResult.Errors.Should().Contain(k => k.Key == nameof(HasRequiredPropertyOperation.TheProperty));
            validationResult.Errors.Should().Contain(k => k.Key == nameof(HasRequiredPropertyOperation.TheStringProperty));
        }
Пример #23
0
        public async Task When_Route_Property_In_Json_Body_But_Not_RouteData_With_Multiple_Routes_Then_Sets(Labelled <Action <BlueprintHttpBuilder> > configureHttp)
        {
            // Arrange
            var expected = new MultipleRouteOperation {
                RouteProperty = "expectedValue"
            };

            var handler  = new TestApiOperationHandler <MultipleRouteOperation>(null);
            var executor = TestApiOperationExecutor.CreateHttp(o => o.WithHandler(handler), configureHttp: configureHttp);
            var context  = GetContext(executor, expected);

            // Act
            var source = executor.WhatCodeDidIGenerate();

            await executor.ExecuteAsync(context);

            // Assert
            handler.OperationPassed.Should().BeEquivalentTo(expected);
            handler.OperationPassed.RouteProperty.Should().Be(expected.RouteProperty);
        }
Пример #24
0
        public async Task When_Malformed_JSON_Then_Throws_ApiException(Labelled <Action <BlueprintHttpBuilder> > configureHttp)
        {
            // Arrange
            var handler  = new TestApiOperationHandler <JsonOperation>(null);
            var executor = TestApiOperationExecutor.CreateHttp(o => o.WithHandler(handler), configureHttp: configureHttp);

            var context = executor.HttpContextFor <JsonOperation>();

            context.GetHttpContext().Request.Body = "{..}".AsUtf8Stream();
            context.GetHttpContext().Request.Headers["Content-Type"] = "application/json";

            // Act
            Func <Task> tryExecute = () => executor.ExecuteAsync(context);

            // Assert
            await tryExecute.Should()
            .ThrowApiExceptionAsync()
            .WithType("invalid_json")
            .WithTitle("The JSON payload is invalid");
        }
Пример #25
0
        public async Task When_Json_Body_And_FromBody_Attribute_Then_Populates(Labelled <Action <BlueprintHttpBuilder> > configureHttp)
        {
            // Arrange
            var expectedBody = new JsonOperation
            {
                IntegerProperty  = 761,
                EnumProperty     = OperationEnum.EnumOne,
                GuidProperty     = Guid.NewGuid(),
                StringArray      = new[] { "arr1", "arr2" },
                StringEnumerable = new[] { "arr3", "arr4" },
                StringList       = new List <string> {
                    "arr5", "arr6"
                },
                StringProperty          = "a string",
                NullableIntegerProperty = null,
                StringIntegerDictionary = new Dictionary <string, int> {
                    { "one", 1 }, { "twice", 12 }
                }
            };

            var handler  = new TestApiOperationHandler <JsonOperationUsingFromBody>(null);
            var executor = TestApiOperationExecutor.CreateHttp(
                o => o.WithHandler(handler),
                configureHttp: configureHttp);

            var jsonBody = JsonConvert.SerializeObject(expectedBody);

            var context = executor.HttpContextFor <JsonOperationUsingFromBody>();

            context.GetHttpContext().Request.Body = jsonBody.AsUtf8Stream();
            context.GetHttpContext().Request.Headers["Content-Type"] = "application/json";

            // Act
            await executor.ExecuteAsync(context);

            // Assert
            handler.OperationPassed.Should().BeEquivalentTo(new JsonOperationUsingFromBody
            {
                JsonOperation = expectedBody,
            });
        }
        public async Task When_Executed_Then_Unsupported_Types_Not_Set()
        {
            // Arrange
            var expected = new NonSupportedTypeOperation
            {
                ListProperty         = new List <string>(),
                ChildComplexProperty = new SensitiveOperation(),
                StringArray          = Array.Empty <string>(),
            };

            var handler  = new TestApiOperationHandler <NonSupportedTypeOperation>(null);
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            using var activity = Activity.Current = new Activity("PopulationTest").Start();

            // Act
            await executor.ExecuteAsync(expected);

            // Assert
            activity.TagObjects.Should().BeEmpty();
        }
        public async Task When_Operation_Passes_Validation_Then_Result_Executed()
        {
            // Arrange
            var toReturn = 12345;

            var handler  = new TestApiOperationHandler <HasRequiredPropertyOperation>(toReturn);
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithHandler(handler));

            // Act
            var result = await executor.ExecuteWithNewScopeAsync(new HasRequiredPropertyOperation
            {
                TheProperty       = "something not null",
                TheStringProperty = "a string that is long enough",
            });

            // Assert
            var okResult = result.ShouldBeOperationResultType <OkResult>();

            okResult.Content.Should().Be(toReturn);
            handler.WasCalled.Should().BeTrue();
        }
Пример #28
0
        public async Task When_Middleware_Requests_Multiple_Generic_Classes_With_Different_Type_Parameters_Then_Success()
        {
            // Arrange
            var toReturn = 12345;

            var handler  = new TestApiOperationHandler <OperationWithInjectable>(toReturn);
            var executor = TestApiOperationExecutor.CreateStandalone(o => o
                                                                     .WithHandler(handler)
                                                                     .Pipeline(p => p.AddMiddlewareBefore <MiddlewareWithMultipleDependencyInjectionVariable <IOptions <MyOptions>, IOptions <MyOtherOptions> > >(MiddlewareStage.Execution)),
                                                                     s =>
            {
                s.AddOptions <MyOptions>();
                s.AddOptions <MyOtherOptions>();
            });

            // Act
            await executor.ExecuteWithNewScopeAsync(new OperationWithInjectable());

            // Assert
            handler.OperationPassed.InjectableProperty.Should().NotBeNull();
        }
Пример #29
0
        public async Task When_Route_Property_In_Json_Body_Then_Route_Value_Wins(Labelled <Action <BlueprintHttpBuilder> > configureHttp)
        {
            // Arrange
            var expected = new JsonWithRouteOperation
            {
                RouteProperty    = "unexpectedRouteValue",
                IntegerProperty  = 761,
                EnumProperty     = OperationEnum.EnumOne,
                GuidProperty     = Guid.NewGuid(),
                StringArray      = new[] { "arr1", "arr2" },
                StringEnumerable = new[] { "arr3", "arr4" },
                StringList       = new List <string> {
                    "arr5", "arr6"
                },
                StringProperty          = "a string",
                NullableIntegerProperty = null,
                StringIntegerDictionary = new Dictionary <string, int> {
                    { "one", 1 }, { "twice", 12 }
                }
            };

            var expectedRouteValue = "theRouteValue";

            var handler  = new TestApiOperationHandler <JsonWithRouteOperation>(null);
            var executor = TestApiOperationExecutor.CreateHttp(o => o.WithHandler(handler), configureHttp: configureHttp);
            var context  = GetContext(executor, expected);

            context.GetRouteData().Values[nameof(JsonWithRouteOperation.RouteProperty)] = expectedRouteValue;

            // Act
            await executor.ExecuteAsync(context);

            // Assert
            handler.OperationPassed.Should().BeEquivalentTo(
                expected,
                o => o.Excluding(x => x.RouteProperty));

            handler.OperationPassed.RouteProperty.Should().Be(expectedRouteValue);
        }
        public async Task When_multiple_child_operations_does_not_cast_or_wrap_in_if_when_handling_parent()
        {
            // Arrange
            var baseHandler   = new TestApiOperationHandler <OperationBase>("ignored");
            var child1Handler = new TestApiOperationHandler <OperationChild1>("ignored");
            var child2Handler = new TestApiOperationHandler <OperationChild2>("ignored");

            var executor = TestApiOperationExecutor
                           .CreateStandalone(o => o
                                             .WithHandler(baseHandler)
                                             .WithHandler(child1Handler)
                                             .WithHandler(child2Handler)
                                             .WithOperation <OperationBase>(c => c.RequiresReturnValue   = false)
                                             .WithOperation <OperationChild1>(c => c.RequiresReturnValue = false)
                                             .WithOperation <OperationChild2>(c => c.RequiresReturnValue = false));

            // Act
            var result = executor.WhatCodeDidIGenerateFor <OperationChild2>();

            // Assert
            await Verifier.Verify(result);
        }