diff --git a/core/src/main/java/google/registry/batch/CloudTasksUtils.java b/core/src/main/java/google/registry/batch/CloudTasksUtils.java index 59e7eb3ef..3ecc662f1 100644 --- a/core/src/main/java/google/registry/batch/CloudTasksUtils.java +++ b/core/src/main/java/google/registry/batch/CloudTasksUtils.java @@ -77,6 +77,7 @@ public class CloudTasksUtils implements Serializable { @Config("projectId") String projectId, @Config("locationId") String locationId, @Config("oauthClientId") String oauthClientId, + // Note that this has to be a service account, due to limitations of the Cloud Tasks API. @ApplicationDefaultCredential GoogleCredentialsBundle credential, SerializableCloudTasksClient client) { this.retrier = retrier; diff --git a/core/src/main/java/google/registry/model/OteAccountBuilder.java b/core/src/main/java/google/registry/model/OteAccountBuilder.java index d0429c0d9..5fe9183cd 100644 --- a/core/src/main/java/google/registry/model/OteAccountBuilder.java +++ b/core/src/main/java/google/registry/model/OteAccountBuilder.java @@ -271,7 +271,7 @@ public final class OteAccountBuilder { Optional groupEmailAddress, CloudTasksUtils cloudTasksUtils, IamClient iamClient) { for (User user : users) { User.grantIapPermission( - user.getEmailAddress(), groupEmailAddress, cloudTasksUtils, iamClient); + user.getEmailAddress(), groupEmailAddress, cloudTasksUtils, null, iamClient); } } diff --git a/core/src/main/java/google/registry/model/console/User.java b/core/src/main/java/google/registry/model/console/User.java index 989f31578..f690025b1 100644 --- a/core/src/main/java/google/registry/model/console/User.java +++ b/core/src/main/java/google/registry/model/console/User.java @@ -14,22 +14,28 @@ package google.registry.model.console; +import static com.google.common.base.Preconditions.checkArgument; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.tools.server.UpdateUserGroupAction.GROUP_UPDATE_QUEUE; import com.google.cloud.tasks.v2.Task; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.flogger.FluentLogger; +import com.google.common.net.MediaType; import google.registry.batch.CloudTasksUtils; import google.registry.persistence.VKey; import google.registry.request.Action.Service; import google.registry.tools.IamClient; +import google.registry.tools.ServiceConnection; import google.registry.tools.server.UpdateUserGroupAction; import google.registry.tools.server.UpdateUserGroupAction.Mode; import google.registry.util.RegistryEnvironment; +import java.io.IOException; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.Nullable; import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.Embeddable; @@ -57,18 +63,30 @@ public class User extends UserBase { public static void grantIapPermission( String emailAddress, Optional groupEmailAddress, - CloudTasksUtils cloudTasksUtils, + @Nullable CloudTasksUtils cloudTasksUtils, + @Nullable ServiceConnection connection, IamClient iamClient) { if (RegistryEnvironment.isInTestServer()) { return; } + checkArgument( + cloudTasksUtils != null || connection != null, + "At least one of cloudTasksUtils or connection must be set"); + checkArgument( + cloudTasksUtils == null || connection == null, + "Only one of cloudTasksUtils or connection can be set"); if (groupEmailAddress.isEmpty()) { logger.atInfo().log("Granting IAP role to user %s", emailAddress); iamClient.addBinding(emailAddress, IAP_SECURED_WEB_APP_USER_ROLE); } else { logger.atInfo().log("Adding %s to group %s", emailAddress, groupEmailAddress.get()); - modifyGroupMembershipAsync( - emailAddress, groupEmailAddress.get(), cloudTasksUtils, UpdateUserGroupAction.Mode.ADD); + if (cloudTasksUtils != null) { + modifyGroupMembershipAsync( + emailAddress, groupEmailAddress.get(), cloudTasksUtils, UpdateUserGroupAction.Mode.ADD); + } else { + modifyGroupMembershipSync( + emailAddress, groupEmailAddress.get(), connection, UpdateUserGroupAction.Mode.ADD); + } } } @@ -81,18 +99,29 @@ public class User extends UserBase { public static void revokeIapPermission( String emailAddress, Optional groupEmailAddress, - CloudTasksUtils cloudTasksUtils, + @Nullable CloudTasksUtils cloudTasksUtils, + @Nullable ServiceConnection connection, IamClient iamClient) { if (RegistryEnvironment.isInTestServer()) { return; } + checkArgument( + cloudTasksUtils != null || connection != null, + "At least one of cloudTasksUtils or connection must be set"); + checkArgument( + cloudTasksUtils == null || connection == null, + "Only one of cloudTasksUtils or connection can be set"); if (groupEmailAddress.isEmpty()) { logger.atInfo().log("Removing IAP role from user %s", emailAddress); iamClient.removeBinding(emailAddress, IAP_SECURED_WEB_APP_USER_ROLE); } else { logger.atInfo().log("Removing %s from group %s", emailAddress, groupEmailAddress.get()); - modifyGroupMembershipAsync( - emailAddress, groupEmailAddress.get(), cloudTasksUtils, Mode.REMOVE); + if (cloudTasksUtils != null) { + modifyGroupMembershipAsync( + emailAddress, groupEmailAddress.get(), cloudTasksUtils, Mode.REMOVE); + } else { + modifyGroupMembershipSync(emailAddress, groupEmailAddress.get(), connection, Mode.REMOVE); + } } } @@ -115,6 +144,25 @@ public class User extends UserBase { cloudTasksUtils.enqueue(GROUP_UPDATE_QUEUE, task); } + private static void modifyGroupMembershipSync( + String userEmailAddress, String groupEmailAddress, ServiceConnection connection, Mode mode) { + try { + connection.sendPostRequest( + UpdateUserGroupAction.PATH, + ImmutableMap.of( + "userEmailAddress", + userEmailAddress, + "groupEmailAddress", + groupEmailAddress, + "groupUpdateMode", + mode.name()), + MediaType.PLAIN_TEXT_UTF_8, + new byte[0]); + } catch (IOException e) { + throw new RuntimeException("Cannot send request to server", e); + } + } + @Override @Access(AccessType.PROPERTY) public Long getId() { diff --git a/core/src/main/java/google/registry/tools/CreateUserCommand.java b/core/src/main/java/google/registry/tools/CreateUserCommand.java index 063cdcb3d..d3a413840 100644 --- a/core/src/main/java/google/registry/tools/CreateUserCommand.java +++ b/core/src/main/java/google/registry/tools/CreateUserCommand.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static google.registry.model.console.User.grantIapPermission; import com.beust.jcommander.Parameters; -import google.registry.batch.CloudTasksUtils; import google.registry.config.RegistryConfig.Config; import google.registry.model.console.User; import google.registry.model.console.UserDao; @@ -28,12 +27,12 @@ import javax.inject.Inject; /** Command to create a new User. */ @Parameters(separators = " =", commandDescription = "Update a user account") -public class CreateUserCommand extends CreateOrUpdateUserCommand { +public class CreateUserCommand extends CreateOrUpdateUserCommand implements CommandWithConnection { + + private ServiceConnection connection; @Inject IamClient iamClient; - @Inject CloudTasksUtils cloudTasksUtils; - @Inject @Config("gSuiteConsoleUserGroupEmailAddress") Optional maybeGroupEmailAddress; @@ -48,7 +47,12 @@ public class CreateUserCommand extends CreateOrUpdateUserCommand { @Override protected String execute() throws Exception { String ret = super.execute(); - grantIapPermission(email, maybeGroupEmailAddress, cloudTasksUtils, iamClient); + grantIapPermission(email, maybeGroupEmailAddress, null, connection, iamClient); return ret; } + + @Override + public void setConnection(ServiceConnection connection) { + this.connection = connection; + } } diff --git a/core/src/main/java/google/registry/tools/DeleteUserCommand.java b/core/src/main/java/google/registry/tools/DeleteUserCommand.java index a128ebf13..8dba6e52f 100644 --- a/core/src/main/java/google/registry/tools/DeleteUserCommand.java +++ b/core/src/main/java/google/registry/tools/DeleteUserCommand.java @@ -20,7 +20,6 @@ import static google.registry.util.PreconditionsUtils.checkArgumentPresent; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import google.registry.batch.CloudTasksUtils; import google.registry.config.RegistryConfig.Config; import google.registry.model.console.User; import google.registry.model.console.UserDao; @@ -30,12 +29,12 @@ import javax.inject.Inject; /** Deletes a {@link User}. */ @Parameters(separators = " =", commandDescription = "Delete a user account") -public class DeleteUserCommand extends ConfirmingCommand { +public class DeleteUserCommand extends ConfirmingCommand implements CommandWithConnection { + + private ServiceConnection connection; @Inject IamClient iamClient; - @Inject CloudTasksUtils cloudTasksUtils; - @Inject @Config("gSuiteConsoleUserGroupEmailAddress") Optional maybeGroupEmailAddress; @@ -59,7 +58,12 @@ public class DeleteUserCommand extends ConfirmingCommand { checkArgumentPresent(optionalUser, "Email no longer corresponds to a valid user"); tm().delete(optionalUser.get()); }); - User.revokeIapPermission(email, maybeGroupEmailAddress, cloudTasksUtils, iamClient); + User.revokeIapPermission(email, maybeGroupEmailAddress, null, connection, iamClient); return String.format("Deleted user with email %s", email); } + + @Override + public void setConnection(ServiceConnection connection) { + this.connection = connection; + } } diff --git a/core/src/main/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorAction.java b/core/src/main/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorAction.java index 67f8cab8a..b24e11ced 100644 --- a/core/src/main/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorAction.java +++ b/core/src/main/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorAction.java @@ -263,7 +263,7 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction { }); UserDao.saveUser(user); User.grantIapPermission( - user.getEmailAddress(), maybeGroupEmailAddress, cloudTasksUtils, iamClient); + user.getEmailAddress(), maybeGroupEmailAddress, cloudTasksUtils, null, iamClient); data.put("password", password); data.put("passcode", phonePasscode); diff --git a/core/src/test/java/google/registry/model/console/UserTest.java b/core/src/test/java/google/registry/model/console/UserTest.java index a72947dfb..ca52a089e 100644 --- a/core/src/test/java/google/registry/model/console/UserTest.java +++ b/core/src/test/java/google/registry/model/console/UserTest.java @@ -21,15 +21,20 @@ import static google.registry.persistence.transaction.TransactionManagerFactory. import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import com.google.cloud.tasks.v2.HttpMethod; +import com.google.common.collect.ImmutableMap; +import com.google.common.net.MediaType; import google.registry.batch.CloudTasksUtils; import google.registry.model.EntityTestCase; import google.registry.testing.CloudTasksHelper; import google.registry.testing.CloudTasksHelper.TaskMatcher; import google.registry.testing.DatabaseHelper; import google.registry.tools.IamClient; +import google.registry.tools.ServiceConnection; +import google.registry.tools.server.UpdateUserGroupAction; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -57,6 +62,34 @@ public class UserTest extends EntityTestCase { }); } + @Test + void testFailure_asyncAndSyncModeConflict() { + assertThrows( + IllegalArgumentException.class, + () -> User.grantIapPermission("email@example.com", Optional.empty(), null, null, null)); + assertThrows( + IllegalArgumentException.class, + () -> + User.grantIapPermission( + "email@example.com", + Optional.empty(), + mock(CloudTasksUtils.class), + mock(ServiceConnection.class), + null)); + assertThrows( + IllegalArgumentException.class, + () -> User.revokeIapPermission("email@example.com", Optional.empty(), null, null, null)); + assertThrows( + IllegalArgumentException.class, + () -> + User.revokeIapPermission( + "email@example.com", + Optional.empty(), + mock(CloudTasksUtils.class), + mock(ServiceConnection.class), + null)); + } + @Test void testFailure_badInputs() { User.Builder builder = new User.Builder(); @@ -116,19 +149,20 @@ public class UserTest extends EntityTestCase { } @Test - void testGrantIapPermission() { + void testGrantIapPermissionAsync() { CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(); IamClient iamClient = mock(IamClient.class); CloudTasksUtils cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils(); // Individual permission. - User.grantIapPermission("email@example.com", Optional.empty(), cloudTasksUtils, iamClient); + User.grantIapPermission( + "email@example.com", Optional.empty(), cloudTasksUtils, null, iamClient); cloudTasksHelper.assertNoTasksEnqueued(); verify(iamClient).addBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE); // Group membership. User.grantIapPermission( - "email@example.com", Optional.of("console@example.com"), cloudTasksUtils, iamClient); + "email@example.com", Optional.of("console@example.com"), cloudTasksUtils, null, iamClient); cloudTasksHelper.assertTasksEnqueued( "console-user-group-update", new TaskMatcher() @@ -142,19 +176,49 @@ public class UserTest extends EntityTestCase { } @Test - void testRevokeIapPermission() { + void testGrantIapPermissionSync() throws Exception { + ServiceConnection connection = mock(ServiceConnection.class); + IamClient iamClient = mock(IamClient.class); + + // Individual permission. + User.grantIapPermission("email@example.com", Optional.empty(), null, connection, iamClient); + verifyNoInteractions(connection); + verify(iamClient).addBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE); + + // Group membership. + User.grantIapPermission( + "email@example.com", Optional.of("console@example.com"), null, connection, iamClient); + verify(connection) + .sendPostRequest( + UpdateUserGroupAction.PATH, + ImmutableMap.of( + "userEmailAddress", + "email@example.com", + "groupEmailAddress", + "console@example.com", + "groupUpdateMode", + "ADD"), + MediaType.PLAIN_TEXT_UTF_8, + new byte[0]); + verifyNoMoreInteractions(iamClient); + verifyNoMoreInteractions(connection); + } + + @Test + void testRevokeIapPermissionAsync() { CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(); IamClient iamClient = mock(IamClient.class); CloudTasksUtils cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils(); // Individual permission. - User.revokeIapPermission("email@example.com", Optional.empty(), cloudTasksUtils, iamClient); + User.revokeIapPermission( + "email@example.com", Optional.empty(), cloudTasksUtils, null, iamClient); cloudTasksHelper.assertNoTasksEnqueued(); verify(iamClient).removeBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE); // Group membership. User.revokeIapPermission( - "email@example.com", Optional.of("console@example.com"), cloudTasksUtils, iamClient); + "email@example.com", Optional.of("console@example.com"), cloudTasksUtils, null, iamClient); cloudTasksHelper.assertTasksEnqueued( "console-user-group-update", new TaskMatcher() @@ -166,4 +230,33 @@ public class UserTest extends EntityTestCase { .param("groupUpdateMode", "REMOVE")); verifyNoMoreInteractions(iamClient); } + + @Test + void testRevokeIapPermissionSync() throws Exception { + ServiceConnection connection = mock(ServiceConnection.class); + IamClient iamClient = mock(IamClient.class); + + // Individual permission. + User.revokeIapPermission("email@example.com", Optional.empty(), null, connection, iamClient); + verifyNoInteractions(connection); + verify(iamClient).removeBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE); + + // Group membership. + User.revokeIapPermission( + "email@example.com", Optional.of("console@example.com"), null, connection, iamClient); + verify(connection) + .sendPostRequest( + UpdateUserGroupAction.PATH, + ImmutableMap.of( + "userEmailAddress", + "email@example.com", + "groupEmailAddress", + "console@example.com", + "groupUpdateMode", + "REMOVE"), + MediaType.PLAIN_TEXT_UTF_8, + new byte[0]); + verifyNoMoreInteractions(iamClient); + verifyNoMoreInteractions(connection); + } } diff --git a/core/src/test/java/google/registry/tools/CreateUserCommandTest.java b/core/src/test/java/google/registry/tools/CreateUserCommandTest.java index c322bf795..43e4588dc 100644 --- a/core/src/test/java/google/registry/tools/CreateUserCommandTest.java +++ b/core/src/test/java/google/registry/tools/CreateUserCommandTest.java @@ -22,16 +22,15 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; -import com.google.cloud.tasks.v2.HttpMethod; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; +import com.google.common.net.MediaType; import google.registry.model.console.GlobalRole; import google.registry.model.console.RegistrarRole; import google.registry.model.console.User; import google.registry.model.console.UserDao; -import google.registry.testing.CloudTasksHelper; -import google.registry.testing.CloudTasksHelper.TaskMatcher; import google.registry.testing.DatabaseHelper; +import google.registry.tools.server.UpdateUserGroupAction; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,13 +39,13 @@ import org.junit.jupiter.api.Test; public class CreateUserCommandTest extends CommandTestCase { private final IamClient iamClient = mock(IamClient.class); - private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(); + private final ServiceConnection connection = mock(ServiceConnection.class); @BeforeEach void beforeEach() { command.iamClient = iamClient; command.maybeGroupEmailAddress = Optional.empty(); - command.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils(); + command.setConnection(connection); } @Test @@ -59,7 +58,7 @@ public class CreateUserCommandTest extends CommandTestCase { assertThat(onlyUser.getUserRoles().getRegistrarRoles()).isEmpty(); verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE); verifyNoMoreInteractions(iamClient); - cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update"); + verifyNoInteractions(connection); } @Test @@ -71,16 +70,20 @@ public class CreateUserCommandTest extends CommandTestCase { assertThat(onlyUser.getUserRoles().isAdmin()).isFalse(); assertThat(onlyUser.getUserRoles().getGlobalRole()).isEqualTo(GlobalRole.NONE); assertThat(onlyUser.getUserRoles().getRegistrarRoles()).isEmpty(); - cloudTasksHelper.assertTasksEnqueued( - "console-user-group-update", - new TaskMatcher() - .method(HttpMethod.POST) - .service("TOOLS") - .path("/_dr/admin/updateUserGroup") - .param("userEmailAddress", "user@example.test") - .param("groupEmailAddress", "group@example.test") - .param("groupUpdateMode", "ADD")); + verify(connection) + .sendPostRequest( + UpdateUserGroupAction.PATH, + ImmutableMap.of( + "userEmailAddress", + "user@example.test", + "groupEmailAddress", + "group@example.test", + "groupUpdateMode", + "ADD"), + MediaType.PLAIN_TEXT_UTF_8, + new byte[0]); verifyNoInteractions(iamClient); + verifyNoMoreInteractions(connection); } @Test @@ -100,7 +103,7 @@ public class CreateUserCommandTest extends CommandTestCase { assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isTrue(); verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE); verifyNoMoreInteractions(iamClient); - cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update"); + verifyNoInteractions(connection); } @Test @@ -110,7 +113,7 @@ public class CreateUserCommandTest extends CommandTestCase { .isEqualTo(GlobalRole.FTE); verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE); verifyNoMoreInteractions(iamClient); - cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update"); + verifyNoInteractions(connection); } @Test @@ -129,7 +132,7 @@ public class CreateUserCommandTest extends CommandTestCase { RegistrarRole.PRIMARY_CONTACT)); verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE); verifyNoMoreInteractions(iamClient); - cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update"); + verifyNoInteractions(connection); } @Test @@ -144,7 +147,7 @@ public class CreateUserCommandTest extends CommandTestCase { .hasMessageThat() .isEqualTo("A user with email user@example.test already exists"); verifyNoMoreInteractions(iamClient); - cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update"); + verifyNoInteractions(connection); } @Test diff --git a/core/src/test/java/google/registry/tools/DeleteUserCommandTest.java b/core/src/test/java/google/registry/tools/DeleteUserCommandTest.java index 39cb2733f..02b62c8d8 100644 --- a/core/src/test/java/google/registry/tools/DeleteUserCommandTest.java +++ b/core/src/test/java/google/registry/tools/DeleteUserCommandTest.java @@ -22,11 +22,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; -import com.google.cloud.tasks.v2.HttpMethod; +import com.google.common.collect.ImmutableMap; +import com.google.common.net.MediaType; import google.registry.model.console.UserDao; -import google.registry.testing.CloudTasksHelper; -import google.registry.testing.CloudTasksHelper.TaskMatcher; import google.registry.testing.DatabaseHelper; +import google.registry.tools.server.UpdateUserGroupAction; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -35,13 +35,13 @@ import org.junit.jupiter.api.Test; public class DeleteUserCommandTest extends CommandTestCase { private final IamClient iamClient = mock(IamClient.class); - private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(); + private final ServiceConnection connection = mock(ServiceConnection.class); @BeforeEach void beforeEach() { command.iamClient = iamClient; command.maybeGroupEmailAddress = Optional.empty(); - command.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils(); + command.setConnection(connection); } @Test @@ -52,7 +52,7 @@ public class DeleteUserCommandTest extends CommandTestCase { assertThat(UserDao.loadUser("email@example.test")).isEmpty(); verify(iamClient).removeBinding("email@example.test", IAP_SECURED_WEB_APP_USER_ROLE); verifyNoMoreInteractions(iamClient); - cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update"); + verifyNoInteractions(connection); } @Test @@ -62,16 +62,20 @@ public class DeleteUserCommandTest extends CommandTestCase { assertThat(UserDao.loadUser("email@example.test")).isPresent(); runCommandForced("--email", "email@example.test"); assertThat(UserDao.loadUser("email@example.test")).isEmpty(); - cloudTasksHelper.assertTasksEnqueued( - "console-user-group-update", - new TaskMatcher() - .method(HttpMethod.POST) - .service("TOOLS") - .path("/_dr/admin/updateUserGroup") - .param("userEmailAddress", "email@example.test") - .param("groupEmailAddress", "group@example.test") - .param("groupUpdateMode", "REMOVE")); + verify(connection) + .sendPostRequest( + UpdateUserGroupAction.PATH, + ImmutableMap.of( + "userEmailAddress", + "email@example.test", + "groupEmailAddress", + "group@example.test", + "groupUpdateMode", + "REMOVE"), + MediaType.PLAIN_TEXT_UTF_8, + new byte[0]); verifyNoInteractions(iamClient); + verifyNoMoreInteractions(connection); } @Test @@ -83,6 +87,6 @@ public class DeleteUserCommandTest extends CommandTestCase { .hasMessageThat() .isEqualTo("Email does not correspond to a valid user"); verifyNoInteractions(iamClient); - cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update"); + verifyNoInteractions(connection); } }