Esempio n. 1
0
        /// <summary>
        /// Verifies the data service response.
        /// </summary>
        /// <param name="responseData">The expected data for the response.</param>
        /// <param name="response">The response to verify.</param>
        /// <param name="cachedOperationsFromResponse">The individual operation responses, pre-enumerated and cached</param>
        /// <exception cref="TaupoNotSupportedException">
        /// When exception is expected in the response data
        /// </exception>
        public void VerifyDataServiceResponse(DataServiceResponseData responseData, DataServiceResponse response, IList <OperationResponse> cachedOperationsFromResponse)
        {
            ExceptionUtilities.CheckArgumentNotNull(responseData, "responseData");
            ExceptionUtilities.CheckArgumentNotNull(response, "response");
            ExceptionUtilities.CheckArgumentNotNull(cachedOperationsFromResponse, "cachedOperationsFromResponse");
            ExceptionUtilities.CheckAllRequiredDependencies(this);

            this.Assert.AreEqual(responseData.IsBatchResponse, response.IsBatchResponse, "Verifying if it's a batch response.");
            this.Assert.AreEqual(responseData.BatchStatusCode, response.BatchStatusCode, "Verifying batch status code.");

            //// No batch headers verification
            //// Note: verifying order of operation responses as well as the content.

            this.Assert.AreEqual(responseData.Count, cachedOperationsFromResponse.Count, "Unexpected number of operation responses in data service response");

            for (int responseOrder = 0; responseOrder < responseData.Count; responseOrder++)
            {
                var operationResponseData = responseData[responseOrder];
                var currentResponse       = cachedOperationsFromResponse[responseOrder];

                ChangeOperationResponseData changeResponseData = operationResponseData as ChangeOperationResponseData;
                if (changeResponseData != null)
                {
                    ChangeOperationResponse changeResponse = currentResponse as ChangeOperationResponse;
                    this.Assert.IsNotNull(changeResponse, GetVerificationFailureMessage(responseOrder, "Unexpected type of the operation response.\r\nExpected: {0}\r\nActual:   {1}", typeof(ChangeOperationResponse).FullName, currentResponse));

                    this.VerifyChangeOperationResponse(changeResponseData, changeResponse, responseOrder);
                }
                else
                {
                    throw new TaupoNotSupportedException(
                              GetVerificationFailureMessage(responseOrder, "Verification for the operation response data of type '{0}' is not supported by this verifier.", operationResponseData.GetType().FullName));
                }
            }
        }
        /// <summary>
        /// Verifies the data service response.
        /// </summary>
        /// <param name="responseData">The expected data for the response.</param>
        /// <param name="response">The response to verify.</param>
        /// <param name="cachedOperationsFromResponse">The individual operation responses, pre-enumerated and cached</param>
        /// <exception cref="TaupoNotSupportedException">
        /// When exception is expected in the response data 
        /// </exception>
        public void VerifyDataServiceResponse(DataServiceResponseData responseData, DataServiceResponse response, IList<OperationResponse> cachedOperationsFromResponse)
        {
            ExceptionUtilities.CheckArgumentNotNull(responseData, "responseData");
            ExceptionUtilities.CheckArgumentNotNull(response, "response");
            ExceptionUtilities.CheckArgumentNotNull(cachedOperationsFromResponse, "cachedOperationsFromResponse");
            ExceptionUtilities.CheckAllRequiredDependencies(this);

            this.Assert.AreEqual(responseData.IsBatchResponse, response.IsBatchResponse, "Verifying if it's a batch response.");
            this.Assert.AreEqual(responseData.BatchStatusCode, response.BatchStatusCode, "Verifying batch status code.");

            //// No batch headers verification
            //// Note: verifying order of operation responses as well as the content.

            this.Assert.AreEqual(responseData.Count, cachedOperationsFromResponse.Count, "Unexpected number of operation responses in data service response");

            for (int responseOrder = 0; responseOrder < responseData.Count; responseOrder++)
            {
                var operationResponseData = responseData[responseOrder];
                var currentResponse = cachedOperationsFromResponse[responseOrder];

                ChangeOperationResponseData changeResponseData = operationResponseData as ChangeOperationResponseData;
                if (changeResponseData != null)
                {
                    ChangeOperationResponse changeResponse = currentResponse as ChangeOperationResponse;
                    this.Assert.IsNotNull(changeResponse, GetVerificationFailureMessage(responseOrder, "Unexpected type of the operation response.\r\nExpected: {0}\r\nActual:   {1}", typeof(ChangeOperationResponse).FullName, currentResponse));

                    this.VerifyChangeOperationResponse(changeResponseData, changeResponse, responseOrder);
                }
                else
                {
                    throw new TaupoNotSupportedException(
                        GetVerificationFailureMessage(responseOrder, "Verification for the operation response data of type '{0}' is not supported by this verifier.", operationResponseData.GetType().FullName));
                }
            }
        }
Esempio n. 3
0
            private void UpdateResponse(DataServiceResponseData responseData, DescriptorData descriptor, HttpResponseData response)
            {
                var operationResponse = new ChangeOperationResponseData(descriptor);

                operationResponse.StatusCode = (int)response.StatusCode;
                foreach (var header in response.Headers)
                {
                    operationResponse.Headers.Add(header.Key, header.Value);
                }

                responseData.Add(operationResponse);
            }
Esempio n. 4
0
            private void HandleBatchRequest(DataServiceResponseData responseData)
            {
                this.parent.Assert.AreEqual(1, this.httpQueue.Count, "Should only observe one request in $batch scenarios");
                var batchRequest = (HttpRequestData)this.httpQueue.Peek().Key;

                var batchRequestPayload = this.parent.BatchDeserializer.DeserializeBatchRequest(batchRequest);

                this.parent.Assert.AreEqual(1, batchRequestPayload.Changesets.Count(), "Batch request payload should only have one changeset");
                this.parent.Assert.AreEqual(0, batchRequestPayload.Operations.Count(), "Batch request payload should not contain any operations");

                var expectedBatchHeaders = new HttpHeaderCollection()
                {
                    Accept                = MimeTypes.MultipartMixed,
                    IfMatch               = null,
                    Prefer                = null,
                    HttpMethod            = null,
                    DataServiceVersion    = DataServiceProtocolVersion.V4.ConvertToHeaderFormat() + ";" + HttpHeaders.NetFx,
                    MaxDataServiceVersion = this.contextData.MaxProtocolVersion.ConvertToHeaderFormat() + ";" + HttpHeaders.NetFx,
                };

                this.CompareHeaders(expectedBatchHeaders, batchRequest.Headers);

                var batchResponse = this.httpQueue.Dequeue().Value;

                responseData.BatchStatusCode = (int)batchResponse.StatusCode;
                responseData.BatchHeaders.AddRange(batchResponse.Headers);

                var batchResponsePayload = this.parent.BatchDeserializer.DeserializeBatchResponse(batchRequestPayload, batchResponse);

                this.parent.Assert.AreEqual(1, batchResponsePayload.Changesets.Count(), "Batch response payload should only have one changeset");
                this.parent.Assert.AreEqual(0, batchResponsePayload.Operations.Count(), "Batch response payload should not contain any operations");

                var requestChangeset  = batchRequestPayload.Changesets.Single();
                var responseChangeset = batchResponsePayload.Changesets.Single();

                this.parent.Assert.AreEqual(requestChangeset.Count(), responseChangeset.Count(), "Unexpected number of response operations in changeset");

                // TODO: report this better
                this.parent.Assert.IsTrue(requestChangeset.Operations.All(r => r is ODataRequest), "Some requests were not fully deserialized");

                this.httpQueue = new Queue <KeyValuePair <IHttpRequest, HttpResponseData> >(requestChangeset.Operations.Zip(responseChangeset.Operations, (req, res) => new KeyValuePair <IHttpRequest, HttpResponseData>(req, res)));
            }
        /// <summary>
        /// Calculates the expected response from DataServiceContext.SaveChanges based on the context data before saving changes.
        /// Assumes there are no errors in the response.
        /// </summary>
        /// <param name="dataBeforeSaveChanges">The data before saving changes.</param>
        /// <param name="options">The options for saving changes.</param>
        /// <param name="context">The DataServiceContext instance which is calling SaveChanges.</param>
        /// <returns><see cref="DataServiceResponseData"/> that expresses expectations for the response.</returns>
        public DataServiceResponseData CalculateSaveChangesResponseData(DataServiceContextData dataBeforeSaveChanges, SaveChangesOptions options, DSClient.DataServiceContext context)
        {
            ExceptionUtilities.CheckArgumentNotNull(dataBeforeSaveChanges, "dataBeforeSaveChanges");
            ExceptionUtilities.CheckArgumentNotNull(context, "context");

            DataServiceResponseData responseData = new DataServiceResponseData();

            responseData.IsBatchResponse = options == SaveChangesOptions.Batch;

            bool hasChanges = false;

            // Note: ordering is important as changes should be processed in the order specified by user.
            foreach (DescriptorData descriptorData in dataBeforeSaveChanges.GetOrderedChanges())
            {
                int statusCode = (int)HttpStatusCode.NoContent;

                var entityDescriptorData = descriptorData as EntityDescriptorData;
                if (entityDescriptorData != null)
                {
                    if (entityDescriptorData.IsMediaLinkEntry && entityDescriptorData.DefaultStreamState == EntityStates.Modified)
                    {
                        responseData.Add(new ChangeOperationResponseData(descriptorData)
                        {
                            StatusCode = statusCode
                        });
                    }

                    if (descriptorData.State == EntityStates.Added)
                    {
                        statusCode = GetStatusCodeForInsert(context);

                        if (entityDescriptorData.IsMediaLinkEntry)
                        {
                            responseData.Add(new ChangeOperationResponseData(descriptorData)
                            {
                                StatusCode = statusCode
                            });
                            statusCode = GetStatusCodeForUpdate(context);
                        }
                    }
                    else if (descriptorData.State == EntityStates.Modified)
                    {
                        statusCode = GetStatusCodeForUpdate(context);
                    }
                    else if (descriptorData.State != EntityStates.Deleted)
                    {
                        continue;
                    }
                }

                var linkDescriptorData = descriptorData as LinkDescriptorData;
                if (linkDescriptorData != null && (linkDescriptorData.State == EntityStates.Added || linkDescriptorData.State == EntityStates.Modified))
                {
                    if (!linkDescriptorData.WillTriggerSeparateRequest())
                    {
                        continue;
                    }
                }

                responseData.Add(new ChangeOperationResponseData(descriptorData)
                {
                    StatusCode = statusCode
                });
                hasChanges = true;
            }

            if (responseData.IsBatchResponse)
            {
                responseData.BatchStatusCode = hasChanges ? (int)HttpStatusCode.Accepted : 0;
            }
            else
            {
                responseData.BatchStatusCode = -1;
            }

            return(responseData);
        }
            private void UpdateResponse(DataServiceResponseData responseData, DescriptorData descriptor, HttpResponseData response)
            {
                var operationResponse = new ChangeOperationResponseData(descriptor);
                operationResponse.StatusCode = (int)response.StatusCode;
                foreach (var header in response.Headers)
                {
                    operationResponse.Headers.Add(header.Key, header.Value);
                }

                responseData.Add(operationResponse);
            }
            private void HandleBatchRequest(DataServiceResponseData responseData)
            {
                this.parent.Assert.AreEqual(1, this.httpQueue.Count, "Should only observe one request in $batch scenarios");
                var batchRequest = (HttpRequestData)this.httpQueue.Peek().Key;

                var batchRequestPayload = this.parent.BatchDeserializer.DeserializeBatchRequest(batchRequest);
                this.parent.Assert.AreEqual(1, batchRequestPayload.Changesets.Count(), "Batch request payload should only have one changeset");
                this.parent.Assert.AreEqual(0, batchRequestPayload.Operations.Count(), "Batch request payload should not contain any operations");

                var expectedBatchHeaders = new HttpHeaderCollection()
                {
                    Accept = MimeTypes.MultipartMixed,
                    IfMatch = null,
                    Prefer = null,
                    HttpMethod = null,
                    DataServiceVersion = DataServiceProtocolVersion.V4.ConvertToHeaderFormat() + ";" + HttpHeaders.NetFx,
                    MaxDataServiceVersion = this.contextData.MaxProtocolVersion.ConvertToHeaderFormat() + ";" + HttpHeaders.NetFx,
                };

                this.CompareHeaders(expectedBatchHeaders, batchRequest.Headers);

                var batchResponse = this.httpQueue.Dequeue().Value;
                responseData.BatchStatusCode = (int)batchResponse.StatusCode;
                responseData.BatchHeaders.AddRange(batchResponse.Headers);

                var batchResponsePayload = this.parent.BatchDeserializer.DeserializeBatchResponse(batchRequestPayload, batchResponse);
                this.parent.Assert.AreEqual(1, batchResponsePayload.Changesets.Count(), "Batch response payload should only have one changeset");
                this.parent.Assert.AreEqual(0, batchResponsePayload.Operations.Count(), "Batch response payload should not contain any operations");

                var requestChangeset = batchRequestPayload.Changesets.Single();
                var responseChangeset = batchResponsePayload.Changesets.Single();
                this.parent.Assert.AreEqual(requestChangeset.Count(), responseChangeset.Count(), "Unexpected number of response operations in changeset");

                // TODO: report this better
                this.parent.Assert.IsTrue(requestChangeset.Operations.All(r => r is ODataRequest), "Some requests were not fully deserialized");

                this.httpQueue = new Queue<KeyValuePair<IHttpRequest, HttpResponseData>>(requestChangeset.Operations.Zip(responseChangeset.Operations, (req, res) => new KeyValuePair<IHttpRequest, HttpResponseData>(req, res)));
            }
            /// <summary>
            /// Runs the save changes pipeline
            /// </summary>
            /// <returns>The response data to expect</returns>
            public DataServiceResponseData Run()
            {
                var responseData = new DataServiceResponseData();

                bool isBatch = responseData.IsBatchResponse = this.options == SaveChangesOptions.Batch;
                if (!isBatch)
                {
                    responseData.BatchStatusCode = -1;
                }

                // add the default stream descriptors into the ordered changes immediately before the entities they go with
                var orderedChanges = this.contextData.GetOrderedChanges()
                    .SelectMany(
                        d =>
                        {
                            var entityDescriptor = d as EntityDescriptorData;
                            if (entityDescriptor == null || !entityDescriptor.IsMediaLinkEntry)
                            {
                                return new[] { d };
                            }

                            return new DescriptorData[] { entityDescriptor.DefaultStreamDescriptor, entityDescriptor }
                                .Where(d2 => d2.State != EntityStates.Unchanged);
                        })
                    .ToList();

                if (orderedChanges.Count == 0)
                {
                    this.parent.Assert.AreEqual(0, this.httpQueue.Count, "No requests should have been sent");
                    return responseData;
                }
                
                if (isBatch)
                {
                    this.HandleBatchRequest(responseData);
                }
                
                var pendingUpdates = new List<Action>();
                foreach (var change in orderedChanges)
                {
                    var descriptor = change;
                    var expectedRequest = this.parent.RequestCalculator.CalculateRequest(this.contextData, this.propertyValuesBeforeSave, descriptor, this.options);

                    if (expectedRequest != null)
                    {
                        this.parent.Assert.AreNotEqual(0, this.httpQueue.Count, "Fewer requests were made than expected");
                        var pair = this.httpQueue.Dequeue();

                        var streamDescriptor = descriptor as StreamDescriptorData;
                        if (streamDescriptor != null && streamDescriptor.Name == null)
                        {
                            descriptor = streamDescriptor.EntityDescriptor;
                        }

                        SetContentIDHeader(descriptor, expectedRequest, isBatch);

                        bool tunnelling = this.contextData.UsePostTunneling;
                        if (isBatch || expectedRequest.Verb == HttpVerb.Post)
                        {
                            tunnelling = false;
                        }

                        SetVerbTunnelling(expectedRequest, tunnelling);

                        this.CompareRequest(expectedRequest, pair.Key);

                        this.UpdateResponse(responseData, descriptor, pair.Value);

                        if (!isBatch)
                        {
                            this.ApplyResponseAndUpdateState(descriptor, pair);
                        }
                        else
                        {
                            pendingUpdates.Add(() => this.ApplyResponseAndUpdateState(descriptor, pair));
                        }
                    }
                }

                pendingUpdates.ForEach(a => a());

                this.parent.Assert.AreEqual(0, this.httpQueue.Count, "More requests were made than expected");
                
                return responseData;
            }
        /// <summary>
        /// Calculates the expected response from DataServiceContext.SaveChanges based on the context data before saving changes.
        /// Assumes there are no errors in the response.
        /// </summary>
        /// <param name="dataBeforeSaveChanges">The data before saving changes.</param>
        /// <param name="options">The options for saving changes.</param>
        /// <param name="context">The DataServiceContext instance which is calling SaveChanges.</param>
        /// <returns><see cref="DataServiceResponseData"/> that expresses expectations for the response.</returns>
        public DataServiceResponseData CalculateSaveChangesResponseData(DataServiceContextData dataBeforeSaveChanges, SaveChangesOptions options, DSClient.DataServiceContext context)
        {
            ExceptionUtilities.CheckArgumentNotNull(dataBeforeSaveChanges, "dataBeforeSaveChanges");
            ExceptionUtilities.CheckArgumentNotNull(context, "context");

            DataServiceResponseData responseData = new DataServiceResponseData();

            responseData.IsBatchResponse = options == SaveChangesOptions.Batch;

            bool hasChanges = false;

            // Note: ordering is important as changes should be processed in the order specified by user.
            foreach (DescriptorData descriptorData in dataBeforeSaveChanges.GetOrderedChanges())
            {
                int statusCode = (int)HttpStatusCode.NoContent;

                var entityDescriptorData = descriptorData as EntityDescriptorData;
                if (entityDescriptorData != null)
                {
                    if (entityDescriptorData.IsMediaLinkEntry && entityDescriptorData.DefaultStreamState == EntityStates.Modified)
                    {
                        responseData.Add(new ChangeOperationResponseData(descriptorData) { StatusCode = statusCode });
                    }

                    if (descriptorData.State == EntityStates.Added)
                    {
                        statusCode = GetStatusCodeForInsert(context);

                        if (entityDescriptorData.IsMediaLinkEntry)
                        {
                            responseData.Add(new ChangeOperationResponseData(descriptorData) { StatusCode = statusCode });
                            statusCode = GetStatusCodeForUpdate(context);
                        }
                    }
                    else if (descriptorData.State == EntityStates.Modified)
                    {
                        statusCode = GetStatusCodeForUpdate(context);
                    }
                    else if (descriptorData.State != EntityStates.Deleted)
                    {
                        continue;
                    }
                }

                var linkDescriptorData = descriptorData as LinkDescriptorData;
                if (linkDescriptorData != null && (linkDescriptorData.State == EntityStates.Added || linkDescriptorData.State == EntityStates.Modified))
                {
                    if (!linkDescriptorData.WillTriggerSeparateRequest())
                    {
                        continue;
                    }
                }

                responseData.Add(new ChangeOperationResponseData(descriptorData) { StatusCode = statusCode });
                hasChanges = true;
            }

            if (responseData.IsBatchResponse)
            {
                responseData.BatchStatusCode = hasChanges ? (int)HttpStatusCode.Accepted : 0;
            }
            else
            {
                responseData.BatchStatusCode = -1;
            }

            return responseData;
        }
Esempio n. 10
0
            /// <summary>
            /// Runs the save changes pipeline
            /// </summary>
            /// <returns>The response data to expect</returns>
            public DataServiceResponseData Run()
            {
                var responseData = new DataServiceResponseData();

                bool isBatch = responseData.IsBatchResponse = this.options == SaveChangesOptions.Batch;

                if (!isBatch)
                {
                    responseData.BatchStatusCode = -1;
                }

                // add the default stream descriptors into the ordered changes immediately before the entities they go with
                var orderedChanges = this.contextData.GetOrderedChanges()
                                     .SelectMany(
                    d =>
                {
                    var entityDescriptor = d as EntityDescriptorData;
                    if (entityDescriptor == null || !entityDescriptor.IsMediaLinkEntry)
                    {
                        return(new[] { d });
                    }

                    return(new DescriptorData[] { entityDescriptor.DefaultStreamDescriptor, entityDescriptor }
                           .Where(d2 => d2.State != EntityStates.Unchanged));
                })
                                     .ToList();

                if (orderedChanges.Count == 0)
                {
                    this.parent.Assert.AreEqual(0, this.httpQueue.Count, "No requests should have been sent");
                    return(responseData);
                }

                if (isBatch)
                {
                    this.HandleBatchRequest(responseData);
                }

                var pendingUpdates = new List <Action>();

                foreach (var change in orderedChanges)
                {
                    var descriptor      = change;
                    var expectedRequest = this.parent.RequestCalculator.CalculateRequest(this.contextData, this.propertyValuesBeforeSave, descriptor, this.options);

                    if (expectedRequest != null)
                    {
                        this.parent.Assert.AreNotEqual(0, this.httpQueue.Count, "Fewer requests were made than expected");
                        var pair = this.httpQueue.Dequeue();

                        var streamDescriptor = descriptor as StreamDescriptorData;
                        if (streamDescriptor != null && streamDescriptor.Name == null)
                        {
                            descriptor = streamDescriptor.EntityDescriptor;
                        }

                        SetContentIDHeader(descriptor, expectedRequest, isBatch);

                        bool tunnelling = this.contextData.UsePostTunneling;
                        if (isBatch || expectedRequest.Verb == HttpVerb.Post)
                        {
                            tunnelling = false;
                        }

                        SetVerbTunnelling(expectedRequest, tunnelling);

                        this.CompareRequest(expectedRequest, pair.Key);

                        this.UpdateResponse(responseData, descriptor, pair.Value);

                        if (!isBatch)
                        {
                            this.ApplyResponseAndUpdateState(descriptor, pair);
                        }
                        else
                        {
                            pendingUpdates.Add(() => this.ApplyResponseAndUpdateState(descriptor, pair));
                        }
                    }
                }

                pendingUpdates.ForEach(a => a());

                this.parent.Assert.AreEqual(0, this.httpQueue.Count, "More requests were made than expected");

                return(responseData);
            }