/// <summary> /// Verifies the given OData request/response pair /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public virtual void Verify(ODataRequest request, ODataResponse response) { ExceptionUtilities.CheckArgumentNotNull(request, "request"); ExceptionUtilities.CheckArgumentNotNull(response, "response"); ExceptionUtilities.CheckAllRequiredDependencies(this); ExceptionUtilities.CheckObjectNotNull(this.Logger, "Cannot run verifier without logger"); }
public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); string expectedETag = this.CalculateExpectedETag(request, response); this.Verify(expectedETag, request, response); }
/// <summary> /// Verifies that any navigation or relationship/association links in the payload are correct. /// </summary> /// <param name="request">The request that was sent</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); // Do not need to verify relationship links in metadata response. Skip it to avoid NullReferenceException in GetExpectedEntitySet call // TODO: Make the Json Light decision based on the request URI. Skip for now, since the links are not serialized by default. if (request.Uri.IsMetadata() || this.IsJsonLightResponse(response)) { return; } // gather up all the entities to verify List<EntityInstance> entities = new List<EntityInstance>(); if (response.RootElement.ElementType == ODataPayloadElementType.EntityInstance) { entities.Add(response.RootElement as EntityInstance); } else if (response.RootElement.ElementType == ODataPayloadElementType.EntitySetInstance) { foreach (EntityInstance ei in response.RootElement as EntitySetInstance) { entities.Add(ei as EntityInstance); } } this.VerifyEntityLinks(request, response, entities); }
/// <summary> /// Returns true if for all entity or entity set response payloads /// </summary> /// <param name="response">The response</param> /// <returns>Whether or not this verifier applies to the response</returns> public bool Applies(ODataResponse response) { return response.RootElement != null && ( response.RootElement.ElementType == ODataPayloadElementType.EntityInstance || response.RootElement.ElementType == ODataPayloadElementType.EntitySetInstance || response.RootElement.ElementType == ODataPayloadElementType.LinkCollection); }
/// <summary> /// Checks that the response's status code matches the expected value /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); this.Assert(response.StatusCode == this.ExpectedStatusCode, string.Format(CultureInfo.InvariantCulture, "Expected status code '{0}', observed '{1}'", this.ExpectedStatusCode, response.StatusCode), request, response); this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Status code was '{0}'", this.ExpectedStatusCode); }
/// <summary> /// Verifies response has 'X-Content-Type-Options' HTTP header /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); string noSniffResponseHeaderValue = null; bool noSniffExists = response.Headers.TryGetValue(HttpHeaders.XContentTypeOptions, out noSniffResponseHeaderValue); this.Assert(noSniffExists, HttpHeaders.XContentTypeOptions + " header was not found in the response", request, response); this.Assert(NoSniffValue.Equals(noSniffResponseHeaderValue), HttpHeaders.XContentTypeOptions + " header contained incorrect value " + noSniffResponseHeaderValue, request, response); }
/// <summary> /// Verifies the given OData request/response pair /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); if (response.RootElement != null) { this.Validator.ValidateDateTimeFormatting(response.RootElement); } }
/// <summary> /// Helper method for verifiers to make assertions /// </summary> /// <param name="condition">The condition to assert is true</param> /// <param name="message">The message to write to the log if the assertion fails</param> /// <param name="request">The request being verified</param> /// <param name="response">The response being verified</param> protected void Assert(bool condition, string message, ODataRequest request, ODataResponse response) { if (!condition) { ExceptionUtilities.CheckArgumentNotNull(message, "message"); ExceptionUtilities.CheckArgumentNotNull(request, "request"); ExceptionUtilities.CheckArgumentNotNull(response, "response"); this.Logger.WriteLine(LogLevel.Error, message); this.ReportFailure(request, response); throw new ResponseVerificationException(); } }
internal void Verify(string expectedETag, ODataRequest request, ODataResponse response) { string etagValue; bool hadETag = response.Headers.TryGetValue(HttpHeaders.ETag, out etagValue); if (expectedETag == null) { this.Assert(!hadETag, string.Format(CultureInfo.InvariantCulture, "Response contained unexpected ETag header with value '{0}'.", etagValue), request, response); } else { this.Assert(hadETag, string.Format(CultureInfo.InvariantCulture, "ETag header unexpectedly missing from response. Expected '{0}'.", expectedETag), request, response); this.Assert(expectedETag == etagValue, string.Format(CultureInfo.InvariantCulture, "ETag header value incorrect.\r\nExpected: '{0}'\r\nActual: '{1}'", expectedETag, etagValue), request, response); } }
/// <summary> /// Helper method for verifiers to report failure /// </summary> /// <param name="request">The request that failed verification</param> /// <param name="response">The response that failed verification</param> protected void ReportFailure(ODataRequest request, ODataResponse response) { ExceptionUtilities.CheckArgumentNotNull(request, "request"); ExceptionUtilities.CheckArgumentNotNull(response, "response"); if (this.OnReportingFailure != null) { this.OnReportingFailure(request, response); } this.Logger.WriteLine(LogLevel.Verbose, "Response verification failure"); request.WriteToLog(this.Logger, LogLevel.Verbose); response.WriteToLog(this.Logger, LogLevel.Verbose); }
public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); try { var expected = this.Evaluator.Evaluate(request.Uri); this.VerificationServices.ValidateResponsePayload(request.Uri, response, expected, this.maxProtocolVersion); } catch (Exception e) { this.ReportFailure(request, response); throw new ResponseVerificationException(e); } }
/// <summary> /// Checks that the response's status code matches the expected value /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); ODataErrorPayload errorPayload; this.Assert(TryGetErrorPayloadFromResponse(response, this.FormatSelector, out errorPayload), "Expected an error payload", request, response); this.ExpectedErrorMessage.VerifyExceptionMessage( this.ResourceVerifier, errorPayload, (assertion, errorMessage) => { this.Assert(assertion, errorMessage, request, response); }); }
/// <summary> /// Checks that the response's status code matches the expected value /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); if (request.Uri.IsBatch()) { throw new TaupoNotSupportedException("Batching isn't supported yet, when it is we need to iterate over sets of requests in batch and calculate the version"); } var expectedDataServiceProtocolVersion = this.CalculateDataServiceProtocolVersion(request, response); string expectedProtocolVersion = expectedDataServiceProtocolVersion.ToString().Replace("V", string.Empty) + ".0;"; string actualDataServiceVersionHeaderValue = response.Headers[HttpHeaders.DataServiceVersion]; this.Assert(expectedProtocolVersion.Equals(actualDataServiceVersionHeaderValue), string.Format(CultureInfo.InvariantCulture, "Data Service Version Header error, Expected '{0}' Actual '{1}'", expectedProtocolVersion, actualDataServiceVersionHeaderValue), request, response); }
public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); if (response.StatusCode == HttpStatusCode.NoContent) { this.Assert( !response.Headers.ContainsKey(HttpHeaders.ContentType), "Content-Type header should not be returned for the responses without content.", request, response); } else { // Verify the response Content-Type header exists and is consistent with the request Accept header string responseContentType; this.Assert( response.Headers.TryGetValue(HttpHeaders.ContentType, out responseContentType), "Response Content-Type header does not exist.", request, response); string acceptedContentType = this.RetrieveAcceptedContentType(request); // TODO: strict verification based on the type of the uri var acceptedMimeTypes = acceptedContentType.Split(',').Select(type => type.ToLowerInvariant()).ToList(); this.Assert( acceptedMimeTypes.Any(h => responseContentType.StartsWith(h, StringComparison.Ordinal)) || acceptedMimeTypes.Any(acceptType => acceptType == MimeTypes.Any), "Response Content-Type header should be consistent with request Accept header.", request, response); if (responseContentType.StartsWith(MimeTypes.ApplicationJson, StringComparison.OrdinalIgnoreCase)) { this.Assert( responseContentType.StartsWith(MimeTypes.ApplicationJsonODataLightNonStreaming, StringComparison.Ordinal) || responseContentType.StartsWith(MimeTypes.ApplicationJsonODataLightStreaming, StringComparison.Ordinal), "JSON responses should be fully qualified", request, response); } } }
/// <summary> /// Helper method for getting the error payload from the response. Handles a special case for media-resource operations which may contain errors despite not normally /// being deserialized as such. /// </summary> /// <param name="response">The current response</param> /// <param name="formatSelector">The format selector to use if the response needs to be deserialized</param> /// <param name="errorPayload">The error payload if one was found</param> /// <returns>Whether or not an error payload was found</returns> internal static bool TryGetErrorPayloadFromResponse(ODataResponse response, IProtocolFormatStrategySelector formatSelector, out ODataErrorPayload errorPayload) { ExceptionUtilities.CheckArgumentNotNull(response, "response"); errorPayload = null; var payload = response.RootElement; if (payload == null) { return false; } if (payload.ElementType == ODataPayloadElementType.ODataErrorPayload) { errorPayload = (ODataErrorPayload)payload; return true; } // From here out, try to handle special case for streams which come back with error payloads and are not interpreted if (payload.ElementType != ODataPayloadElementType.PrimitiveValue) { return false; } var body = ((PrimitiveValue)payload).ClrValue as byte[]; if (body == null) { return false; } var contentType = response.GetHeaderValueIfExists(HttpHeaders.ContentType); if (contentType == null) { return false; } // deserialize it ExceptionUtilities.CheckArgumentNotNull(formatSelector, "formatSelector"); var formatForContentType = formatSelector.GetStrategy(contentType, null); var deserializer = formatForContentType.GetDeserializer(); payload = deserializer.DeserializeFromBinary(body, new ODataPayloadContext { EncodingName = HttpUtilities.GetContentTypeCharsetOrNull(contentType) }); errorPayload = payload as ODataErrorPayload; return errorPayload != null; }
/// <summary> /// Verify the given OData request/response pair against all attached verifiers that apply /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); IDisposable scope = null; if (this.Context != null) { scope = this.Context.Begin(request); } try { foreach (var verifier in this.Verifiers) { // make sure the verifier applies var selective = verifier as ISelectiveResponseVerifier; if (selective == null || (selective.Applies(request) && selective.Applies(response))) { try { verifier.Verify(request, response); } catch (ResponseVerificationException e) { // wrap and re-throw to preserve original call-stack. Ideally we would just let these through. throw new ResponseVerificationException(e); } catch (Exception e) { this.Logger.WriteLine(LogLevel.Error, "Verifier '{0}' threw unexpected exception '{1}'", verifier, e.Message); this.ReportFailure(request, response); throw new ResponseVerificationException(e); } } } } finally { if (scope != null) { scope.Dispose(); } } }
/// <summary> /// Verifies the given OData request/response pair using the delegate given at construction time. /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); try { this.verify(request, response); } catch (ResponseVerificationException e) { // wrap and re-throw to preserve original call-stack. Ideally we would just let these through. throw new ResponseVerificationException(e); } catch (Exception e) { this.ReportFailure(request, response); throw new ResponseVerificationException(e); } }
/// <summary> /// Validates the data in the response payload based on the expected query value /// </summary> /// <param name="requestUri">The request uri</param> /// <param name="response">The response</param> /// <param name="expected">The expected query value</param> /// <param name="maxProtocolVersion">The max protocol version of the service</param> public void ValidateResponsePayload(ODataUri requestUri, ODataResponse response, QueryValue expected, DataServiceProtocolVersion maxProtocolVersion) { ExceptionUtilities.CheckObjectNotNull(requestUri, "requestUri"); ExceptionUtilities.CheckObjectNotNull(response, "response"); ExceptionUtilities.CheckObjectNotNull(expected, "expected"); var expectedVersion = response.GetDataServiceVersion(); this.Validator.ExpectedProtocolVersion = expectedVersion; this.Validator.ExpectedPayloadOptions = ODataPayloadOptions.None; string contentType; if (response.Headers.TryGetValue(HttpHeaders.ContentType, out contentType)) { var strategy = this.FormatSelector.GetStrategy(contentType, requestUri); ExceptionUtilities.CheckObjectNotNull(strategy, "Could not get strategy for content type '{0}'", contentType); this.Validator.PrimitiveValueComparer = strategy.GetPrimitiveComparer(); this.Validator.ExpectedPayloadOptions = this.ProtocolImplementationDetails.GetExpectedPayloadOptions(contentType, expectedVersion, requestUri); } this.ValidateAndPrintInfoOnError(requestUri, response.RootElement, expected); }
/// <summary> /// Verifies the response payload type is consistent with the request uri /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); var expectedPayloadType = request.Uri.GetExpectedPayloadType(); // Posting to entity set expects entity instance in response, but posting to service operation response should be consistent with its definition if (request.Verb == HttpVerb.Post && request.Uri.IsEntitySet() && (!request.Uri.IsWebInvokeServiceOperation()) && (!request.Uri.IsAction())) { expectedPayloadType = ODataPayloadElementType.EntityInstance; } string message = null; if (response.RootElement.ElementType != expectedPayloadType) { message = @"Response payload type did not match. Expected: '{0}' Actual: '{1}'"; message = string.Format(CultureInfo.InvariantCulture, message, expectedPayloadType, response.RootElement.ElementType); } this.Assert(message == null, message, request, response); }
/// <summary> /// Return true for all Atom+Xml responses /// </summary> /// <param name="response">The response that might need to be verified</param> /// <returns>Whether or not this verifier applies to the response</returns> public bool Applies(ODataResponse response) { return response.StatusCode != HttpStatusCode.NoContent; }
/// <summary> /// Verify the relationship link is as expected. /// </summary> /// <param name="navigation">navigation property instance for the navigation link</param> /// <param name="associationLink">relationship link to verify</param> /// <param name="expectedAssociationUri">expected link value</param> /// <param name="request">The request needed for error report.</param> /// <param name="response">The response to check the content type.</param> private void VerifyAssociationLink(NavigationPropertyInstance navigation, DeferredLink associationLink, string expectedAssociationUri, ODataRequest request, ODataResponse response) { bool linkShouldBeNull = false; if (!this.IsAtomResponse(response) && navigation.IsExpanded) { var expandedValue = ((ExpandedLink)navigation.Value).ExpandedElement; linkShouldBeNull = expandedValue == null; } if (linkShouldBeNull) { // json navigations are null, then there are no links if (associationLink != null) { this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected association link == null, observed '{0}'", associationLink.UriString); this.ReportFailure(request, response); throw new ResponseVerificationException(); } } else { this.VerifyLinkUriValue(associationLink.UriString, expectedAssociationUri, request, response); // extra verifications for atom payload if (this.IsAtomResponse(response)) { this.VerifyAtomAssociationLinkTypeAttribute(associationLink, request, response); this.VerifyAtomTitleAttribute(navigation, associationLink, request, response); } } }
/// <summary> /// Verify that the type attribute of relationship link is not null and has the expected value "application/xml". /// </summary> /// <param name="link">The navigation link to verify.</param> /// <param name="request">The request needed for error report.</param> /// <param name="response">The response needed for error report.</param> private void VerifyAtomAssociationLinkTypeAttribute(ODataPayloadElement link, ODataRequest request, ODataResponse response) { ContentTypeAnnotation typeAnnotation = link.Annotations.OfType<ContentTypeAnnotation>().SingleOrDefault(); string expectedTypeAttributeValue = "application/xml"; if (typeAnnotation == null || string.Compare(typeAnnotation.Value, expectedTypeAttributeValue, StringComparison.CurrentCulture) != 0) { this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected relationship link type attribute '{0}', observed '{1}'", expectedTypeAttributeValue, typeAnnotation == null ? "null" : typeAnnotation.Value); this.ReportFailure(request, response); throw new ResponseVerificationException(); } }
/// <summary> /// Verify that the title attribute of navigation/relationship link is not null and has the value consistent with the navigation property name. /// </summary> /// <param name="navigation">navigation property instance for the navigation link</param> /// <param name="link">The navigation/relationship link to verify.</param> /// <param name="request">The request needed for error report.</param> /// <param name="response">The response needed for error report.</param> private void VerifyAtomTitleAttribute(NavigationPropertyInstance navigation, ODataPayloadElement link, ODataRequest request, ODataResponse response) { TitleAnnotation titleAnnotation = link.Annotations.OfType<TitleAnnotation>().SingleOrDefault(); if (titleAnnotation == null || string.Compare(titleAnnotation.Value, navigation.Name, StringComparison.CurrentCulture) != 0) { this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected navigation property Title attribute '{0}', observed '{1}'", navigation.Name, titleAnnotation == null ? "null" : titleAnnotation.Value); this.ReportFailure(request, response); throw new ResponseVerificationException(); } }
/// <summary> /// Verify that the navigation/relationship link value is consistent with expected value. /// </summary> /// <param name="link">The navigation/relationship link to verify.</param> /// <param name="expectedLinkValue">The expected link.</param> /// <param name="request">The request needed for error report.</param> /// <param name="response">The response needed for error report.</param> private void VerifyLinkUriValue(string link, string expectedLinkValue, ODataRequest request, ODataResponse response) { if (link == null || !link.Equals(expectedLinkValue, StringComparison.Ordinal)) { this.Logger.WriteLine(LogLevel.Error, CultureInfo.InvariantCulture, "Expected link {0}, observed {1}", expectedLinkValue, link == null ? "Link not Found." : link); this.ReportFailure(request, response); throw new ResponseVerificationException(); } }
/// <summary> /// Verify that the type attribute of navigation link is not null and has the expected value. /// </summary> /// <param name="link">The relationship link to verify.</param> /// <param name="request">The request needed for error report.</param> /// <param name="response">The response needed for error report.</param> private void VerifyAtomNavigationLinkTypeAttribute(ODataPayloadElement link, ODataRequest request, ODataResponse response) { ContentTypeAnnotation typeAnnotation = link.Annotations.OfType<ContentTypeAnnotation>().SingleOrDefault(); if (typeAnnotation == null || (!typeAnnotation.Value.Equals("application/atom+xml;type=feed", StringComparison.Ordinal) && !typeAnnotation.Value.Equals("application/atom+xml;type=entry", StringComparison.Ordinal))) { this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected navigation link type attribute application/atom+xml;type=feed or application/atom+xml;type=entry, observed '{0}'", typeAnnotation == null ? "null" : typeAnnotation.Value); this.ReportFailure(request, response); throw new ResponseVerificationException(); } }
/// <summary> /// Returns true if response content is Json Light format. /// </summary> /// <param name="response">The response to check the content type.</param> /// <returns>True if the content type is Json Light, otherwise False.</returns> private bool IsJsonLightResponse(ODataResponse response) { string contentType = response.Headers[HttpHeaders.ContentType]; return contentType.StartsWith(MimeTypes.ApplicationJsonODataLightNonStreaming, StringComparison.CurrentCulture) || contentType.StartsWith(MimeTypes.ApplicationJsonODataLightStreaming, StringComparison.CurrentCulture); }
/// <summary> /// Returns true if response content is Atom feed/entry. /// </summary> /// <param name="response">The response to check the content type.</param> /// <returns>True if the content type is Atom, otherwise False.</returns> private bool IsAtomResponse(ODataResponse response) { return response.Headers[HttpHeaders.ContentType].StartsWith(MimeTypes.ApplicationAtomXml, StringComparison.CurrentCulture); }
/// <summary> /// Verify the expanded navigation link is as expected. /// </summary> /// <param name="navigation">expanded navigation link to verify</param> /// <param name="expectedNavigationUri">expected link value</param> /// <param name="request">The request needed for error report.</param> /// <param name="response">The response to check the content type.</param> private void VerifyNavigationExpandedLink(NavigationPropertyInstance navigation, string expectedNavigationUri, ODataRequest request, ODataResponse response) { if (this.IsAtomResponse(response)) { this.VerifyNavigationLink(navigation, expectedNavigationUri, request, response); } else { // JSON never has navigation links for expanded elements string expandedLinkUri = ((ExpandedLink)navigation.Value).UriString; if (expandedLinkUri != null) { this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected expanded link uri == null, observed {0}", expandedLinkUri); this.ReportFailure(request, response); throw new ResponseVerificationException(); } } List<EntityInstance> expandedEntities = new List<EntityInstance>(); ODataPayloadElement expandedElement = ((ExpandedLink)navigation.Value).ExpandedElement; if (expandedElement != null) { EntityInstance entityInstance = expandedElement as EntityInstance; if (entityInstance != null) { expandedEntities.Add(entityInstance); } else { EntitySetInstance entitySetInstance = expandedElement as EntitySetInstance; foreach (EntityInstance ei in entitySetInstance) { expandedEntities.Add(ei); } } this.VerifyEntityLinks(request, response, expandedEntities); } }
/// <summary> /// Verify the navigation link is as expected. /// </summary> /// <param name="navigation">navigation link to verify</param> /// <param name="expectedNavigationUri">expected link value</param> /// <param name="request">The request needed for error report.</param> /// <param name="response">The response to check the content type.</param> private void VerifyNavigationLink(NavigationPropertyInstance navigation, string expectedNavigationUri, ODataRequest request, ODataResponse response) { if (navigation.IsExpanded) { this.VerifyLinkUriValue(((ExpandedLink)navigation.Value).UriString, expectedNavigationUri, request, response); } else { this.VerifyLinkUriValue(((DeferredLink)navigation.Value).UriString, expectedNavigationUri, request, response); } // extra verifications for atom payload if (this.IsAtomResponse(response)) { this.VerifyAtomNavigationLinkTypeAttribute(navigation.Value, request, response); this.VerifyAtomTitleAttribute(navigation, navigation.Value, request, response); } }
/// <summary> /// Verifies that any navigation or relationship/association links in the entities are correct. /// </summary> /// <param name="request">The request that was sent</param> /// <param name="response">The response to verify</param> /// <param name="entities">List of entities to verify links</param> private void VerifyEntityLinks(ODataRequest request, ODataResponse response, List<EntityInstance> entities) { // service behavior for telling whether or not the links should be there var serviceBehavior = this.model.GetDefaultEntityContainer().GetDataServiceBehavior(); foreach (var entity in entities) { foreach (var navigation in entity.Properties.OfType<NavigationPropertyInstance>()) { string expectedNavigationUri = this.BuildExpectedNavigationUri(entity, navigation); // verify navigation link if (navigation.Value.ElementType == ODataPayloadElementType.DeferredLink) { this.VerifyNavigationLink(navigation, expectedNavigationUri, request, response); } else { this.VerifyNavigationExpandedLink(navigation, expectedNavigationUri, request, response); } // verify relationship link var associationLink = navigation.AssociationLink; string expectedAssociationUri = this.BuildExpectedAssociationUri(entity, navigation); if (serviceBehavior.IncludeAssociationLinksInResponse == true) { this.VerifyAssociationLink(navigation, associationLink, expectedAssociationUri, request, response); } else if (associationLink != null) { this.Logger.WriteLine(LogLevel.Verbose, "Property '{0}' had association link '{1}'", navigation.Name, associationLink); this.Logger.WriteLine(LogLevel.Error, "Association link unexpectedly non-null for current service configuration"); this.ReportFailure(request, response); throw new ResponseVerificationException(); } } } }