Repository: googleapis/java-storage
Pull request #2124 (2023-07-17 21:59:47)

test(deps): update cross product test dependencies
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.cloud:google-cloud-pubsub](https://togithub.com/googleapis/java-pubsub) | `1.123.17` -> `1.123.18` | [![age](https://developer.mend.io/api/mc/badges/age/maven/com.google.cloud:google-cloud-pubsub/1.123.18?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/com.google.cloud:google-cloud-pubsub/1.123.18?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/com.google.cloud:google-cloud-pubsub/1.123.17/1.123.18?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/com.google.cloud:google-cloud-pubsub/1.123.17/1.123.18?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [com.google.api.grpc:proto-google-cloud-pubsub-v1](https://togithub.com/googleapis/java-pubsub/proto-google-cloud-pubsub-v1) ([source](https://togithub.com/googleapis/java-pubsub)) | `1.105.17` -> `1.105.18` | [![age](https://developer.mend.io/api/mc/badges/age/maven/com.google.api.grpc:proto-google-cloud-pubsub-v1/1.105.18?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/com.google.api.grpc:proto-google-cloud-pubsub-v1/1.105.18?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/com.google.api.grpc:proto-google-cloud-pubsub-v1/1.105.17/1.105.18?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/com.google.api.grpc:proto-google-cloud-pubsub-v1/1.105.17/1.105.18?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>googleapis/java-pubsub (com.google.cloud:google-cloud-pubsub)</summary>

### [`v1.123.18`](https://togithub.com/googleapis/java-pubsub/blob/HEAD/CHANGELOG.md#112318-2023-07-17)

[Compare Source](https://togithub.com/googleapis/java-pubsub/compare/v1.123.17...v1.123.18)

##### Documentation

-   Tightened requirements on cloud storage subscription filename suffixes ([#&#8203;1639](https://togithub.com/googleapis/java-pubsub/issues/1639)) ([34a182a](https://togithub.com/googleapis/java-pubsub/commit/34a182a1655c843cd728850b278ba2e9ce9a5680))

##### Dependencies

-   Update dependency com.google.cloud:google-cloud-core to v2.21.0 ([#&#8203;1650](https://togithub.com/googleapis/java-pubsub/issues/1650)) ([69879d4](https://togithub.com/googleapis/java-pubsub/commit/69879d4c9d7a220f9c5a38b144d49e71ad3b94d0))
-   Update dependency com.google.cloud:google-cloud-shared-dependencies to v3.13.0 ([#&#8203;1651](https://togithub.com/googleapis/java-pubsub/issues/1651)) ([83fdbe2](https://togithub.com/googleapis/java-pubsub/commit/83fdbe2435d8fa03b5648272a794874ff910fcb6))
-   Update dependency com.google.protobuf:protobuf-java-util to v3.23.4 ([#&#8203;1649](https://togithub.com/googleapis/java-pubsub/issues/1649)) ([9f9c5ca](https://togithub.com/googleapis/java-pubsub/commit/9f9c5ca2abddde274c79cf36bf2aea91b7b8b455))
-   Update dependency org.apache.avro:avro to v1.11.2 ([#&#8203;1646](https://togithub.com/googleapis/java-pubsub/issues/1646)) ([5859fe4](https://togithub.com/googleapis/java-pubsub/commit/5859fe4c6540fa577942f38e117798d3c3cdc498))
-   Update dependency org.xerial.snappy:snappy-java to v1.1.10.2 ([#&#8203;1654](https://togithub.com/googleapis/java-pubsub/issues/1654)) ([05445f0](https://togithub.com/googleapis/java-pubsub/commit/05445f0ccf1cd78a604ec267d7a146cdd1770c59))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/googleapis/java-storage).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNi44LjExIiwidXBkYXRlZEluVmVyIjoiMzYuOC4xMSIsInRhcmdldEJyYW5jaCI6Im1haW4ifQ==-->



Patch:
diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml
index d85094f2..b7d53b47 100644
--- a/google-cloud-storage/pom.xml
+++ b/google-cloud-storage/pom.xml
@@ -16,7 +16,7 @@
   </parent>
   <properties>
     <site.installationModule>google-cloud-storage</site.installationModule>
-    <pubsub-proto.version>1.105.17</pubsub-proto.version>
+    <pubsub-proto.version>1.105.18</pubsub-proto.version>
     <junit-platform.version>5.9.3</junit-platform.version>
   </properties>
   <dependencies>
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryUnbufferedWritableByteChannel.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryUnbufferedWritableByteChannel.java
index 35ad97ff..8201fbc6 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryUnbufferedWritableByteChannel.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryUnbufferedWritableByteChannel.java
@@ -36,9 +36,9 @@ final class ApiaryUnbufferedWritableByteChannel implements UnbufferedWritableByt
   private final SettableApiFuture<StorageObject> result;
   private final LongConsumer committedBytesCallback;
 
-  private boolean open = true;
+  private boolean open;
   private long cumulativeByteCount;
-  private boolean finished = false;
+  private boolean finished;
 
   ApiaryUnbufferedWritableByteChannel(
       HttpClientContext httpClientContext,
@@ -50,6 +50,9 @@ final class ApiaryUnbufferedWritableByteChannel implements UnbufferedWritableByt
     this.session = ResumableSession.json(httpClientContext, deps, alg, resumableWrite);
     this.result = result;
     this.committedBytesCallback = committedBytesCallback;
+    this.open = true;
+    this.cumulativeByteCount = resumableWrite.getBeginOffset();
+    this.finished = false;
   }
 
   @Override
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteChannel.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteChannel.java
index 28c4b8d1..c74c94f4 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteChannel.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteChannel.java
@@ -82,7 +82,7 @@ class BlobWriteChannel extends BaseWriteChannel<StorageOptions, BlobInfo>
             entity != null ? Conversions.apiary().blobInfo().encode(entity) : null;
         return new BlobWriteChannelV2.BlobWriteChannelV2State(
                 (HttpStorageOptions) serviceOptions,
-                JsonResumableWrite.of(encode, ImmutableMap.of(), uploadId),
+                JsonResumableWrite.of(encode, ImmutableMap.of(), uploadId, position),
                 position,
                 isOpen,
                 chunkSize,
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteChannelV2.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteChannelV2.java
index 1bb07425..48bad4de 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteChannelV2.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BlobWriteChannelV2.java
@@ -107,6 +107,10 @@ final class BlobWriteChannelV2 extends BaseStorageWriteChannel<StorageObject> {
 
     @Override
     public WriteChannel restore() {
+      JsonResumableWrite resumableWrite = this.resumableWrite;
+      if (position != null) {
+        resumableWrite = resumableWrite.withBeginOffset(position);
+      }
       BlobWriteChannelV2 channel =
           new BlobWriteChannelV2(BlobReadChannelContext.from(options), resumableWrite);
       if (chunkSize != null) {
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java
index 5abb43c1..5a486499 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSessionPutTask.java
@@ -153,7 +153,7 @@ final class JsonResumableSessionPutTask
           success = true;
           //noinspection DataFlowIssue  compareTo result will filter out actualSize == null
           return ResumableOperationResult.complete(storageObject, actualSize.longValue());
-        } else if (compare < 0) {
+        } else if (compare > 0) {
           StorageException se =
               JsonResumableSessionFailureScenario.SCENARIO_4_1.toStorageException(
                   uploadId, response, null, toString(storageObject));
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableWrite.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableWrite.java
index 4ec1fc89..336ce0e4 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableWrite.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableWrite.java
@@ -16,6 +16,8 @@
 
 package com.google.cloud.storage;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 import com.google.api.services.storage.model.StorageObject;
 import com.google.cloud.storage.spi.v1.StorageRpc;
 import com.google.common.base.MoreObjects;
@@ -41,6 +43,7 @@ final class JsonResumableWrite implements Serializable {
   @MonotonicNonNull private final String signedUrl;
 
   @NonNull private final String uploadId;
+  private final long beginOffset;
 
   private volatile String objectJson;
 
@@ -48,17 +51,32 @@ final class JsonResumableWrite implements Serializable {
       StorageObject object,
       Map<StorageRpc.Option, ?> options,
       String signedUrl,
-      @NonNull String uploadId) {
+      @NonNull String uploadId,
+      long beginOffset) {
     this.object = object;
     this.options = options;
     this.signedUrl = signedUrl;
     this.uploadId = uploadId;
+    this.beginOffset = beginOffset;
   }
 
   public @NonNull String getUploadId() {
     return uploadId;
   }
 
+  public long getBeginOffset() {
+    return beginOffset;
+  }
+
+  public JsonResumableWrite withBeginOffset(long newBeginOffset) {
+    checkArgument(
+        newBeginOffset >= beginOffset,
+        "New beginOffset must be >= existing beginOffset (%s >= %s)",
+        newBeginOffset,
+        beginOffset);
+    return new JsonResumableWrite(object, options, signedUrl, uploadId, newBeginOffset);
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) {
@@ -68,15 +86,16 @@ final class JsonResumableWrite implements Serializable {
       return false;
     }
     JsonResumableWrite that = (JsonResumableWrite) o;
-    return Objects.equals(object, that.object)
+    return beginOffset == that.beginOffset
+        && Objects.equals(object, that.object)
         && Objects.equals(options, that.options)
         && Objects.equals(signedUrl, that.signedUrl)
-        && uploadId.equals(that.uploadId);
+        && Objects.equals(uploadId, that.uploadId);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(object, options, signedUrl, uploadId);
+    return Objects.hash(object, options, signedUrl, uploadId, beginOffset);
   }
 
   @Override
@@ -86,6 +105,7 @@ final class JsonResumableWrite implements Serializable {
         .add("options", options)
         .add("signedUrl", signedUrl)
         .add("uploadId", uploadId)
+        .add("beginOffset", beginOffset)
         .toString();
   }
 
@@ -112,11 +132,11 @@ final class JsonResumableWrite implements Serializable {
   }
 
   static JsonResumableWrite of(
-      StorageObject req, Map<StorageRpc.Option, ?> options, String uploadId) {
-    return new JsonResumableWrite(req, options, null, uploadId);
+      StorageObject req, Map<StorageRpc.Option, ?> options, String uploadId, long beginOffset) {
+    return new JsonResumableWrite(req, options, null, uploadId, beginOffset);
   }
 
-  static JsonResumableWrite of(String signedUrl, String uploadId) {
-    return new JsonResumableWrite(null, null, signedUrl, uploadId);
+  static JsonResumableWrite of(String signedUrl, String uploadId, long beginOffset) {
+    return new JsonResumableWrite(null, null, signedUrl, uploadId, beginOffset);
   }
 }
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
index fd387717..d4ad2601 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
@@ -243,7 +243,7 @@ final class StorageImpl extends BaseService<StorageOptions> implements Storage {
             optionsMap,
             retryAlgorithmManager.getForResumableUploadSessionCreate(optionsMap));
     JsonResumableWrite jsonResumableWrite =
-        JsonResumableWrite.of(encode, optionsMap, uploadIdSupplier.get());
+        JsonResumableWrite.of(encode, optionsMap, uploadIdSupplier.get(), 0);
 
     JsonResumableSession session =
         ResumableSession.json(
@@ -671,7 +671,7 @@ final class StorageImpl extends BaseService<StorageOptions> implements Storage {
             optionsMap,
             retryAlgorithmManager.getForResumableUploadSessionCreate(optionsMap));
     JsonResumableWrite jsonResumableWrite =
-        JsonResumableWrite.of(encode, optionsMap, uploadIdSupplier.get());
+        JsonResumableWrite.of(encode, optionsMap, uploadIdSupplier.get(), 0);
     return new BlobWriteChannelV2(BlobReadChannelContext.from(getOptions()), jsonResumableWrite);
   }
 
@@ -688,7 +688,7 @@ final class StorageImpl extends BaseService<StorageOptions> implements Storage {
         ResumableMedia.startUploadForSignedUrl(
             getOptions(), signedURL, forResumableUploadSessionCreate);
     JsonResumableWrite jsonResumableWrite =
-        JsonResumableWrite.of(signedUrlString, uploadIdSupplier.get());
+        JsonResumableWrite.of(signedUrlString, uploadIdSupplier.get(), 0);
     return new BlobWriteChannelV2(BlobReadChannelContext.from(getOptions()), jsonResumableWrite);
   }
 
diff --git a/pom.xml b/pom.xml
index af3f0ffd..61e942ee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,7 +93,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-pubsub</artifactId>
-        <version>1.123.17</version>
+        <version>1.123.18</version>
         <scope>test</scope>
       </dependency>
       <dependency>
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index ee717f25..7b16d416 100644
--- a/samples/install-without-bom/pom.xml
+++ b/samples/install-without-bom/pom.xml
@@ -61,7 +61,7 @@
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-pubsub</artifactId>
-      <version>1.123.17</version>
+      <version>1.123.18</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
diff --git a/samples/native-image-sample/pom.xml b/samples/native-image-sample/pom.xml
index 841e4a18..80a4593b 100644
--- a/samples/native-image-sample/pom.xml
+++ b/samples/native-image-sample/pom.xml
@@ -61,7 +61,7 @@
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-pubsub</artifactId>
-      <version>1.123.17</version>
+      <version>1.123.18</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 3723a7f2..7b23b225 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -52,7 +52,7 @@
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-pubsub</artifactId>
-      <version>1.123.17</version>
+      <version>1.123.18</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 3f47056d..becb9489 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -72,7 +72,7 @@
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-pubsub</artifactId>
-      <version>1.123.17</version>
+      <version>1.123.18</version>
       <scope>test</scope>
     </dependency>
   </dependencies>


Test patch:
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/ITJsonResumableSessionTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/ITJsonResumableSessionTest.java
index 108e6d6d..7336749d 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/ITJsonResumableSessionTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/ITJsonResumableSessionTest.java
@@ -113,7 +113,8 @@ public final class ITJsonResumableSessionTest {
       URI endpoint = fakeHttpServer.getEndpoint();
       String uploadUrl = String.format("%s/upload/%s", endpoint.toString(), UUID.randomUUID());
 
-      JsonResumableWrite resumableWrite = JsonResumableWrite.of(null, ImmutableMap.of(), uploadUrl);
+      JsonResumableWrite resumableWrite =
+          JsonResumableWrite.of(null, ImmutableMap.of(), uploadUrl, 0);
       JsonResumableSession session =
           new JsonResumableSession(
               httpClientContext, RETRYING_DEPENDENCIES, RETRY_ALGORITHM, resumableWrite);
@@ -167,7 +168,8 @@ public final class ITJsonResumableSessionTest {
       URI endpoint = fakeHttpServer.getEndpoint();
       String uploadUrl = String.format("%s/upload/%s", endpoint.toString(), UUID.randomUUID());
 
-      JsonResumableWrite resumableWrite = JsonResumableWrite.of(null, ImmutableMap.of(), uploadUrl);
+      JsonResumableWrite resumableWrite =
+          JsonResumableWrite.of(null, ImmutableMap.of(), uploadUrl, 0);
       JsonResumableSession session =
           new JsonResumableSession(
               httpClientContext, RETRYING_DEPENDENCIES, RETRY_ALGORITHM, resumableWrite);
@@ -234,7 +236,8 @@ public final class ITJsonResumableSessionTest {
       URI endpoint = fakeHttpServer.getEndpoint();
       String uploadUrl = String.format("%s/upload/%s", endpoint.toString(), UUID.randomUUID());
 
-      JsonResumableWrite resumableWrite = JsonResumableWrite.of(null, ImmutableMap.of(), uploadUrl);
+      JsonResumableWrite resumableWrite =
+          JsonResumableWrite.of(null, ImmutableMap.of(), uploadUrl, 0);
       JsonResumableSession session =
           new JsonResumableSession(
               httpClientContext, RETRYING_DEPENDENCIES, RETRY_ALGORITHM, resumableWrite);
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/SerializationTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/SerializationTest.java
index 1e5eda2d..87b88a78 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/SerializationTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/SerializationTest.java
@@ -216,7 +216,8 @@ public class SerializationTest extends BaseSerializationTest {
             JsonResumableWrite.of(
                 Conversions.apiary().blobInfo().encode(BlobInfo.newBuilder("b", "n").build()),
                 ImmutableMap.of(),
-                "upload-id"));
+                "upload-id",
+                0));
     return new Restorable<?>[] {readerV2, writer};
   }
 
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteChannelTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteChannelTest.java
index 9b4ab366..338f2d9b 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteChannelTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobWriteChannelTest.java
@@ -16,6 +16,7 @@
 
 package com.google.cloud.storage.it;
 
+import static com.google.cloud.storage.TestUtils.xxd;
 import static com.google.common.truth.Truth.assertThat;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertArrayEquals;
@@ -27,6 +28,7 @@ import static org.junit.Assert.assertTrue;
 import com.google.api.client.json.JsonParser;
 import com.google.api.gax.rpc.FixedHeaderProvider;
 import com.google.cloud.NoCredentials;
+import com.google.cloud.RestorableState;
 import com.google.cloud.WriteChannel;
 import com.google.cloud.conformance.storage.v1.InstructionList;
 import com.google.cloud.conformance.storage.v1.Method;
@@ -53,6 +55,7 @@ import com.google.common.collect.ImmutableMap;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.Optional;
 import java.util.logging.Logger;
 import org.junit.Test;
@@ -153,6 +156,39 @@ public final class ITBlobWriteChannelTest {
     }
   }
 
+  @Test
+  public void restoreProperlyPlumbsBeginOffset() throws IOException {
+    BlobInfo info = BlobInfo.newBuilder(bucket, generator.randomObjectName()).build();
+    int _256KiB = 256 * 1024;
+
+    byte[] bytes1 = DataGenerator.base64Characters().genBytes(_256KiB);
+    byte[] bytes2 = DataGenerator.base64Characters().genBytes(73);
+
+    int allLength = bytes1.length + bytes2.length;
+    byte[] expected = Arrays.copyOf(bytes1, allLength);
+    System.arraycopy(bytes2, 0, expected, bytes1.length, bytes2.length);
+    String xxdExpected = xxd(expected);
+
+    RestorableState<WriteChannel> capture;
+    {
+      WriteChannel writer = storage.writer(info, BlobWriteOption.doesNotExist());
+      writer.setChunkSize(_256KiB);
+      writer.write(ByteBuffer.wrap(bytes1));
+      // explicitly do not close writer, it will finalize the session
+      capture = writer.capture();
+    }
+
+    assertThat(capture).isNotNull();
+    WriteChannel restored = capture.restore();
+    restored.write(ByteBuffer.wrap(bytes2));
+    restored.close();
+
+    byte[] readAllBytes = storage.readAllBytes(info.getBlobId());
+    assertThat(readAllBytes).hasLength(expected.length);
+    String xxdActual = xxd(readAllBytes);
+    assertThat(xxdActual).isEqualTo(xxdExpected);
+  }
+
   private void doJsonUnexpectedEOFTest(int contentSize, int cappedByteCount) throws IOException {
     String blobPath = String.format("%s/%s/blob", generator.randomObjectName(), NOW_STRING);