epugh commented on code in PR #4726:
URL: https://github.com/apache/solr/pull/4726#discussion_r3767467561


##########
solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSBackupRepositoryTest.java:
##########
@@ -77,4 +93,157 @@ public void 
testInitStoreDoesNotFailWithMissingCredentials() {
 
     gcsBackupRepository.init(new NamedList<>(config));
   }
+
+  @Test
+  public void testCopyIndexFileToPropagatesReadFailures() throws Exception {
+    Storage failingStorage = createFailingStorage();
+    GCSBackupRepository repo = createRepositoryWithStorage(failingStorage);
+
+    try (Directory dest = new ByteBuffersDirectory()) {
+      URI sourceDir = repo.resolve(getBaseUri(), "backup");
+      IOException thrown =
+          expectThrows(
+              IOException.class,
+              () -> repo.copyIndexFileTo(sourceDir, "any.dat", dest, 
"dest.dat"));
+      assertTrue(thrown.getMessage().contains("Failed to copy index file from 
GCS"));
+      assertNotNull(thrown.getCause());
+      assertTrue(thrown.getCause() instanceof StorageException);
+      assertEquals("simulated GCS read failure", 
thrown.getCause().getMessage());
+    }
+  }
+
+  @Test
+  public void testCopyIndexFileToHandlesZeroByteReads() throws Exception {
+    Storage realStorage = LocalStorageHelper.customOptions(false).getService();
+    byte[] data = new byte[100];
+    random().nextBytes(data);
+    // "solrBackupsBucket" matches GCSConfigParser.DEFAULT_GCS_BUCKET_VALUE
+    String bucketName = "solrBackupsBucket";
+
+    GCSBackupRepository repo = createRepositoryWithStorage(realStorage);
+    URI sourceDir = repo.resolve(getBaseUri(), "backup");
+    BlobId blobId = BlobId.of(bucketName, sourceDir + "/source.dat");
+    realStorage.create(BlobInfo.newBuilder(blobId).build(), data);
+
+    Storage zeroReturningStorage = createZeroReturningStorage(realStorage);
+    GCSBackupRepository proxyRepo = 
createRepositoryWithStorage(zeroReturningStorage);
+
+    try (Directory dest = new ByteBuffersDirectory()) {
+      proxyRepo.copyIndexFileTo(sourceDir, "source.dat", dest, "dest.dat");
+      try (IndexInput in = dest.openInput("dest.dat", IOContext.DEFAULT)) {
+        assertEquals(data.length, in.length());
+        byte[] read = new byte[data.length];
+        in.readBytes(read, 0, data.length);
+        assertArrayEquals(data, read);
+      }
+    }
+  }
+
+  @Test
+  public void testCopyIndexFileToCopiesFile() throws Exception {
+    Storage realStorage = LocalStorageHelper.customOptions(false).getService();
+    byte[] data = new byte[100];
+    random().nextBytes(data);
+    // "solrBackupsBucket" matches GCSConfigParser.DEFAULT_GCS_BUCKET_VALUE
+    String bucketName = "solrBackupsBucket";
+
+    GCSBackupRepository repo = createRepositoryWithStorage(realStorage);
+    URI sourceDir = repo.resolve(getBaseUri(), "backup");
+    BlobId blobId = BlobId.of(bucketName, sourceDir + "/source.dat");
+    realStorage.create(BlobInfo.newBuilder(blobId).build(), data);
+
+    try (Directory dest = new ByteBuffersDirectory()) {
+      repo.copyIndexFileTo(sourceDir, "source.dat", dest, "dest.dat");
+      try (IndexInput in = dest.openInput("dest.dat", IOContext.DEFAULT)) {
+        assertEquals(data.length, in.length());
+        byte[] read = new byte[data.length];
+        in.readBytes(read, 0, data.length);
+        assertArrayEquals(data, read);
+      }
+    }
+  }
+
+  private static Storage createFailingStorage() {
+    Storage delegate = LocalStorageHelper.customOptions(false).getService();
+    return (Storage)
+        Proxy.newProxyInstance(
+            Storage.class.getClassLoader(),
+            new Class<?>[] {Storage.class},
+            (proxy, method, args) -> {
+              if ("reader".equals(method.getName())) {
+                throw new StorageException(0, "simulated GCS read failure");
+              }
+              return invokeAndUnwrap(method, delegate, args);
+            });
+  }
+
+  private static Storage createZeroReturningStorage(Storage delegate) {
+    return (Storage)
+        Proxy.newProxyInstance(
+            Storage.class.getClassLoader(),
+            new Class<?>[] {Storage.class},
+            (proxy, method, args) -> {
+              if ("reader".equals(method.getName())
+                  && args != null
+                  && args.length == 1
+                  && args[0] instanceof BlobId) {
+                ReadChannel realChannel = (ReadChannel) 
invokeAndUnwrap(method, delegate, args);
+                return createZeroFirstReadChannel(realChannel);
+              }
+              return invokeAndUnwrap(method, delegate, args);
+            });
+  }
+
+  private static ReadChannel createZeroFirstReadChannel(ReadChannel delegate) {
+    return (ReadChannel)
+        Proxy.newProxyInstance(
+            ReadChannel.class.getClassLoader(),
+            new Class<?>[] {ReadChannel.class},
+            new InvocationHandler() {
+              private boolean returnedZero = false;
+
+              @Override
+              public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+                if ("read".equals(method.getName())
+                    && args != null
+                    && args.length == 1
+                    && args[0] instanceof ByteBuffer) {
+                  if (!returnedZero) {
+                    returnedZero = true;
+                    return 0;
+                  }
+                }
+                return invokeAndUnwrap(method, delegate, args);
+              }
+            });
+  }
+
+  private static Object invokeAndUnwrap(Method method, Object target, Object[] 
args)
+      throws Throwable {
+    try {
+      return method.invoke(target, args);
+    } catch (InvocationTargetException e) {
+      throw e.getCause();
+    }
+  }
+
+  private GCSBackupRepository createRepositoryWithStorage(Storage storage) {
+    TestGCSBackupRepository repo = new TestGCSBackupRepository(storage);
+    repo.init(getBaseBackupRepositoryConfiguration());
+    return repo;
+  }
+
+  private static class TestGCSBackupRepository extends GCSBackupRepository {

Review Comment:
   maybe just a bit of docs to cue the user that we are udisng this to be able 
to simulate certain behaviors?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to