Example #1
0
        public async Task When_ResourceCreated_Then_Attributes_Populated()
        {
            using var t = SystemTime.PauseForThread();

            // Arrange
            var executor = TestApiOperationExecutor.CreateHttp(o => o
                                                               .WithOperation <CreationOperation>()
                                                               .WithOperation <SelfQuery>()
                                                               .AddResourceEvents <NullResourceEventRepository>());

            // Act
            var context = executor.HttpContextFor(new CreationOperation {
                IdToCreate = "1234"
            });

            var result = await executor.ExecuteAsync(context);

            // Assert
            var @event = result.ShouldBeContent <CreatedResourceEvent>();

            @event.Created.UtcDateTime.Should().BeCloseTo(t.UtcNow);
            @event.Object.Should().Be("event");
            @event.EventId.Should().Be("awesome.created");
            @event.ChangeType.Should().Be(ResourceEventChangeType.Created);
            @event.ResourceObject.Should().Be("awesome");
            @event.Href.Should().Be("https://api.blueprint-testing.com/api/resources/1234");
        }
        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_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_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_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_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 async Task When_ResourceCreated_Then_Attributes_Populated()
        {
            using (var t = SystemTime.PauseForThread())
            {
                // Arrange
                var executor = TestApiOperationExecutor.CreateHttp(o => o
                                                                   .WithOperation <CreationOperation>()
                                                                   .WithOperation <SelfQuery>()
                                                                   .AddResourceEvents <NullResourceEventRepository>()
                                                                   .AddAuthentication(a => a.UseContextLoader <TestUserAuthorisationContextFactory>())
                                                                   .AddAuthorisation());

                // Act
                var context = executor.HttpContextFor(new CreationOperation {
                    IdToCreate = "1234"
                })
                              .WithAuth(new Claim("sub", "User8547"));

                var result = await executor.ExecuteAsync(context);

                // Assert
                var @event = result.ShouldBeContent <CreatedResourceEvent>();

                @event.Created.UtcDateTime.Should().BeCloseTo(t.UtcNow);
                @event.Object.Should().Be("event");
                @event.EventId.Should().Be("awesome.created");
                @event.ChangeType.Should().Be(ResourceEventChangeType.Created);
                @event.ResourceObject.Should().Be("awesome");
            }
        }
        public async Task When_ResourceCreated_And_Self_Query_Exists_Populates_Data()
        {
            // Arrange
            var executor = TestApiOperationExecutor.CreateHttp(o => o
                                                               .WithOperation <CreationOperation>()
                                                               .WithOperation <SelfQuery>()
                                                               .AddResourceEvents <NullResourceEventRepository>());

            // Act
            // Note, do 2 executions to ensure correct parameters and being passed around
            var context1 = executor.HttpContextFor(new CreationOperation {
                IdToCreate = "1234"
            });
            var context2 = executor.HttpContextFor(new CreationOperation {
                IdToCreate = "9876"
            });

            var result1 = await executor.ExecuteAsync(context1);

            var result2 = await executor.ExecuteAsync(context2);

            // Assert
            result1.ShouldBeContent <CreatedResourceEvent>().Data.Id.Should().Be("1234");
            result2.ShouldBeContent <CreatedResourceEvent>().Data.Id.Should().Be("9876");
        }
Example #10
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();
        }
Example #11
0
        private static ApiOperationContext GetContext <T>(TestApiOperationExecutor executor, string queryString)
        {
            var context = executor.HttpContextFor <T>();

            context.GetHttpContext().Request.QueryString = new QueryString(queryString);

            return(context);
        }
Example #12
0
        public static Task <OperationResult> ExecuteWithAuth(this TestApiOperationExecutor executor, object operation, params Claim[] claims)
        {
            var context = executor.ContextFor(operation);

            context.WithAuth(claims);

            return(executor.ExecuteAsync(context));
        }
Example #13
0
        private static ApiOperationContext GetContext <T>(TestApiOperationExecutor executor, string routeKey, string routeValue)
        {
            var context = executor.HttpContextFor <T>();

            context.GetHttpContext().Request.Headers["Content-Type"] = "application/json";
            context.GetRouteData().Values[routeKey] = routeValue;

            return(context);
        }
        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>();
        }
Example #15
0
        private static ApiOperationContext GetContext <T>(TestApiOperationExecutor executor, T body)
        {
            var jsonBody = JsonConvert.SerializeObject(body);

            return(executor.HttpContextFor <T>(ctx =>
            {
                ctx.Request.Body = jsonBody.AsUtf8Stream();
                ctx.Request.Headers["Content-Type"] = "application/json";
            }));
        }
Example #16
0
        private static ApiOperationContext GetContext <T>(
            TestApiOperationExecutor executor,
            Dictionary <string, string> cookies)
        {
            var context         = executor.HttpContextFor <T>();
            var cookiesAsHeader = cookies.Select(c => $"{c.Key}={c.Value}").ToArray();

            context.GetHttpContext().Request.Headers["Cookie"] = cookiesAsHeader;

            return(context);
        }
Example #17
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.");
        }
Example #18
0
        public async Task When_Operation_with_inline_handler_then_populates_variables()
        {
            // Arrange
            var executor = TestApiOperationExecutor
                           .CreateHttp(o => o.WithOperation <HttpOperation>());

            // Act
            var result = await executor.ExecuteAsync <HttpOperation>();

            // Assert
            result.ShouldBeOperationResultType <NoResultOperationResult>();
        }
 private static ApiOperationContext GetContext <T>(
     TestApiOperationExecutor executor,
     Dictionary <string, string> headers)
 {
     return(executor.HttpContextFor <T>(ctx =>
     {
         foreach (var h in headers)
         {
             ctx.Request.Headers[h.Key] = h.Value;
         }
     }));
 }
        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");
        }
        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_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>();
        }
        private static ApiOperationContext GetContext <T>(
            TestApiOperationExecutor executor,
            Dictionary <string, string> headers)
        {
            var context = executor.HttpContextFor <T>();

            foreach (var h in headers)
            {
                context.GetHttpContext().Request.Headers[h.Key] = h.Value;
            }

            return(context);
        }
        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_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);
        }
Example #26
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);
        }
Example #27
0
        public async Task When_HttpMessagePopulation_Child_Operation_Then_Properties_Are_Not_Populated()
        {
            // Arrange
            var executor = TestApiOperationExecutor.CreateHttp(o => o
                                                               .WithOperation <CreationOperation>()
                                                               .WithOperation <SelfQuery>()
                                                               .AddResourceEvents <NullResourceEventRepository>());

            // Act
            var result = await executor.ExecuteAsync(new CreationOperation { IdToCreate = "1234" });

            // Assert
            result.ShouldBeContent <CreatedResourceEvent>().Data.Id.Should().Be("1234");
        }
        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_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.");
        }