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"); }
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); }
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(); }
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); }
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_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); }
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"); }
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(); }
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(); }
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)); }
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); }
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"); }
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(); }
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(); }
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); }