public async Task When_ResourceDeleted_ApiResource_Returned_With_Then_Href_Populated()
        {
            // Arrange
            using var t = SystemTime.PauseForThread();

            var executor = TestApiOperationExecutor
                           .CreateStandalone(o => o
                                             .Http()
                                             .AddHateoasLinks()
                                             .AddResourceEvents <NullResourceEventRepository>()
                                             .WithOperation <ResourceSelfOperation>()
                                             .WithOperation <ResourceDeletionOperation>()
                                             .WithOperation <ResourceLinkWithoutIdOperation>()
                                             .WithOperation <ResourceLinkWithIdOperation>()
                                             );

            // Act
            var result = await executor.ExecuteAsync(new ResourceDeletionOperation
            {
                Id = "87457",
            });

            // Assert
            var okResult = result.ShouldBeOperationResultType <OkResult>();
            var @event   = okResult.Content.Should().BeOfType <ResourceDeleted <ResourceToReturn> >().Subject;

            @event.Created.UtcDateTime.Should().BeCloseTo(t.UtcNow);
            @event.Object.Should().Be("event");
            @event.EventId.Should().Be("toReturn.deleted");
            @event.ChangeType.Should().Be(ResourceEventChangeType.Deleted);
            @event.ResourceObject.Should().Be("toReturn");
            @event.Href.Should().Be("https://api.blueprint-testing.com/api/resources/87457");
        }
        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");
        }
        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_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_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);
        }
示例#6
0
        public async Task When_Dependency_From_Operation_Context_And_IoC_Then_Injects()
        {
            // Arrange
            var operation = new InlineHandle();
            var executor  = TestApiOperationExecutor.CreateStandalone(
                o => o
                .WithOperation <InlineHandle>()
                .AddAuthentication(a => a.UseContextLoader <AnonymousUserAuthorisationContextFactory>())
                .AddAuthorisation(),
                s =>
            {
                s.AddSingleton <IClaimsIdentityProvider, NullClaimsIdentityProvider>();
                s.AddTransient <IDependency, Dependency>();
            });

            // Act
            await executor.ExecuteWithNewScopeAsync(operation);

            // Assert
            operation.Context.Should().NotBeNull();
            operation.Context.Operation.Should().Be(operation);

            operation.Dependency.Should().NotBeNull();
            operation.User.Should().NotBeNull();
        }
        public void When_Return_Is_Not_Compatible_With_Declared_Then_Exception_Thrown()
        {
            // Arrange
            Action create = () => TestApiOperationExecutor.CreateStandalone(o => o
                                                                            .WithOperation <WrongDeclaredReturnType>());

            // Assert
            create.Should().ThrowExactly <InvalidReturnTypeException>();
        }
        private async Task ShouldCallInlineMethod <T>(Action <object> assertContent) where T : new()
        {
            // Arrange
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithOperation <T>());

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

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

            assertContent(okResult.Content);
        }
        public async Task When_Specific_Handler_Exists_Then_Finds_In_Scan()
        {
            // Arrange
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithOperation <ScanOperation>());

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

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

            okResult.Content.Should().Be("6789");
        }
        public async Task With_Object_Declared_And_OperationResult_Derived_Return_No_Wrapping()
        {
            // Arrange
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithOperation <OperationAsRecord>());

            var returnValue = new StatusCodeResult(HttpStatusCode.OK);

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

            // Assert
            result.Should().Be(returnValue);
        }
        public async Task When_CancellationToken_not_cancelled_runs_to_completion()
        {
            // Arrange
            var cancellationToken = new CancellationTokenSource();

            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithOperation <CancellableOperation>());

            // Act
            var result = await executor.ExecuteWithNewScopeAsync(new CancellableOperation(), cancellationToken.Token);

            // Assert
            result.Should().BeOfType <NoResultOperationResult>();
        }
        public async Task When_Interface_Operation_Registered_RequiresReturnValue_False_Concrete_Operation_Can_Be_Executed()
        {
            // Arrange
            var executor = TestApiOperationExecutor
                           .CreateStandalone(o => o
                                             .WithHandler(new TestApiOperationHandler <OperationImpl>("ignored"))
                                             .WithOperation <IOperationInterface>(c => c.RequiresReturnValue = false));

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

            // Assert
            result.Should().BeOfType <OkResult>();
        }
        public void When_Interface_Operation_Registered_RequiresReturnValue_True_Exception_On_Build()
        {
            // Arrange
            Action tryBuildExecutor = () => TestApiOperationExecutor
                                      .CreateStandalone(o => o
                                                        .WithHandler(new TestApiOperationHandler <OperationImpl>("ignored"))
                                                        .WithOperation <IOperationInterface>(c => c.RequiresReturnValue = true));

            // Act
            tryBuildExecutor.Should().ThrowExactly <InvalidOperationException>()
            .WithMessage(@"Unable to build an executor for the operation Blueprint.Tests.Core.Given_PolymorphicOperationDeclaration+IOperationInterface because the single handler registered, IoC as Blueprint.Tests.TestApiOperationHandler`1[Blueprint.Tests.Core.Given_PolymorphicOperationDeclaration+OperationImpl], did not return a variable but the operation has RequiresReturnValue set to true. 

This can happen if an the only registered handler for an operation is one that is NOT of the same type (for example a handler IApiOperationHandler<ConcreteClass> for the operation IOperationInterface) where it cannot be guaranteed that the handler will be executed.");
        }
        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_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);
        }
        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();
        }
        public async Task When_Returned_Object_Is_More_Specific_And_OperationResult_Does_Not_Convert()
        {
            // Arrange
            var okResult  = new OkResult("theReturn");
            var operation = new RuntimeSpecificReturnAsync(okResult);
            var executor  = TestApiOperationExecutor.CreateStandalone(o => o.WithOperation <RuntimeSpecificReturnAsync>());

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

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

            actualOkResult.Should().BeSameAs(okResult);
        }
        public async Task When_CancellationToken_cancelled_UnhandledExceptionOperationResult_returned()
        {
            // Arrange
            var cancellationToken = new CancellationTokenSource();

            cancellationToken.Cancel();

            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithOperation <CancellableOperation>());

            // Act
            Func <Task> tryExecute = () => executor.ExecuteWithNewScopeAsync(new CancellableOperation(), cancellationToken.Token);

            // Assert
            await tryExecute.Should().ThrowExactlyAsync <OperationCanceledException>();
        }
        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);
        }
        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();
        }
示例#21
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>();");
        }
        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"));
        }
        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 With_Object_Declared_And_NOT_OperationResult_Derived_Return_Wrapped()
        {
            // Arrange
            var executor = TestApiOperationExecutor.CreateStandalone(o => o.WithOperation <OperationWithObjectReturn>());

            var returnValue = new object();

            // Act
            var result = await executor.ExecuteWithNewScopeAsync(new OperationWithObjectReturn
            {
                Result = returnValue
            });

            // Assert
            result.Should().BeOfType <OkResult>();
            result.As <OkResult>().Content.Should().Be(returnValue);
        }
示例#25
0
        public async Task When_Dependency_From_Operation_Context_And_IoC_Then_Injects()
        {
            // Arrange
            var operation = new InlineHandle();
            var executor  = TestApiOperationExecutor.CreateStandalone(
                o => o.WithOperation <InlineHandle>(),
                s => s.AddTransient <IDependency, Dependency>());

            // Act
            await executor.ExecuteWithNewScopeAsync(operation);

            // Assert
            operation.Context.Should().NotBeNull();
            operation.Context.Operation.Should().Be(operation);

            operation.Dependency.Should().NotBeNull();
        }
        public async Task When_User_Does_Have_Required_Claim_Then_Exception()
        {
            // Arrange
            var executor = TestApiOperationExecutor.CreateStandalone(o => o
                                                                     .WithOperation <ClaimRequiredOperation>()
                                                                     .AddAuthentication(a => a.UseContextLoader <TestUserAuthorisationContextFactory>())
                                                                     .AddAuthorisation());

            // Act
            var result = await executor.ExecuteWithAuth(
                new ClaimRequiredOperation(),
                new Claim(ClaimTypes.Permission, "*", "ExecuteThisOperation"));

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

            okResult.Content.Should().Be("12345");
        }
        public async Task When_User_Does_Not_Have_Required_Claim_Then_Exception()
        {
            // Arrange
            var executor = TestApiOperationExecutor.CreateStandalone(o => o
                                                                     .WithOperation <ClaimRequiredOperation>()
                                                                     .AddAuthentication(a => a.UseContextLoader <TestUserAuthorisationContextFactory>())
                                                                     .AddAuthorisation());

            // Act
            Func <Task> tryExecute = () => executor.ExecuteWithAuth(new ClaimRequiredOperation());

            // Assert
            var forbiddenException = await tryExecute.Should().ThrowExactlyAsync <ForbiddenException>();

            forbiddenException.And.Detail.Should().Be("User does not have required claim urn:claims/permission ExecuteThisOperation for *");
            forbiddenException.And.Message.Should().Be("User does not have required claim urn:claims/permission ExecuteThisOperation for *");
            forbiddenException.And.Title.Should().Be("You do not have enough permissions to perform this action");
        }
        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");
        }
示例#29
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();
        }
示例#30
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();
        }