public async Task EmptyCollectionOnContext_YieldsNothingPublished() { var nextCalled = false; Task CallNext(GraphQueryExecutionContext context, CancellationToken token) { nextCalled = true; return(Task.CompletedTask); } var next = new GraphMiddlewareInvocationDelegate <GraphQueryExecutionContext>(CallNext); var queue = new SubscriptionEventQueue(); var publisher = new PublishRaisedSubscriptionEventsMiddleware <GraphSchema>(queue); var server = new TestServerBuilder() .Build(); var context = server.CreateQueryContextBuilder() .Build(); var col = context.Items.GetOrAdd( SubscriptionConstants.RAISED_EVENTS_COLLECTION_KEY, (_) => new List <SubscriptionEventProxy>()); await publisher.InvokeAsync(context, next, default); Assert.IsTrue(nextCalled); Assert.AreEqual(0, queue.Count); }
public async Task ListOfEnums_ProjectsIntoAListCorrectly() { // test the case where a leaf graph type is returne das a // list List<int>, List<string>, List<SomeEnum> etc. // ensure the data items are properly projected into a list // when rendered var server = new TestServerBuilder() .AddGraphType <ListController>() .Build(); var builder = server.CreateQueryContextBuilder() .AddQueryText("query { createEnumList }"); // ensure its a string when printed var result = await server.RenderResult(builder); var expectedOutput = @"{ ""data"": { ""createEnumList"" : [""TESTVALUE1"", ""TESTVALUE2""] } }"; CommonAssertions.AreEqualJsonStrings(expectedOutput, result); }
public void RequestCompletedLogEntry() { var server = new TestServerBuilder() .AddGraphType <LogTestController>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText("{ testField }"); var request = builder.OperationRequest; var context = builder.Build(); var mock = new Mock <IGraphOperationResult>(); mock.Setup(x => x.Request).Returns(request); mock.Setup(x => x.Data).Returns(new ResponseFieldSet()); mock.Setup(x => x.Messages).Returns(new GraphMessageCollection()); context.Result = mock.Object; var entry = new RequestCompletedLogEntry(context); Assert.AreEqual(LogEventIds.RequestCompleted.Id, entry.EventId); Assert.AreEqual(request.Id, entry.OperationRequestId); Assert.AreEqual(false, entry.ResultHasErrors); Assert.AreEqual(true, entry.ResultHasData); Assert.IsNotNull(entry.ToString()); }
public async Task ExtendedTypeOfToAKnownGraphType_ShouldBeProcessed() { var server = new TestServerBuilder() .AddGraphType <MixedReturnTypeController>() .Build(); // controller returns a MixedReturnTypeB, but is declared in the schema as MixedReturnTypeA // (the return type of the controller method signature) // MixedB should be able to masquerade as MixedA var builder = server.CreateQueryContextBuilder() .AddQueryText("query { createReturnObject { field1 }}"); // the returned object should be carried forward to produce a result var result = await server.RenderResult(builder); var expectedOutput = @"{ ""data"": { ""createReturnObject"" : { ""field1"": ""FieldValue1"" } } }"; CommonAssertions.AreEqualJsonStrings(expectedOutput, result); }
public void CollectionKeyIsNotACollection_ThrowsException() { Task CallNext(GraphQueryExecutionContext context, CancellationToken token) { return(Task.CompletedTask); } var next = new GraphMiddlewareInvocationDelegate <GraphQueryExecutionContext>(CallNext); var queue = new SubscriptionEventQueue(); var publisher = new PublishRaisedSubscriptionEventsMiddleware <GraphSchema>(queue); var server = new TestServerBuilder() .Build(); var context = server.CreateQueryContextBuilder() .Build(); var col = context.Items.GetOrAdd( SubscriptionConstants.RAISED_EVENTS_COLLECTION_KEY, (_) => new object()); Assert.ThrowsAsync <GraphExecutionException>(async() => { await publisher.InvokeAsync(context, next, default); }); }
public async Task NestedScalarVariable_IsUsedInsteadOfDefault() { var server = new TestServerBuilder() .AddGraphType <InputValueController>() .AddGraphQL(o => { o.ResponseOptions.ExposeExceptions = true; }) .Build(); // variable passed is just 1 value of hte input object (not the whole thing) var builder = server.CreateQueryContextBuilder(); builder.AddVariableData("{ \"variable1\" : \"stringPassedValue\" }"); builder.AddQueryText("query($variable1: String){ " + "complexValue(arg1: { property1: $variable1, property2: 15} ) " + "{ property1 property2 } }"); var result = await server.RenderResult(builder); var expected = @"{ ""data"" : { ""complexValue"" : { ""property1"" : ""stringPassedValue"", ""property2"" : 15 } } }"; CommonAssertions.AreEqualJsonStrings(expected, result); }
public async Task ExceptionThrownByChildFieldExecution_IsCapturedByParent() { var server = new TestServerBuilder() .AddGraphType <SimpleExecutionController>() .AddSchemaBuilderAction(a => { a.FieldExecutionPipeline.AddMiddleware(new ForceExceptionForProperty1Middlware()); }) .Build(); // with the field execution pipeline addition // this query should run normally but resolving property results // in a random exception // the master query should capture and report this exception var query = server.CreateQueryContextBuilder() .AddQueryText("query { simple {simpleQueryMethod { property1 }}}") .Build(); await server.ExecuteQuery(query); Assert.IsFalse(query.Messages.IsSucessful); Assert.AreEqual(1, query.Messages.Count); Assert.AreEqual(Constants.ErrorCodes.EXECUTION_ERROR, query.Messages[0].Code); Assert.IsNotNull(query.Messages[0].Exception); Assert.AreEqual(GraphMessageSeverity.Critical, query.Messages[0].Severity); Assert.AreEqual("Forced Exception for testing", query.Messages[0].Exception.Message); }
public async Task SingleFieldResolution_NoSuppliedVariables_ResolvePathThroughController_CallsResolutions_Correctly() { var server = new TestServerBuilder() .AddGraphType <SimpleExecutionController>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText(@"query Operation1($var1: String = ""not-default string"") { simple{ simpleQueryMethod (arg1: $var1) { property1} } }"); builder.AddOperationName("Operation1"); // supply no values, allowing the defaults to take overand returning the single // requested "Property1" with the default string defined on the method. var result = await server.RenderResult(builder); CommonAssertions.AreEqualJsonStrings( @"{ ""data"": { ""simple"": { ""simpleQueryMethod"" : { ""property1"" : ""not-default string"" } } } }", result); }
public async Task TypeNameMetaField_OnObject_returnsValue() { var server = new TestServerBuilder() .AddGraphType <SimpleExecutionController>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText("query Operation1{ simple { simpleQueryMethod { property1 __typename} } }"); builder.AddOperationName("Operation1"); // supply no values, allowing the defaults to take overand returning the single // requested "Property1" with the default string defined on the method. var result = await server.RenderResult(builder); CommonAssertions.AreEqualJsonStrings( @"{ ""data"": { ""simple"": { ""simpleQueryMethod"" : { ""property1"" : ""default string"", ""__typename"" : ""TwoPropertyObject"" } } } }", result); }
public async Task ObjectMethodAsField_Asyncronously_ResolvesCorrectly() { var server = new TestServerBuilder() .AddGraphType <ObjectMethodController>() .Build(); var builder = server.CreateQueryContextBuilder() .AddQueryText("query { objects { retrieveObject { asyncMethod} } }"); // call the child AsyncMethod (which is a asycrnous method described as a field on an object, not a controller) // ensure it returns a value var result = await server.RenderResult(builder); var expectedOutput = @"{ ""data"": { ""objects"" : { ""retrieveObject"" : { ""asyncMethod"" : 8 } } } }"; CommonAssertions.AreEqualJsonStrings(expectedOutput, result); }
public async Task Rule_6_4_3_EnsureCorrectErrorIsGenerated(string expectedRuleError, string queryText) { var server = new TestServerBuilder() .AddGraphType <PeopleMoverController>() .AddGraphType <AllowDirective>() .AddGraphType <RestrictDirective>() .Build(); var queryBuilder = server.CreateQueryContextBuilder(); queryBuilder.AddQueryText(queryText); // execute the query var result = await server.ExecuteQuery(queryBuilder); // inspect for error codes if (result.Messages.Count != 1) { var errors = result.Messages.Where( x => x.MetaData?.ContainsKey("Rule") == true) .Select(x => x.MetaData["Rule"]); string errorMessage = $"Expected 1 error ({expectedRuleError}) but recieved {result.Messages.Count}: '{string.Join(", ", errors)}'"; Assert.Fail(errorMessage); } var message = result.Messages[0]; Assert.IsNotNull(message.MetaData); Assert.IsTrue(message.MetaData.ContainsKey("Rule")); var ruleBroke = message.MetaData["Rule"]; Assert.AreEqual(expectedRuleError, ruleBroke.ToString()); }
public async Task FieldOnConcreteTypes_ButNotOnSharedInterface_WhenInterfaceIsReturned_IsDenied() { var server = new TestServerBuilder() .AddGraphType <ConcreteObjectA>() .AddGraphType <ConcreteObjectB>() .AddGraphType <InterfaceExtensionController>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText(@" { multiObjects { middleName fullName } }"); // MiddleName is declared on ObjectA but NOT on the interface returned by 'multiObjects' // since the return type of multiObjects IS the interface it should be denied as a // valid field even though the underlying concrete types could process it var result = await server.ExecuteQuery(builder); Assert.AreEqual(GraphMessageSeverity.Critical, result.Messages.Severity); }
public async Task NoItemsOnContext_YieldsNothingPublished() { var nextCalled = false; Task CallNext(GraphQueryExecutionContext context, CancellationToken token) { nextCalled = true; return(Task.CompletedTask); } var next = new GraphMiddlewareInvocationDelegate <GraphQueryExecutionContext>(CallNext); var queue = new SubscriptionEventQueue(); var publisher = new PublishRaisedSubscriptionEventsMiddleware <GraphSchema>(queue); var server = new TestServerBuilder() .Build(); var context = server.CreateQueryContextBuilder() .Build(); await publisher.InvokeAsync(context, next, default); Assert.IsTrue(nextCalled); Assert.AreEqual(0, queue.Count); }
public async Task IteratingACollection_CallsResolutions_Correctly() { var server = new TestServerBuilder() .AddGraphType <SimpleExecutionController>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText("query Operation1{ simple { collectionQueryMethod { property1 property2 } } }"); builder.AddOperationName("Operation1"); // supply no values, allowing the defaults to take overand returning the single // requested "Property1" with the default string defined on the method. var result = await server.RenderResult(builder); var output = @"{ ""data"" : { ""simple"": { ""collectionQueryMethod"": [ { ""property1"": ""string0"", ""property2"": 0 }, { ""property1"": ""string1"", ""property2"": 1 }, { ""property1"": ""string2"", ""property2"": 2 } ] } } }"; CommonAssertions.AreEqualJsonStrings( output, result); }
public async Task BasicBranch_ViaNamedFragments_RendersCorrectly() { var server = new TestServerBuilder() .AddGraphType <FragmentProcessingController>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText(@" query { fragTester { makeHybridData { ...aData ...bData } } } fragment aData on FragmentDataA{ property1 } fragment bData on FragmentDataB{ property2 property1 }"); var r = await server.ExecuteQuery(builder); var result = await server.RenderResult(builder); // note the ordering on bData fragment var expectedReslt = @" { ""data"" : { ""fragTester"": { ""makeHybridData"": [ { ""property1"": ""fragmentA_prop1_0"" }, { ""property1"": ""fragmentA_prop1_1"" }, { ""property2"": ""fragmentB_prop2_0"", ""property1"": ""fragmentB_prop1_0"" }, { ""property2"": ""fragmentB_prop2_1"", ""property1"": ""fragmentB_prop1_1"" }] } } }"; CommonAssertions.AreEqualJsonStrings(expectedReslt, result); }
public async Task TypeExtension_AppliedToAnInterface_CanBeActedOnByMultipleConcreteObjects() { var server = new TestServerBuilder() .AddGraphType <ConcreteObjectA>() .AddGraphType <ConcreteObjectB>() .AddGraphType <InterfaceExtensionController>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText(@" { multiObjects { firstName fullName } }"); // fullName is an type extension keyed on the interface that both object types inherit it does not exist as a declared // member of ObjectA, B or the common interface // the interface explicitly DOES NOT declare FirstName or LastName ensuring they must be generated from the // concrete types var result = await server.RenderResult(builder); // output combines declared methods on concrete objects, firstName, and the interface extension, fullName var expectedOutput = @" { ""data"": { ""multiObjects"": [ { ""firstName"" : ""0A_prop1"", ""fullName"": ""0A_prop1 0A_prop2"" }, { ""firstName"" : ""1A_prop1"", ""fullName"": ""1A_prop1 1A_prop2"" }, { ""firstName"" : ""0B_prop1"", ""fullName"": ""0B_prop1 0B_prop2"" }, { ""firstName"" : ""1B_prop1"", ""fullName"": ""1B_prop1 1B_prop2"" } ] } }"; CommonAssertions.AreEqualJsonStrings(expectedOutput, result); }
public async Task WhenAnInterfaceIsSpreadInAUnion_TheUnionMembersThatImplementTheInterface_ShouldReturnInterfaceFields() { // sourceDataInheritance returns a union type (FragmentDataA | FragmentDataB | FragmentDataC) // All Three implement interface graph type FragmentDataItem and should include any fields in the "sampleFrag" fragment // which is typed to the interface var server = new TestServerBuilder() .AddGraphType <FragmentProcessingController>() .AddGraphType <IFragmentDataItem>() .AddSchemaBuilderAction(o => { o.Options.ResponseOptions.ExposeExceptions = true; }) .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText(@" query { fragTester { sourceDataInheritance { ...sampleFrag } } } fragment sampleFrag on FragmentDataItem { property1 }"); var expectedResult = @" { ""data"" : { ""fragTester"": { ""sourceDataInheritance"": [ { ""property1"": ""fragmentA_prop1_0"" }, { ""property1"": ""fragmentB_prop1_0"" }, { ""property1"": ""fragmentA_prop1_FromC"" }] } } }"; var result = await server.RenderResult(builder); CommonAssertions.AreEqualJsonStrings(expectedResult, result); }
public async Task WhenNoLeafValuesAreRequested_ItemIsReturnedAsNullAndPropegated() { var server = new TestServerBuilder() .AddGraphType <SimpleExecutionController>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText("query Operation1{ simple { simpleQueryMethod } }"); builder.AddOperationName("Operation1"); var result = await server.RenderResult(builder); CommonAssertions.AreEqualJsonStrings( "{ \"data\" : null }", result); }
public async Task ListOfEnums_WithAnUnlabeledValue_Fails() { var server = new TestServerBuilder() .AddGraphType <ListController>() .Build(); // controller returns a list of {Value1, -3} var builder = server.CreateQueryContextBuilder() .AddQueryText("query { createEnumListWithInValidValue }"); // ensure hte -3 wasnt accepted as valid for the enum being // returned var result = await server.ExecuteQuery(builder); Assert.IsFalse(result.Messages.IsSucessful); Assert.AreEqual(1, result.Messages.Count); }
public async Task SimpleResolution_WithDirective_CallsDirectiveCorrectly() { var server = new TestServerBuilder() .AddGraphType <SimpleExecutionController>() .AddGraphType <CallTestDirective>() .Build(); var builder = server.CreateQueryContextBuilder() .AddQueryText("query Operation1{ simple { simpleQueryMethod @callTest(arg: 3) { property1} } }") .AddOperationName("Operation1"); // supply no values, allowing the defaults to take overand returning the single // requested "Property1" with the default string defined on the method. var result = await server.ExecuteQuery(builder); Assert.AreEqual(0, result.Messages.Count); Assert.AreEqual(1, CallTestDirective.TotalCalls); }
public async Task IndeterminateUnionType_ShouldCallIntoUnionProxyResolveMethod_AndProduceResult() { MixedTypeUnion.TotalCallCount = 0; var server = new TestServerBuilder() .AddGraphType <MixedReturnTypeController>() .AddSchemaBuilderAction(a => { a.Options.ResponseOptions.ExposeExceptions = true; }) .Build(); // controller actually returns a MixedReturnTypeC, but is declared in the schema as // MixedUnionType (of A and B) // MixedC inherits from both A and B and could be either // the library should call into MixedUnionType and ask it to resolve the relationship // // mixedUnion should return TypeA, thus rendering field 1 var builder = server.CreateQueryContextBuilder() .AddQueryText( @"query { createIndeterminateReturn { ... on MixedReturnTypeB { field2 } ... on MixedReturnTypeA { field1 } }}"); // the returned object should be carried forward to produce a result var result = await server.RenderResult(builder); var expectedOutput = @"{ ""data"": { ""createIndeterminateReturn"" : { ""field1"": ""FieldValue1"" } } }"; CommonAssertions.AreEqualJsonStrings(expectedOutput, result); Assert.AreEqual(1, MixedTypeUnion.TotalCallCount); }
public async Task DirectiveExecution_MaintainsRequestMetaDataCollection_CallsDirectiveCorrectly() { var server = new TestServerBuilder() .AddGraphType <SimpleExecutionController>() .AddGraphType <MetaDataShareDirective>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddQueryText("query Operation1{ simple { simpleQueryMethod @metaDataShare(arg: 3) { property1} } }"); builder.AddOperationName("Operation1"); // supply no values, allowing the defaults to take overand returning the single // requested "Property1" with the default string defined on the method. var context = builder.Build(); await server.ExecuteQuery(context); Assert.AreEqual(0, context.Messages.Count); Assert.IsTrue(MetaDataShareDirective.FoundInAfterCompletion); }
public async Task UnhandledException_InUserCode_OnDirectGraphMethodCall_ResultsInMessageOnResponse() { var server = new TestServerBuilder() .AddGraphType <SimpleExecutionController>() .Build(); var builder = server.CreateQueryContextBuilder() .AddQueryText("query { simple { throwable { executeThrow } } }"); // the controller method returns an object with a single mapped method "ExecuteThrow" that throws an exception // this is executed through the graphmethod resolver (not the controller action resolver) allowing an internal // exception to fully bubble up (not captured by the action invoker) var result = await server.ExecuteQuery(builder); Assert.AreEqual(1, result.Messages.Count); Assert.AreEqual(GraphMessageSeverity.Critical, result.Messages.Severity); Assert.AreEqual(Constants.ErrorCodes.UNHANDLED_EXCEPTION, result.Messages[0].Code); Assert.IsNotNull(result.Messages[0].Exception); Assert.AreEqual("Failure from ObjectWithThrowMethod", result.Messages[0].Exception.Message); }
public async Task IncludeDirective_ResponseAppropriately(bool includeValue, string expectedJson) { var server = new TestServerBuilder() .AddGraphType <SimpleExecutionController>() .AddGraphType <IncludeDirective>() .Build(); var builder = server.CreateQueryContextBuilder() .AddQueryText( "query Operation1{ simple { simpleQueryMethod { property1, property2 @include(if: " + includeValue.ToString().ToLower() + ") } } }") .AddOperationName("Operation1"); var result = await server.RenderResult(builder); CommonAssertions.AreEqualJsonStrings( expectedJson, result); }
public async Task DataMessage_WithData_SerializesCorrectly() { var server = new TestServerBuilder() .AddGraphController <ApolloDataMessageController>() .AddSubscriptionServer() .Build(); var client = server.CreateSubscriptionClient(); var context = server.CreateQueryContextBuilder() .AddQueryText("query { getValue { property1 property2 } }") .Build(); await server.ExecuteQuery(context); var message = new ApolloServerDataMessage("abc111", context.Result); var converter = new ApolloServerDataMessageConverter( server.Schema, server.ServiceProvider.GetRequiredService <IGraphResponseWriter <GraphSchema> >()); var options = new JsonSerializerOptions(); options.Converters.Add(converter); var response = JsonSerializer.Serialize(message, message.GetType(), options); var expected = @" { ""type"" : ""data"", ""id"" : ""abc111"", ""payload"": { ""data"": { ""getValue"" : { ""property1"": ""abc123"", ""property2"" : 15 } } } }"; CommonAssertions.AreEqualJsonStrings(expected, response); }
public async Task SingleScalarValueVariable_IsUsedInsteadOfDefault() { var server = new TestServerBuilder() .AddGraphType <InputValueController>() .Build(); var builder = server.CreateQueryContextBuilder(); builder.AddVariableData("{ \"variable1\" : \"test string 86\" }"); builder.AddQueryText("query($variable1: String){ scalarValue(arg1: $variable1) }"); var result = await server.RenderResult(builder); var expected = @" { ""data"" : { ""scalarValue"" : ""test string 86"" } }"; CommonAssertions.AreEqualJsonStrings(expected, result); }
public async Task Rule_6_4_4_ErrorPropegation_InvalidatesParentObjects_MultiRootFields() { var server = new TestServerBuilder() .AddGraphType <PeopleMoverController>() .AddGraphType <AllowDirective>() .AddGraphType <RestrictDirective>() .Build(); // field "invalidMovers" will result in a null field because of an error caused by // down stream field peopleMovers.allElevatorsWithANull[1].Name // // however the field "validMovers" should resolve just fine // resulting in a data set with invalidMovers as null but with validMovers as having a value // fully populated var queryBuilder = server.CreateQueryContextBuilder(); queryBuilder.AddQueryText(@"query Operation1{ invalidMovers: peopleMovers { allElevatorsWithANull{ id name } } validMovers: peopleMovers { elevator(id: 5) { id name } } }"); var result = await server.ExecuteQuery(queryBuilder); // ensure error 6.4.3 is recorded (and that its all thats recorded) Assert.AreEqual(1, result.Messages.Count); var message = result.Messages[0]; Assert.IsNotNull(message.MetaData); Assert.IsTrue(message.MetaData.ContainsKey("Rule")); var ruleBroke = message.MetaData["Rule"]; Assert.AreEqual("6.4.3", ruleBroke.ToString()); Assert.IsNotNull(result.Data); var data = result.Data as IResponseFieldSet; Assert.IsNull(data.Fields["invalidMovers"]); Assert.IsNotNull(data.Fields["validMovers"]); }
public async Task CustomScalar_BecomesValidCoreScalarValue() { var server = new TestServerBuilder() .AddGraphType <GraphIdController>() .Build(); // retrieveid returns a "GraphId" var builder = server.CreateQueryContextBuilder() .AddQueryText("query { retrieveId }"); // ensure its a string when printed var result = await server.RenderResult(builder); var expectedOutput = @"{ ""data"": { ""retrieveId"" : ""abc123"" } }"; CommonAssertions.AreEqualJsonStrings(expectedOutput, result); }
public async Task IndeterminateUnionType_NullReturnType_Fails() { MixedTypeUnionNullReturn.TotalCallCount = 0; var server = new TestServerBuilder() .AddGraphType <MixedReturnTypeController>() .AddSchemaBuilderAction(a => { a.Options.ResponseOptions.ExposeExceptions = true; }) .Build(); // controller actually returns a MixedReturnTypeC, but is declared in the schema as // MixedTypeUnionNullReturn (of A and B) // MixedC inherits from both A and B and could be either // the library should call into MixedUnionType and ask it to resolve the relationship // the union type (MixedTypeUnionNullReturn) will return -null- // and the query should fail var builder = server.CreateQueryContextBuilder() .AddQueryText( @"query { createNullIndeterminateReturn { ... on MixedReturnTypeB { field2 } ... on MixedReturnTypeA { field1 } }}"); // the returned object should be carried forward to produce a result var result = await server.ExecuteQuery(builder); Assert.IsNotNull(result); Assert.IsFalse(result.Messages.IsSucessful); Assert.AreEqual(1, result.Messages.Count); Assert.AreEqual(Constants.ErrorCodes.EXECUTION_ERROR, result.Messages[0].Code); Assert.AreEqual(1, MixedTypeUnionNullReturn.TotalCallCount); }
public async Task Rule_6_4_4_ErrorPropegation_InvalidatesParentObjects() { var server = new TestServerBuilder() .AddGraphType <PeopleMoverController>() .AddGraphType <AllowDirective>() .AddGraphType <RestrictDirective>() .Build(); // allElevatorsWithANull is defined to be a list of elevators (type expression: [Elevator!]) // it returns a list but with the second item (of 3) having a null name that when resolved // fails validation for that individual list item as each elevator must have a name according // the the Elevator's individual type requirements. // This results in the second list item becoming null...which in turn results in the // list being invalid (since it now contains a null element) this fails rule 6.4.4 // // ensure that this single invalid name in list item 2 is propegated to null out // the whole list var queryBuilder = server.CreateQueryContextBuilder(); queryBuilder.AddQueryText("query Operation1{ peopleMovers { allElevatorsWithANull{ id name } } }"); var result = await server.ExecuteQuery(queryBuilder); // ensure error 6.4.3 is recorded (and that its all thats recorded) Assert.AreEqual(1, result.Messages.Count); var message = result.Messages[0]; Assert.IsNotNull(message.MetaData); Assert.IsTrue(message.MetaData.ContainsKey("Rule")); var ruleBroke = message.MetaData["Rule"]; Assert.AreEqual("6.4.3", ruleBroke.ToString()); // since only one top level query was made (peopleMovers) // this reslts in all fields of the query being null, forcing "data" to also be null Assert.IsNull(result.Data); }