diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientV2.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientV2.java
new file mode 100644
index 0000000000..f8284a09c8
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientV2.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.admin.v2;
+
+import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub;
+import java.io.IOException;
+
+/**
+ * Modern Cloud Bigtable Instance Admin Client.
+ *
+ *
This client extends the auto-generated {@link BaseBigtableInstanceAdminClient} to provide
+ * manual overrides and additional convenience methods for Critical User Journeys (CUJs) that the
+ * GAPIC generator cannot handle natively.
+ */
+public class BigtableInstanceAdminClientV2 extends BaseBigtableInstanceAdminClient {
+
+ protected BigtableInstanceAdminClientV2(BaseBigtableInstanceAdminSettings settings)
+ throws IOException {
+ super(settings);
+ }
+
+ protected BigtableInstanceAdminClientV2(BigtableInstanceAdminStub stub) {
+ super(stub);
+ }
+
+ /** Constructs an instance of BigtableInstanceAdminClientV2 with the given settings. */
+ public static final BigtableInstanceAdminClientV2 createClient(
+ BaseBigtableInstanceAdminSettings settings) throws IOException {
+ return new BigtableInstanceAdminClientV2(settings);
+ }
+
+ /** Constructs an instance of BigtableInstanceAdminClientV2 with the given stub. */
+ public static final BigtableInstanceAdminClientV2 createClient(BigtableInstanceAdminStub stub) {
+ return new BigtableInstanceAdminClientV2(stub);
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientV2.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientV2.java
new file mode 100644
index 0000000000..f61f2f0904
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientV2.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.admin.v2;
+
+import com.google.api.core.ApiFunction;
+import com.google.api.core.ApiFuture;
+import com.google.api.core.ApiFutures;
+import com.google.api.gax.grpc.GrpcCallSettings;
+import com.google.api.gax.grpc.GrpcCallableFactory;
+import com.google.api.gax.grpc.ProtoOperationTransformers.MetadataTransformer;
+import com.google.api.gax.grpc.ProtoOperationTransformers.ResponseTransformer;
+import com.google.api.gax.longrunning.OperationSnapshot;
+import com.google.api.gax.longrunning.OperationTimedPollAlgorithm;
+import com.google.api.gax.retrying.RetrySettings;
+import com.google.api.gax.rpc.ApiExceptions;
+import com.google.api.gax.rpc.ClientContext;
+import com.google.api.gax.rpc.OperationCallSettings;
+import com.google.api.gax.rpc.OperationCallable;
+import com.google.api.gax.rpc.UnaryCallSettings;
+import com.google.api.gax.rpc.UnaryCallable;
+import com.google.bigtable.admin.v2.OptimizeRestoredTableMetadata;
+import com.google.cloud.bigtable.admin.v2.models.ConsistencyRequest;
+import com.google.cloud.bigtable.admin.v2.models.OptimizeRestoredTableOperationToken;
+import com.google.cloud.bigtable.admin.v2.models.RestoredTableResult;
+import com.google.cloud.bigtable.admin.v2.stub.AwaitConsistencyCallable;
+import com.google.cloud.bigtable.admin.v2.stub.BigtableTableAdminStub;
+import com.google.cloud.bigtable.admin.v2.stub.BigtableTableAdminStubSettings;
+import com.google.common.base.Strings;
+import com.google.longrunning.Operation;
+import com.google.protobuf.Empty;
+import io.grpc.MethodDescriptor;
+import io.grpc.MethodDescriptor.Marshaller;
+import io.grpc.MethodDescriptor.MethodType;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.ExecutionException;
+import org.threeten.bp.Duration;
+
+/**
+ * Modern Cloud Bigtable Table Admin Client.
+ *
+ *
This client extends the auto-generated {@link BaseBigtableTableAdminClient} to provide manual
+ * overrides and additional convenience methods for Critical User Journeys (CUJs) that the GAPIC
+ * generator cannot handle natively (e.g., chained Long Running Operations, Consistency Polling).
+ */
+public class BigtableTableAdminClientV2 extends BaseBigtableTableAdminClient {
+ private final AwaitConsistencyCallable awaitConsistencyCallable;
+ private final OperationCallable
+ optimizeRestoredTableOperationBaseCallable;
+
+ protected BigtableTableAdminClientV2(BaseBigtableTableAdminSettings settings) throws IOException {
+ super(settings);
+ this.awaitConsistencyCallable =
+ createAwaitConsistencyCallable((BigtableTableAdminStubSettings) settings.getStubSettings());
+ this.optimizeRestoredTableOperationBaseCallable =
+ createOptimizeRestoredTableOperationBaseCallable(
+ (BigtableTableAdminStubSettings) settings.getStubSettings());
+ }
+
+ protected BigtableTableAdminClientV2(BigtableTableAdminStub stub) {
+ super(stub);
+ this.awaitConsistencyCallable = null;
+ this.optimizeRestoredTableOperationBaseCallable = null;
+ }
+
+ @com.google.common.annotations.VisibleForTesting
+ BigtableTableAdminClientV2(
+ BigtableTableAdminStub stub,
+ AwaitConsistencyCallable awaitConsistencyCallable,
+ OperationCallable
+ optimizeRestoredTableOperationBaseCallable) {
+ super(stub);
+ this.awaitConsistencyCallable = awaitConsistencyCallable;
+ this.optimizeRestoredTableOperationBaseCallable = optimizeRestoredTableOperationBaseCallable;
+ }
+
+ private AwaitConsistencyCallable createAwaitConsistencyCallable(
+ BigtableTableAdminStubSettings settings) throws IOException {
+ ClientContext clientContext = ClientContext.create(settings);
+ // TODO(igorbernstein2): expose polling settings
+ RetrySettings pollingSettings =
+ RetrySettings.newBuilder()
+ .setTotalTimeout(
+ settings.checkConsistencySettings().getRetrySettings().getTotalTimeout())
+ .setInitialRetryDelay(Duration.ofSeconds(10))
+ .setRetryDelayMultiplier(1.0)
+ .setMaxRetryDelay(Duration.ofSeconds(10))
+ .setInitialRpcTimeout(Duration.ZERO)
+ .setMaxRpcTimeout(Duration.ZERO)
+ .setRpcTimeoutMultiplier(1.0)
+ .build();
+
+ return AwaitConsistencyCallable.create(
+ getStub().generateConsistencyTokenCallable(),
+ getStub().checkConsistencyCallable(),
+ clientContext,
+ pollingSettings);
+ }
+
+ private OperationCallable
+ createOptimizeRestoredTableOperationBaseCallable(BigtableTableAdminStubSettings settings)
+ throws IOException {
+ ClientContext clientContext = ClientContext.create(settings);
+
+ GrpcCallSettings unusedInitialCallSettings =
+ GrpcCallSettings.create(
+ MethodDescriptor.newBuilder()
+ .setType(MethodType.UNARY)
+ .setFullMethodName(
+ "google.bigtable.admin.v2.BigtableTableAdmin/OptimizeRestoredTable")
+ .setRequestMarshaller(
+ new Marshaller() {
+ @Override
+ public InputStream stream(Void value) {
+ throw new UnsupportedOperationException("not used");
+ }
+
+ @Override
+ public Void parse(InputStream stream) {
+ throw new UnsupportedOperationException("not used");
+ }
+ })
+ .setResponseMarshaller(
+ new Marshaller() {
+ @Override
+ public InputStream stream(Operation value) {
+ throw new UnsupportedOperationException("not used");
+ }
+
+ @Override
+ public Operation parse(InputStream stream) {
+ throw new UnsupportedOperationException("not used");
+ }
+ })
+ .build());
+
+ final MetadataTransformer protoMetadataTransformer =
+ MetadataTransformer.create(OptimizeRestoredTableMetadata.class);
+
+ final ResponseTransformer protoResponseTransformer =
+ ResponseTransformer.create(com.google.protobuf.Empty.class);
+
+ OperationCallSettings operationCallSettings =
+ OperationCallSettings.newBuilder()
+ .setInitialCallSettings(
+ UnaryCallSettings.newUnaryCallSettingsBuilder()
+ .setSimpleTimeoutNoRetries(Duration.ZERO)
+ .build())
+ .setMetadataTransformer(
+ new ApiFunction() {
+ @Override
+ public OptimizeRestoredTableMetadata apply(OperationSnapshot input) {
+ return protoMetadataTransformer.apply(input);
+ }
+ })
+ .setResponseTransformer(
+ new ApiFunction() {
+ @Override
+ public Empty apply(OperationSnapshot input) {
+ return protoResponseTransformer.apply(input);
+ }
+ })
+ .setPollingAlgorithm(
+ OperationTimedPollAlgorithm.create(
+ RetrySettings.newBuilder()
+ .setInitialRetryDelay(Duration.ofMillis(500L))
+ .setRetryDelayMultiplier(1.5)
+ .setMaxRetryDelay(Duration.ofMillis(5000L))
+ .setInitialRpcTimeout(Duration.ZERO)
+ .setRpcTimeoutMultiplier(1.0)
+ .setMaxRpcTimeout(Duration.ZERO)
+ .setTotalTimeout(Duration.ofMillis(600000L))
+ .build()))
+ .build();
+
+ return GrpcCallableFactory.createOperationCallable(
+ unusedInitialCallSettings,
+ operationCallSettings,
+ clientContext,
+ getStub().getOperationsStub());
+ }
+
+ /** Constructs an instance of BigtableTableAdminClientV2 with the given settings. */
+ public static final BigtableTableAdminClientV2 createClient(
+ BaseBigtableTableAdminSettings settings) throws IOException {
+ return new BigtableTableAdminClientV2(settings);
+ }
+
+ /** Constructs an instance of BigtableTableAdminClientV2 with the given stub. */
+ public static final BigtableTableAdminClientV2 createClient(BigtableTableAdminStub stub) {
+ return new BigtableTableAdminClientV2(stub);
+ }
+
+ /**
+ * Awaits the completion of the "Optimize Restored Table" operation.
+ *
+ * This method blocks until the restore operation is complete, extracts the optimization token,
+ * and returns an ApiFuture for the optimization phase.
+ *
+ * @param restoreFuture The future returned by restoreTableAsync().
+ * @return An ApiFuture that tracks the optimization progress.
+ */
+ public ApiFuture awaitOptimizeRestoredTable(ApiFuture restoreFuture) {
+ // 1. Block and wait for the restore operation to complete
+ RestoredTableResult result;
+ try {
+ result = restoreFuture.get();
+ } catch (Exception e) {
+ throw new RuntimeException("Restore operation failed", e);
+ }
+
+ // 2. Extract the operation token from the result
+ // (RestoredTableResult already wraps the OptimizeRestoredTableOperationToken)
+ OptimizeRestoredTableOperationToken token = result.getOptimizeRestoredTableOperationToken();
+
+ if (token == null || Strings.isNullOrEmpty(token.getOperationName())) {
+ // If there is no optimization operation, return immediate success.
+ return ApiFutures.immediateFuture(Empty.getDefaultInstance());
+ }
+
+ // 3. Return the future for the optimization operation
+ return getOptimizeRestoredTableCallable().resumeFutureCall(token.getOperationName());
+ }
+
+ /**
+ * Awaits a restored table is fully optimized.
+ *
+ * Sample code
+ *
+ *
{@code
+ * RestoredTableResult result =
+ * client.restoreTable(RestoreTableRequest.of(clusterId, backupId).setTableId(tableId));
+ * client.awaitOptimizeRestoredTable(result.getOptimizeRestoredTableOperationToken());
+ * }
+ */
+ public void awaitOptimizeRestoredTable(OptimizeRestoredTableOperationToken token)
+ throws ExecutionException, InterruptedException {
+ awaitOptimizeRestoredTableAsync(token).get();
+ }
+
+ /**
+ * Awaits a restored table is fully optimized asynchronously.
+ *
+ * Sample code
+ *
+ *
{@code
+ * RestoredTableResult result =
+ * client.restoreTable(RestoreTableRequest.of(clusterId, backupId).setTableId(tableId));
+ * ApiFuture future = client.awaitOptimizeRestoredTableAsync(
+ * result.getOptimizeRestoredTableOperationToken());
+ *
+ * ApiFutures.addCallback(
+ * future,
+ * new ApiFutureCallback() {
+ * public void onSuccess(Void unused) {
+ * System.out.println("The optimization of the restored table is done.");
+ * }
+ *
+ * public void onFailure(Throwable t) {
+ * t.printStackTrace();
+ * }
+ * },
+ * MoreExecutors.directExecutor()
+ * );
+ * }
+ */
+ public ApiFuture awaitOptimizeRestoredTableAsync(
+ OptimizeRestoredTableOperationToken token) {
+ ApiFuture emptyFuture =
+ getOptimizeRestoredTableCallable().resumeFutureCall(token.getOperationName());
+ return ApiFutures.transform(
+ emptyFuture,
+ new com.google.api.core.ApiFunction() {
+ @Override
+ public Void apply(Empty input) {
+ return null;
+ }
+ },
+ com.google.common.util.concurrent.MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Polls an existing consistency token until table replication is consistent across all clusters.
+ * Useful for checking consistency of a token generated in a separate process. Blocks until
+ * completion.
+ *
+ * @param tableName The fully qualified table name to check.
+ * @param consistencyToken The token to poll.
+ */
+ public void waitForConsistency(String tableName, String consistencyToken) {
+ ApiExceptions.callAndTranslateApiException(
+ waitForConsistencyAsync(tableName, consistencyToken));
+ }
+
+ /**
+ * Asynchronously polls the consistency token. Returns a future that completes when table
+ * replication is consistent across all clusters.
+ *
+ * @param tableName The fully qualified table name to check.
+ * @param consistencyToken The token to poll.
+ */
+ public ApiFuture waitForConsistencyAsync(String tableName, String consistencyToken) {
+ return getAwaitConsistencyCallable()
+ .futureCall(ConsistencyRequest.forReplicationFromTableName(tableName, consistencyToken));
+ }
+
+ private UnaryCallable getAwaitConsistencyCallable() {
+ if (awaitConsistencyCallable != null) {
+ return awaitConsistencyCallable;
+ }
+ throw new IllegalStateException(
+ "AwaitConsistencyCallable not initialized. BigtableTableAdminClientV2 must be "
+ + "initialized via settings to use this functionality.");
+ }
+
+ private OperationCallable
+ getOptimizeRestoredTableCallable() {
+ if (optimizeRestoredTableOperationBaseCallable != null) {
+ return optimizeRestoredTableOperationBaseCallable;
+ }
+ throw new IllegalStateException(
+ "OptimizeRestoredTableCallable not initialized. BigtableTableAdminClientV2 must be "
+ + "initialized via settings to use this functionality.");
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/stub/AwaitConsistencyCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/stub/AwaitConsistencyCallable.java
index a8ccdd9704..f772421b8d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/stub/AwaitConsistencyCallable.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/stub/AwaitConsistencyCallable.java
@@ -51,7 +51,7 @@
* This callable wraps GenerateConsistencyToken and CheckConsistency RPCs. It will generate a
* token then poll until isConsistent is true.
*/
-class AwaitConsistencyCallable extends UnaryCallable {
+public class AwaitConsistencyCallable extends UnaryCallable {
private final UnaryCallable
generateCallable;
private final UnaryCallable checkCallable;
@@ -59,7 +59,7 @@ class AwaitConsistencyCallable extends UnaryCallable {
@Nullable private final TableAdminRequestContext requestContext;
- static AwaitConsistencyCallable create(
+ public static AwaitConsistencyCallable create(
UnaryCallable
generateCallable,
UnaryCallable checkCallable,
@@ -79,7 +79,7 @@ static AwaitConsistencyCallable create(
generateCallable, checkCallable, retryingExecutor, requestContext);
}
- static AwaitConsistencyCallable create(
+ public static AwaitConsistencyCallable create(
UnaryCallable
generateCallable,
UnaryCallable checkCallable,
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientV2Test.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientV2Test.java
new file mode 100644
index 0000000000..7abf4941a6
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientV2Test.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.admin.v2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+@RunWith(JUnit4.class)
+public class BigtableInstanceAdminClientV2Test {
+
+ @Test
+ public void testCreateWithStub() {
+ BigtableInstanceAdminStub mockStub = Mockito.mock(BigtableInstanceAdminStub.class);
+ BigtableInstanceAdminClientV2 client = BigtableInstanceAdminClientV2.createClient(mockStub);
+
+ assertThat(client).isNotNull();
+ }
+
+ @Test
+ public void testCreateClientWithSettings() throws Exception {
+ BaseBigtableInstanceAdminSettings settings =
+ BaseBigtableInstanceAdminSettings.newBuilder()
+ .setCredentialsProvider(com.google.api.gax.core.NoCredentialsProvider.create())
+ .build();
+ try (BigtableInstanceAdminClientV2 settingsClient =
+ BigtableInstanceAdminClientV2.createClient(settings)) {
+ assertThat(settingsClient).isNotNull();
+ }
+ }
+}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientV2Test.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientV2Test.java
new file mode 100644
index 0000000000..104088d2b3
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableTableAdminClientV2Test.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.admin.v2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.api.core.ApiFuture;
+import com.google.api.core.ApiFutures;
+import com.google.api.gax.longrunning.OperationFuture;
+import com.google.api.gax.rpc.OperationCallable;
+import com.google.bigtable.admin.v2.OptimizeRestoredTableMetadata;
+import com.google.cloud.bigtable.admin.v2.models.ConsistencyRequest;
+import com.google.cloud.bigtable.admin.v2.models.OptimizeRestoredTableOperationToken;
+import com.google.cloud.bigtable.admin.v2.models.RestoredTableResult;
+import com.google.cloud.bigtable.admin.v2.stub.AwaitConsistencyCallable;
+import com.google.cloud.bigtable.admin.v2.stub.BigtableTableAdminStub;
+import com.google.cloud.bigtable.admin.v2.stub.EnhancedBigtableTableAdminStub;
+import com.google.protobuf.Empty;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
+
+@RunWith(JUnit4.class)
+public class BigtableTableAdminClientV2Test {
+ @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ private static final String TABLE_NAME =
+ "projects/my-project/instances/my-instance/tables/my-table";
+
+ @Mock private BigtableTableAdminStub mockStub;
+
+ @Mock private AwaitConsistencyCallable mockAwaitConsistencyCallable;
+
+ @Mock
+ private OperationCallable
+ mockOptimizeRestoredTableCallable;
+
+ private BigtableTableAdminClientV2 client;
+
+ @Before
+ public void setUp() {
+ client =
+ new BigtableTableAdminClientV2(
+ mockStub, mockAwaitConsistencyCallable, mockOptimizeRestoredTableCallable);
+ }
+
+ @Test
+ public void testWaitForConsistencyWithToken() {
+ // Setup
+
+ String token = "my-token";
+ ConsistencyRequest expectedRequest =
+ ConsistencyRequest.forReplicationFromTableName(TABLE_NAME, token);
+
+ final AtomicBoolean wasCalled = new AtomicBoolean(false);
+
+ Mockito.when(mockAwaitConsistencyCallable.futureCall(expectedRequest))
+ .thenAnswer(
+ (Answer>)
+ invocationOnMock -> {
+ wasCalled.set(true);
+ return ApiFutures.immediateFuture(null);
+ });
+
+ // Execute
+ client.waitForConsistency(TABLE_NAME, token);
+
+ // Verify
+ assertThat(wasCalled.get()).isTrue();
+ }
+
+ @Test
+ public void testAwaitOptimizeRestoredTable() throws Exception {
+ // Setup
+
+ String optimizeToken = "my-optimization-token";
+
+ // 1. Mock the Token
+ OptimizeRestoredTableOperationToken mockToken =
+ Mockito.mock(OptimizeRestoredTableOperationToken.class);
+ Mockito.when(mockToken.getOperationName()).thenReturn(optimizeToken);
+
+ // 2. Mock the Result (wrapping the token)
+ RestoredTableResult mockResult = Mockito.mock(RestoredTableResult.class);
+ Mockito.when(mockResult.getOptimizeRestoredTableOperationToken()).thenReturn(mockToken);
+
+ // 3. Mock the Input Future (returning the result)
+ ApiFuture mockRestoreFuture = Mockito.mock(ApiFuture.class);
+ Mockito.when(mockRestoreFuture.get()).thenReturn(mockResult);
+
+ // 4. Mock the Stub's behavior (resuming the Optimize Op)
+ OperationFuture mockOptimizeOp =
+ Mockito.mock(OperationFuture.class);
+ Mockito.when(mockOptimizeRestoredTableCallable.resumeFutureCall(optimizeToken))
+ .thenReturn(mockOptimizeOp);
+
+ // Execute
+ ApiFuture result = client.awaitOptimizeRestoredTable(mockRestoreFuture);
+
+ // Verify
+ assertThat(result).isEqualTo(mockOptimizeOp);
+ Mockito.verify(mockOptimizeRestoredTableCallable).resumeFutureCall(optimizeToken);
+ }
+
+ @Test
+ public void testAwaitOptimizeRestoredTable_NoOp() throws Exception {
+ // Setup: Result with NO optimization token (null or empty)
+ RestoredTableResult mockResult = Mockito.mock(RestoredTableResult.class);
+ Mockito.when(mockResult.getOptimizeRestoredTableOperationToken()).thenReturn(null);
+
+ // Mock the Input Future
+ ApiFuture mockRestoreFuture = Mockito.mock(ApiFuture.class);
+ Mockito.when(mockRestoreFuture.get()).thenReturn(mockResult);
+
+ // Execute
+ ApiFuture result = client.awaitOptimizeRestoredTable(mockRestoreFuture);
+
+ // Verify: Returns immediate success (Empty) without calling the stub
+ assertThat(result.get()).isEqualTo(Empty.getDefaultInstance());
+ }
+
+ @Test
+ public void testCreateClientWithSettings() throws Exception {
+ BaseBigtableTableAdminSettings settings =
+ BaseBigtableTableAdminSettings.newBuilder()
+ .setCredentialsProvider(com.google.api.gax.core.NoCredentialsProvider.create())
+ .build();
+ try (BigtableTableAdminClientV2 settingsClient =
+ BigtableTableAdminClientV2.createClient(settings)) {
+ // Verify that the underlying stub is NOT an Enhanced stub by default
+ // but the client has successfully initialized its own callables.
+ assertThat(settingsClient.getStub()).isNotInstanceOf(EnhancedBigtableTableAdminStub.class);
+ }
+ }
+}