public void BlobStreamSaveChanges() { using (TestWebRequest request = TestWebRequest.CreateForInProcessWcf()) using (PlaybackService.InspectRequestPayload.Restore()) using (PlaybackService.OverridingPlayback.Restore()) { request.ServiceType = typeof(PlaybackService); request.ForceVerboseErrors = true; request.StartService(); byte[] content = new byte[] { 1, 2, 3 }; DataServiceContext ctx = new DataServiceContext(new Uri(request.BaseUri, UriKind.RelativeOrAbsolute)); ctx.EnableAtom = true; ctx.Format.UseAtom(); Stream stream = new MemoryStream(content); ClientCSharpRegressionTests.CustomerWithStream customer = new ClientCSharpRegressionTests.CustomerWithStream() { ID = 1, Name = "Foo" }; ctx.AddObject("Customers", customer); ctx.SetSaveStream(customer, stream, true, new DataServiceRequestArgs() { ContentType = "application/jpeg" }); EntityDescriptor ed = ctx.Entities[0]; int i = 0; string id = "http://myidhost/somerandomIDUrl"; string selfLink = request.BaseUri + "/self-link/Customer(1)"; string editLink = request.BaseUri + "/edit-link/Customer(1)"; string etag = "someetagvalue"; string serverTypeName = "SomeRandomTypeName"; string editMediaLink = "http://myhost/somerandomeUrl/edit-media-link/v1"; string readMediaLink = "http://myedithost/somerandomUrl/foo/self-media-link/v1"; string streamETag = "somerandomeStreamETag"; string mediaType = "application/jpeg"; string locationHeader = "http://mylocationhost/somerandomUrl/location/"; PlaybackService.InspectRequestPayload.Value = (message) => { if (i == 0) { string xml = MediaEntry( id: id, selfLink: selfLink, editLink: editLink, etag: etag, properties: "<d:ID>5</d:ID>", serverTypeName: serverTypeName, readStreamUrl: readMediaLink, editStreamUrl: editMediaLink, mediaETag: streamETag, mediaType: mediaType); // The response payload has some random location header and no etag header. // Verify that the edit link, etag and type name are used from the payload. string responsePayload = "HTTP/1.1 201 Created" + Environment.NewLine + "Content-Type: application/atom+xml" + Environment.NewLine + "Location: " + locationHeader + Environment.NewLine + Environment.NewLine + xml; PlaybackService.OverridingPlayback.Value = responsePayload; } else if (i == 1) { // Make sure that no metadata from the payload is merged yet into the public entity descriptor // In V2, we already set the edit link and id to the location header. So that part cannot be changed now. Assert.AreEqual(ed.Identity, locationHeader, "id must be set to location header"); Assert.AreEqual(ed.EditLink.AbsoluteUri, locationHeader, "edit link value must be equal to the location header"); Assert.AreEqual(ed.SelfLink, null, "Self link must not be populated"); Assert.AreEqual(ed.ETag, null, "etag must be null"); Assert.AreEqual(ed.ServerTypeName, null, "server type name must be null"); Assert.AreEqual(ed.EditStreamUri, null, "edit media stream must be null"); Assert.AreEqual(ed.ReadStreamUri, null, "read media stream must be null"); Assert.AreEqual(ed.StreamETag, null, "stream etag must be null"); // no response for PUT string responsePayload = "HTTP/1.1 200 OK" + Environment.NewLine + "Content-Type: application/atom+xml" + Environment.NewLine + Environment.NewLine; PlaybackService.OverridingPlayback.Value = responsePayload; } i++; }; ctx.SaveChanges(); Assert.IsTrue(i == 2, "only 2 requests should be sent to the server)"); // Make sure the edit link from the payload is used, not the location header Assert.IsTrue(PlaybackService.LastPlayback.Contains("PATCH " + editLink), "should use edit link from the POST response payload"); // make sure the etag is used from the payload Assert.IsTrue(PlaybackService.LastPlayback.Contains("If-Match: W/\"" + etag + "\""), "should use the etag value from the POST response payload"); // make sure the type is used from the payload Assert.IsTrue(PlaybackService.LastPlayback.Contains("category term=\"#" + serverTypeName + "\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\""), "should use the server type name as specified in the POST response payload"); Assert.AreEqual(ed.Identity, id, "id must be set to location header"); Assert.AreEqual(ed.EditLink.AbsoluteUri, editLink, "edit link value must be equal to the location header"); Assert.AreEqual(ed.SelfLink, selfLink, "Self link should be populated"); Assert.AreEqual(ed.ETag, null, "etag must be null, since the PATCH response didn't send any etag in the response header"); Assert.AreEqual(ed.ServerTypeName, serverTypeName, "server type name must NOT be null"); Assert.AreEqual(ed.EditStreamUri, editMediaLink, "edit media stream must NOT be null"); Assert.AreEqual(ed.ReadStreamUri, readMediaLink, "read media stream must NOT be null"); Assert.AreEqual(ed.StreamETag, streamETag, "stream etag must NOT be null"); } }
public void InsertMLEAndSetSaveStream() { // Making sure that inserting an MLE/MR followed by SetSaveStream works well TestUtil.RunCombinations( (IEnumerable<SaveChangesMode>)Enum.GetValues(typeof(SaveChangesMode)), UnitTestsUtil.BooleanValues, (mode, closeStream) => { using (PlaybackService.OverridingPlayback.Restore()) using (PlaybackService.InspectRequestPayload.Restore()) { DataServiceContext context = new DataServiceContext(request.ServiceRoot, ODataProtocolVersion.V4); context.EnableAtom = true; context.Format.UseAtom(); ClientCSharpRegressionTests.CustomerWithStream c = new ClientCSharpRegressionTests.CustomerWithStream() { ID = 1, Name = "Foo" }; context.AddObject("Customers", c); MemoryStream defaultStream = new MemoryStream(new byte[] { 0, 1, 2, 3 }); MemoryStream thumbnailStream = new MemoryStream(new byte[] { 0, 1, 2, 3, 4 }); context.SetSaveStream(c, defaultStream, closeStream, new DataServiceRequestArgs() { ContentType = "image/bmp" }); context.SetSaveStream(c, "Thumbnail", thumbnailStream, closeStream, new DataServiceRequestArgs() { ContentType = "image/bmp" }); // verify the entity descriptor state EntityDescriptor entityDescriptor = context.Entities.Single(); StreamDescriptor streamInfo = entityDescriptor.StreamDescriptors.Single(); Assert.IsTrue(entityDescriptor.State == EntityStates.Added, "entity must be in added state"); Assert.AreEqual(streamInfo.StreamLink.Name, "Thumbnail"); Assert.IsTrue(streamInfo.State == EntityStates.Modified, "named stream must be in modified state"); string editLink = request.ServiceRoot.AbsoluteUri + "/editLink/Customers(1)"; int i = 0; PlaybackService.InspectRequestPayload.Value = (message) => { if (i == 2) { // Verify that the second request was a PUT request Assert.IsTrue(PlaybackService.LastPlayback.Contains(String.Format("PATCH {0}", editLink)), "the second request must be a PATCH request with the edit link"); Assert.AreEqual(!defaultStream.CanWrite, closeStream, "The default stream must have been in the desired state"); } else if (i == 1) { // Verify the first request was a POST request Assert.IsTrue(PlaybackService.LastPlayback.Contains(String.Format("POST {0}/Customers", request.ServiceRoot)), "the first request must be a POST request"); PlaybackService.OverridingPlayback.Value = PlaybackService.ConvertToPlaybackServicePayload(null, null); XDocument document = XDocument.Load(message); Assert.IsTrue(document.Element(AstoriaUnitTests.Tests.UnitTestsUtil.AtomNamespace + "entry") != null, "must contain an entry element"); Assert.AreEqual(!defaultStream.CanWrite, closeStream, "The default stream must have been in the desired state immd after the request"); } else if (i == 0) { // Populate the context with a single customer instance string payload = AtomParserTests.MediaEntry( id: Id, editLink: editLink, properties: Properties, readStreamUrl: request.ServiceRoot + "/Customers(1)/readStreamUrl/$value", links: GetNamedStreamSelfLink(request.ServiceRoot + "/Customers(1)/SelfLink/Thumbnail", contentType: MediaContentType) + GetNamedStreamEditLink(request.ServiceRoot.AbsoluteUri + "/Customers(1)/EditLink/Thumbnail", contentType: MediaContentType, etag: MediaETag)); var headers = new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("Location", "http://locationservice/locationheader") }; PlaybackService.OverridingPlayback.Value = PlaybackService.ConvertToPlaybackServicePayload(headers, payload); // entity state should not be modified until all the responses have been changed Assert.IsTrue(entityDescriptor.State == EntityStates.Modified, "entity must be in added state"); Assert.IsTrue(defaultStream.CanWrite, "The default stream hasn't been closed yet"); } Assert.AreEqual(streamInfo.StreamLink.Name, "Thumbnail"); Assert.IsTrue(streamInfo.State == EntityStates.Modified, "named stream must be in modified state"); // Also the stream info links should not be modified Assert.IsTrue(streamInfo.StreamLink.SelfLink == null, "descriptor should not have been modified yet - self link must be null"); Assert.IsTrue(streamInfo.StreamLink.EditLink == null, "descriptor should not have been modified yet - edit link must be null"); Assert.IsTrue(String.IsNullOrEmpty(streamInfo.StreamLink.ContentType), "descriptor should not have been modified yet - content type must be null"); Assert.IsTrue(String.IsNullOrEmpty(streamInfo.StreamLink.ETag), "descriptor should not have been modified yet - etag must be null"); Assert.IsTrue(thumbnailStream.CanWrite, "The thumbnail stream hasn't been closed yet"); i++; }; DataServiceContextTestUtil.SaveChanges(context, SaveChangesOptions.None, mode); Assert.AreEqual(i, 3, "Only 2 request should have been made"); // Verify that the second request was a PUT request Assert.IsTrue(PlaybackService.LastPlayback.Contains(String.Format("PUT {0}", streamInfo.StreamLink.EditLink)), "the second request must be a PUT request with the edit link"); Assert.AreEqual(streamInfo.StreamLink.SelfLink.AbsoluteUri, request.ServiceRoot + "/Customers(1)/SelfLink/Thumbnail", "self link must be null, since the payload did not have self link"); Assert.AreEqual(streamInfo.StreamLink.EditLink.AbsoluteUri, request.ServiceRoot + "/Customers(1)/EditLink/Thumbnail", "edit link should have been populated"); Assert.AreEqual(!thumbnailStream.CanWrite, closeStream, "The stream must be in the desired state"); } }); }