diff --git a/java/com/google/gerrit/server/group/db/GroupConfig.java b/java/com/google/gerrit/server/group/db/GroupConfig.java
index 421d46d1a7..4f45d15222 100644
--- a/java/com/google/gerrit/server/group/db/GroupConfig.java
+++ b/java/com/google/gerrit/server/group/db/GroupConfig.java
@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.util.stream.Collectors.joining;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
@@ -60,15 +61,14 @@ import org.eclipse.jgit.revwalk.RevSort;
*
TODO(aliceks): expand docs.
*/
public class GroupConfig extends VersionedMetaData {
- public static final String GROUP_CONFIG_FILE = "group.config";
-
static final FooterKey FOOTER_ADD_MEMBER = new FooterKey("Add");
static final FooterKey FOOTER_REMOVE_MEMBER = new FooterKey("Remove");
static final FooterKey FOOTER_ADD_GROUP = new FooterKey("Add-group");
static final FooterKey FOOTER_REMOVE_GROUP = new FooterKey("Remove-group");
- private static final String MEMBERS_FILE = "members";
- private static final String SUBGROUPS_FILE = "subgroups";
+ @VisibleForTesting public static final String GROUP_CONFIG_FILE = "group.config";
+ @VisibleForTesting static final String MEMBERS_FILE = "members";
+ @VisibleForTesting static final String SUBGROUPS_FILE = "subgroups";
private static final Pattern LINE_SEPARATOR_PATTERN = Pattern.compile("\\R");
private final AccountGroup.UUID groupUuid;
diff --git a/java/com/google/gerrit/server/group/testing/BUILD b/java/com/google/gerrit/server/group/testing/BUILD
new file mode 100644
index 0000000000..bde140bca4
--- /dev/null
+++ b/java/com/google/gerrit/server/group/testing/BUILD
@@ -0,0 +1,13 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+ name = "testing",
+ testonly = 1,
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/com/google/gerrit/reviewdb:server",
+ "//java/com/google/gerrit/server",
+ "//lib:truth",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ ],
+)
diff --git a/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java b/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java
new file mode 100644
index 0000000000..f0ab638255
--- /dev/null
+++ b/java/com/google/gerrit/server/group/testing/InternalGroupSubject.java
@@ -0,0 +1,107 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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
+//
+// http://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.gerrit.server.group.testing;
+
+import static com.google.common.truth.Truth.assertAbout;
+
+import com.google.common.truth.BooleanSubject;
+import com.google.common.truth.ComparableSubject;
+import com.google.common.truth.DefaultSubject;
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.IterableSubject;
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.Subject;
+import com.google.common.truth.Truth;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.group.InternalGroup;
+import java.sql.Timestamp;
+import org.eclipse.jgit.lib.ObjectId;
+
+public class InternalGroupSubject extends Subject {
+
+ public static InternalGroupSubject assertThat(InternalGroup group) {
+ return assertAbout(InternalGroupSubject::new).that(group);
+ }
+
+ private InternalGroupSubject(FailureMetadata metadata, InternalGroup actual) {
+ super(metadata, actual);
+ }
+
+ public ComparableSubject, AccountGroup.UUID> groupUuid() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getGroupUUID()).named("groupUuid");
+ }
+
+ public ComparableSubject, AccountGroup.NameKey> nameKey() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getNameKey()).named("nameKey");
+ }
+
+ public StringSubject name() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getName()).named("name");
+ }
+
+ public DefaultSubject id() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getId()).named("id");
+ }
+
+ public StringSubject description() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getDescription()).named("description");
+ }
+
+ public ComparableSubject, AccountGroup.UUID> ownerGroupUuid() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getOwnerGroupUUID()).named("ownerGroupUuid");
+ }
+
+ public BooleanSubject visibleToAll() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.isVisibleToAll()).named("visibleToAll");
+ }
+
+ public ComparableSubject, Timestamp> createdOn() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getCreatedOn()).named("createdOn");
+ }
+
+ public IterableSubject members() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getMembers()).named("members");
+ }
+
+ public IterableSubject subgroups() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getSubgroups()).named("subgroups");
+ }
+
+ public ComparableSubject, ObjectId> refState() {
+ isNotNull();
+ InternalGroup group = actual();
+ return Truth.assertThat(group.getRefState()).named("refState");
+ }
+}
diff --git a/javatests/com/google/gerrit/server/group/db/BUILD b/javatests/com/google/gerrit/server/group/db/BUILD
index 2f2d78ff04..48e8d3034c 100644
--- a/javatests/com/google/gerrit/server/group/db/BUILD
+++ b/javatests/com/google/gerrit/server/group/db/BUILD
@@ -13,6 +13,7 @@ junit_tests(
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/group/db/testing",
+ "//java/com/google/gerrit/server/group/testing",
"//java/com/google/gerrit/testing:gerrit-test-util",
"//java/com/google/gerrit/truth",
"//lib:gwtorm",
diff --git a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
index 022228ace7..4effa942a1 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
@@ -15,18 +15,32 @@
package com.google.gerrit.server.group.db;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.gerrit.truth.OptionalSubject.assertThat;
import static org.hamcrest.CoreMatchers.instanceOf;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.testing.InternalGroupSubject;
+import com.google.gerrit.truth.OptionalSubject;
import com.google.gwtorm.client.KeyUtil;
import com.google.gwtorm.server.StandardKeyEncoder;
+import java.io.IOException;
+import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.time.ZoneId;
import java.util.Optional;
import java.util.TimeZone;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -34,7 +48,10 @@ import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -55,6 +72,7 @@ public class GroupConfigTest {
private final AccountGroup.Id groupId = new AccountGroup.Id(123);
private final AuditLogFormatter auditLogFormatter =
AuditLogFormatter.createBackedBy(ImmutableSet.of(), ImmutableSet.of(), "server-id");
+ private final TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");
@Before
public void setUp() throws Exception {
@@ -63,16 +81,36 @@ public class GroupConfigTest {
}
@Test
- public void nameOfNewGroupMustNotBeNull() throws Exception {
+ public void specifiedGroupUuidIsRespectedForNewGroup() throws Exception {
InternalGroupCreation groupCreation =
- getPrefilledGroupCreationBuilder().setNameKey(new AccountGroup.NameKey(null)).build();
- GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+ getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).build();
+ createGroup(groupCreation);
- try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Name of the group users-XYZ");
- groupConfig.commit(metaDataUpdate);
- }
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().groupUuid().isEqualTo(groupUuid);
+ }
+
+ @Test
+ public void specifiedNameIsRespectedForNewGroup() throws Exception {
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setNameKey(groupName).build();
+ createGroup(groupCreation);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().nameKey().isEqualTo(groupName);
+ }
+
+ @Test
+ public void nameOfGroupUpdateOverridesGroupCreation() throws Exception {
+ AccountGroup.NameKey anotherName = new AccountGroup.NameKey("Another name");
+
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setNameKey(groupName).build();
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setName(anotherName).build();
+ createGroup(groupCreation, groupUpdate);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().nameKey().isEqualTo(anotherName);
}
@Test
@@ -83,11 +121,33 @@ public class GroupConfigTest {
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Name of the group users-XYZ");
+ expectedException.expectMessage("Name of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
+ @Test
+ public void nameOfNewGroupMustNotBeNull() throws Exception {
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setNameKey(new AccountGroup.NameKey(null)).build();
+ GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+
+ try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
+ expectedException.expectCause(instanceOf(ConfigInvalidException.class));
+ expectedException.expectMessage("Name of the group " + groupUuid);
+ groupConfig.commit(metaDataUpdate);
+ }
+ }
+
+ @Test
+ public void specifiedIdIsRespectedForNewGroup() throws Exception {
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().setId(groupId).build();
+ createGroup(groupCreation);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().id().isEqualTo(groupId);
+ }
+
@Test
public void idOfNewGroupMustNotBeNegative() throws Exception {
InternalGroupCreation groupCreation =
@@ -96,13 +156,77 @@ public class GroupConfigTest {
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("ID of the group users-XYZ");
+ expectedException.expectMessage("ID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@Test
- public void ownerUuidOfNewGroupMustNotBeNull() throws Exception {
+ public void descriptionDefaultsToNull() throws Exception {
+ InternalGroupCreation groupCreation =
+ InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(groupName)
+ .setId(groupId)
+ .build();
+ createGroup(groupCreation);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().description().isNull();
+ }
+
+ @Test
+ public void specifiedDescriptionIsRespectedForNewGroup() throws Exception {
+ String description = "This is a test group.";
+
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setDescription(description).build();
+ createGroup(groupCreation, groupUpdate);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().description().isEqualTo(description);
+ }
+
+ @Test
+ public void emptyDescriptionForNewGroupIsIgnored() throws Exception {
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setDescription("").build();
+ createGroup(groupCreation, groupUpdate);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().description().isNull();
+ }
+
+ @Test
+ public void ownerGroupUuidDefaultsToGroupItself() throws Exception {
+ InternalGroupCreation groupCreation =
+ InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(groupName)
+ .setId(groupId)
+ .build();
+ createGroup(groupCreation);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().ownerGroupUuid().isEqualTo(groupUuid);
+ }
+
+ @Test
+ public void specifiedOwnerGroupUuidIsRespectedForNewGroup() throws Exception {
+ AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID("anotherOwnerUuid");
+
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setOwnerGroupUUID(ownerGroupUuid).build();
+ createGroup(groupCreation, groupUpdate);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().ownerGroupUuid().isEqualTo(ownerGroupUuid);
+ }
+
+ @Test
+ public void ownerGroupUuidOfNewGroupMustNotBeNull() throws Exception {
InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setOwnerGroupUUID(new AccountGroup.UUID(null)).build();
@@ -111,13 +235,13 @@ public class GroupConfigTest {
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Owner UUID of the group users-XYZ");
+ expectedException.expectMessage("Owner UUID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@Test
- public void ownerUuidOfNewGroupMustNotBeEmpty() throws Exception {
+ public void ownerGroupUuidOfNewGroupMustNotBeEmpty() throws Exception {
InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setOwnerGroupUUID(new AccountGroup.UUID("")).build();
@@ -126,25 +250,110 @@ public class GroupConfigTest {
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Owner UUID of the group users-XYZ");
+ expectedException.expectMessage("Owner UUID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
+ @Test
+ public void visibleToAllDefaultsToFalse() throws Exception {
+ InternalGroupCreation groupCreation =
+ InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(groupName)
+ .setId(groupId)
+ .build();
+ createGroup(groupCreation);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().visibleToAll().isFalse();
+ }
+
+ @Test
+ public void specifiedVisibleToAllIsRespectedForNewGroup() throws Exception {
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setVisibleToAll(true).build();
+ createGroup(groupCreation, groupUpdate);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().visibleToAll().isTrue();
+ }
+
+ @Test
+ public void createdOnDefaultsToNow() throws Exception {
+ // Git timestamps are only precise to the second.
+ Timestamp testStart = TimeUtil.truncateToSecond(TimeUtil.nowTs());
+
+ InternalGroupCreation groupCreation =
+ InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(groupName)
+ .setId(groupId)
+ .build();
+ createGroup(groupCreation);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().createdOn().isAtLeast(testStart);
+ }
+
+ @Test
+ public void specifiedCreatedOnIsRespectedForNewGroup() throws Exception {
+ Timestamp createdOn = toTimestamp(LocalDate.of(2017, Month.DECEMBER, 11).atTime(13, 44, 10));
+
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setUpdatedOn(createdOn).build();
+ createGroup(groupCreation, groupUpdate);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().createdOn().isEqualTo(createdOn);
+ }
+
+ @Test
+ public void specifiedMembersAreRespectedForNewGroup() throws Exception {
+ Account.Id member1 = new Account.Id(1);
+ Account.Id member2 = new Account.Id(2);
+
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setMemberModification(members -> ImmutableSet.of(member1, member2))
+ .build();
+ createGroup(groupCreation, groupUpdate);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().members().containsExactly(member1, member2);
+ }
+
+ @Test
+ public void specifiedSubgroupsAreRespectedForNewGroup() throws Exception {
+ AccountGroup.UUID subgroup1 = new AccountGroup.UUID("subgroup1");
+ AccountGroup.UUID subgroup2 = new AccountGroup.UUID("subgroup2");
+
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(subgroups -> ImmutableSet.of(subgroup1, subgroup2))
+ .build();
+ createGroup(groupCreation, groupUpdate);
+
+ Optional group = loadGroup(groupCreation.getGroupUUID());
+ assertThatGroup(group).value().subgroups().containsExactly(subgroup1, subgroup2);
+ }
+
@Test
public void nameInConfigMayBeUndefined() throws Exception {
populateGroupConfig(groupUuid, "[group]\n\tid = 42\n\townerGroupUuid = owners\n");
- GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
- assertThat(groupConfig.getLoadedGroup().get().getName()).isEmpty();
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().name().isEmpty();
}
@Test
public void nameInConfigMayBeEmpty() throws Exception {
populateGroupConfig(groupUuid, "[group]\n\tname=\n\tid = 42\n\townerGroupUuid = owners\n");
- GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
- assertThat(groupConfig.getLoadedGroup().get().getName()).isEmpty();
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().name().isEmpty();
}
@Test
@@ -152,7 +361,7 @@ public class GroupConfigTest {
populateGroupConfig(groupUuid, "[group]\n\tname = users\n\townerGroupUuid = owners\n");
expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage("ID of the group users-XYZ");
+ expectedException.expectMessage("ID of the group " + groupUuid);
GroupConfig.loadForGroup(repository, groupUuid);
}
@@ -162,23 +371,180 @@ public class GroupConfigTest {
groupUuid, "[group]\n\tname = users\n\tid = -5\n\townerGroupUuid = owners\n");
expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage("ID of the group users-XYZ");
+ expectedException.expectMessage("ID of the group " + groupUuid);
GroupConfig.loadForGroup(repository, groupUuid);
}
@Test
- public void ownerUuidInConfigMustBeDefined() throws Exception {
+ public void descriptionInConfigMayBeUndefined() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tid = 42\n\townerGroupUuid = owners\n");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().description().isNull();
+ }
+
+ @Test
+ public void descriptionInConfigMayBeEmpty() throws Exception {
+ populateGroupConfig(
+ groupUuid, "[group]\n\tdescription=\n\tid = 42\n\townerGroupUuid = owners\n");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().description().isNull();
+ }
+
+ @Test
+ public void ownerGroupUuidInConfigMustBeDefined() throws Exception {
populateGroupConfig(groupUuid, "[group]\n\tname = users\n\tid = 42\n");
expectedException.expect(ConfigInvalidException.class);
- expectedException.expectMessage("Owner UUID of the group users-XYZ");
+ expectedException.expectMessage("Owner UUID of the group " + groupUuid);
GroupConfig.loadForGroup(repository, groupUuid);
}
+ @Test
+ public void membersFileNeedNotExist() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().members().isEmpty();
+ }
+
+ @Test
+ public void membersFileMayBeEmpty() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateSubgroupsFile(groupUuid, "");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().members().isEmpty();
+ }
+
+ @Test
+ public void membersFileMayContainOnlyWhitespace() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateMembersFile(groupUuid, "\n\t\n\n");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().members().isEmpty();
+ }
+
+ @Test
+ public void membersFileMayUseAnyLineBreakCharacters() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateMembersFile(groupUuid, "1\n2\n3\r4\r\n5\u20296");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group)
+ .value()
+ .members()
+ .containsExactly(
+ new Account.Id(1),
+ new Account.Id(2),
+ new Account.Id(3),
+ new Account.Id(4),
+ new Account.Id(5),
+ new Account.Id(6));
+ }
+
+ @Test
+ public void membersFileMustContainIntegers() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateMembersFile(groupUuid, "One");
+
+ expectedException.expect(ConfigInvalidException.class);
+ expectedException.expectMessage("Invalid file members");
+ loadGroup(groupUuid);
+ }
+
+ @Test
+ public void membersFileUsesLineBreaksToSeparateMembers() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateMembersFile(groupUuid, "1\t2");
+
+ expectedException.expect(ConfigInvalidException.class);
+ expectedException.expectMessage("Invalid file members");
+ loadGroup(groupUuid);
+ }
+
+ @Test
+ public void subgroupsFileNeedNotExist() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().subgroups().isEmpty();
+ }
+
+ @Test
+ public void subgroupsFileMayBeEmpty() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateMembersFile(groupUuid, "");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().subgroups().isEmpty();
+ }
+
+ @Test
+ public void subgroupsFileMayContainOnlyWhitespace() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateSubgroupsFile(groupUuid, "\n\t\n\n");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().subgroups().isEmpty();
+ }
+
+ @Test
+ public void subgroupsFileMayUseAnyLineBreakCharacters() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateSubgroupsFile(groupUuid, "1\n2\n3\r4\r\n5\u20296");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group)
+ .value()
+ .subgroups()
+ .containsExactly(
+ new AccountGroup.UUID("1"),
+ new AccountGroup.UUID("2"),
+ new AccountGroup.UUID("3"),
+ new AccountGroup.UUID("4"),
+ new AccountGroup.UUID("5"),
+ new AccountGroup.UUID("6"));
+ }
+
+ @Test
+ public void subgroupsFileMayContainSubgroupsWithWhitespaceInUuid() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateSubgroupsFile(groupUuid, "1\t2 3");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().subgroups().containsExactly(new AccountGroup.UUID("1\t2 3"));
+ }
+
+ @Test
+ public void subgroupsFileUsesLineBreaksToSeparateSubgroups() throws Exception {
+ populateGroupConfig(groupUuid, "[group]\n\tname=users\n\tid = 42\n\townerGroupUuid = owners\n");
+ populateSubgroupsFile(groupUuid, "1\t2\n3");
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group)
+ .value()
+ .subgroups()
+ .containsExactly(new AccountGroup.UUID("1\t2"), new AccountGroup.UUID("3"));
+ }
+
+ @Test
+ public void nameCanBeUpdated() throws Exception {
+ createArbitraryGroup(groupUuid);
+ AccountGroup.NameKey newName = new AccountGroup.NameKey("New name");
+
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setName(newName).build();
+ updateGroup(groupUuid, groupUpdate);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().nameKey().isEqualTo(newName);
+ }
+
@Test
public void nameCannotBeUpdatedToNull() throws Exception {
- populateGroupConfig(
- groupUuid, "[group]\n\tname = users\n\tid = 42\n\townerGroupUuid = owners\n");
+ createArbitraryGroup(groupUuid);
GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
InternalGroupUpdate groupUpdate =
@@ -187,15 +553,14 @@ public class GroupConfigTest {
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Name of the group users-XYZ");
+ expectedException.expectMessage("Name of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@Test
public void nameCannotBeUpdatedToEmptyString() throws Exception {
- populateGroupConfig(
- groupUuid, "[group]\n\tname = users\n\tid = 42\n\townerGroupUuid = owners\n");
+ createArbitraryGroup(groupUuid);
GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
InternalGroupUpdate groupUpdate =
@@ -204,15 +569,65 @@ public class GroupConfigTest {
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Name of the group users-XYZ");
+ expectedException.expectMessage("Name of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@Test
- public void ownerUuidCannotBeUpdatedToNull() throws Exception {
- populateGroupConfig(
- groupUuid, "[group]\n\tname = users\n\tid = 42\n\townerGroupUuid = owners\n");
+ public void nameCanBeUpdatedToEmptyStringIfExplicitlySpecified() throws Exception {
+ createArbitraryGroup(groupUuid);
+ AccountGroup.NameKey emptyName = new AccountGroup.NameKey("");
+
+ GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
+ groupConfig.setAllowSaveEmptyName();
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setName(emptyName).build();
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+ commit(groupConfig);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().nameKey().isEqualTo(emptyName);
+ }
+
+ @Test
+ public void descriptionCanBeUpdated() throws Exception {
+ createArbitraryGroup(groupUuid);
+ String newDescription = "New description";
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setDescription(newDescription).build();
+ updateGroup(groupUuid, groupUpdate);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().description().isEqualTo(newDescription);
+ }
+
+ @Test
+ public void descriptionCanBeRemoved() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setDescription("").build();
+ Optional group = updateGroup(groupUuid, groupUpdate);
+
+ assertThatGroup(group).value().description().isNull();
+ }
+
+ @Test
+ public void ownerGroupUuidCanBeUpdated() throws Exception {
+ createArbitraryGroup(groupUuid);
+ AccountGroup.UUID newOwnerGroupUuid = new AccountGroup.UUID("New owner");
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setOwnerGroupUUID(newOwnerGroupUuid).build();
+ updateGroup(groupUuid, groupUpdate);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().ownerGroupUuid().isEqualTo(newOwnerGroupUuid);
+ }
+
+ @Test
+ public void ownerGroupUuidCannotBeUpdatedToNull() throws Exception {
+ createArbitraryGroup(groupUuid);
GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
InternalGroupUpdate groupUpdate =
@@ -221,15 +636,14 @@ public class GroupConfigTest {
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Owner UUID of the group users-XYZ");
+ expectedException.expectMessage("Owner UUID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@Test
- public void ownerUuidCannotBeUpdatedToEmptyString() throws Exception {
- populateGroupConfig(
- groupUuid, "[group]\n\tname = users\n\tid = 42\n\townerGroupUuid = owners\n");
+ public void ownerGroupUuidCannotBeUpdatedToEmptyString() throws Exception {
+ createArbitraryGroup(groupUuid);
GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
InternalGroupUpdate groupUpdate =
@@ -238,31 +652,894 @@ public class GroupConfigTest {
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
expectedException.expectCause(instanceOf(ConfigInvalidException.class));
- expectedException.expectMessage("Owner UUID of the group users-XYZ");
+ expectedException.expectMessage("Owner UUID of the group " + groupUuid);
groupConfig.commit(metaDataUpdate);
}
}
@Test
- public void automaticallyLoadedNewGroupDoesNotChangeOnReload() throws Exception {
- InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
- GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
- try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
- groupConfig.commit(metaDataUpdate);
- }
+ public void visibleToAllCanBeUpdated() throws Exception {
+ createArbitraryGroup(groupUuid);
+ boolean oldVisibleAll = loadGroup(groupUuid).map(InternalGroup::isVisibleToAll).orElse(false);
- Optional createdGroup = groupConfig.getLoadedGroup();
- Optional reloadedGroup =
- GroupConfig.loadForGroup(repository, groupCreation.getGroupUUID()).getLoadedGroup();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setVisibleToAll(!oldVisibleAll).build();
+ updateGroup(groupUuid, groupUpdate);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().visibleToAll().isEqualTo(!oldVisibleAll);
+ }
+
+ @Test
+ public void createdOnIsNotAffectedByFurtherUpdates() throws Exception {
+ Timestamp createdOn = toTimestamp(LocalDate.of(2017, Month.MAY, 11).atTime(13, 44, 10));
+ Timestamp updatedOn = toTimestamp(LocalDate.of(2017, Month.DECEMBER, 12).atTime(10, 21, 49));
+
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate initialGroupUpdate =
+ InternalGroupUpdate.builder().setUpdatedOn(createdOn).build();
+ createGroup(groupCreation, initialGroupUpdate);
+
+ InternalGroupUpdate laterGroupUpdate =
+ InternalGroupUpdate.builder()
+ .setName(new AccountGroup.NameKey("Another name"))
+ .setUpdatedOn(updatedOn)
+ .build();
+ Optional group = updateGroup(groupCreation.getGroupUUID(), laterGroupUpdate);
+
+ assertThatGroup(group).value().createdOn().isEqualTo(createdOn);
+ Optional reloadedGroup = loadGroup(groupUuid);
+ assertThatGroup(reloadedGroup).value().createdOn().isEqualTo(createdOn);
+ }
+
+ @Test
+ public void membersCanBeAdded() throws Exception {
+ createArbitraryGroup(groupUuid);
+ Account.Id member1 = new Account.Id(1);
+ Account.Id member2 = new Account.Id(2);
+
+ InternalGroupUpdate groupUpdate1 =
+ InternalGroupUpdate.builder()
+ .setMemberModification(members -> ImmutableSet.of(member1))
+ .build();
+ updateGroup(groupUuid, groupUpdate1);
+
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder()
+ .setMemberModification(members -> Sets.union(members, ImmutableSet.of(member2)))
+ .build();
+ updateGroup(groupUuid, groupUpdate2);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().members().containsExactly(member1, member2);
+ }
+
+ @Test
+ public void membersCanBeDeleted() throws Exception {
+ createArbitraryGroup(groupUuid);
+ Account.Id member1 = new Account.Id(1);
+ Account.Id member2 = new Account.Id(2);
+
+ InternalGroupUpdate groupUpdate1 =
+ InternalGroupUpdate.builder()
+ .setMemberModification(members -> ImmutableSet.of(member1, member2))
+ .build();
+ updateGroup(groupUuid, groupUpdate1);
+
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder()
+ .setMemberModification(members -> Sets.difference(members, ImmutableSet.of(member1)))
+ .build();
+ updateGroup(groupUuid, groupUpdate2);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().members().containsExactly(member2);
+ }
+
+ @Test
+ public void subgroupsCanBeAdded() throws Exception {
+ createArbitraryGroup(groupUuid);
+ AccountGroup.UUID subgroup1 = new AccountGroup.UUID("subgroups1");
+ AccountGroup.UUID subgroup2 = new AccountGroup.UUID("subgroups2");
+
+ InternalGroupUpdate groupUpdate1 =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(subgroups -> ImmutableSet.of(subgroup1))
+ .build();
+ updateGroup(groupUuid, groupUpdate1);
+
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(subgroups -> Sets.union(subgroups, ImmutableSet.of(subgroup2)))
+ .build();
+ updateGroup(groupUuid, groupUpdate2);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().subgroups().containsExactly(subgroup1, subgroup2);
+ }
+
+ @Test
+ public void subgroupsCanBeDeleted() throws Exception {
+ createArbitraryGroup(groupUuid);
+ AccountGroup.UUID subgroup1 = new AccountGroup.UUID("subgroups1");
+ AccountGroup.UUID subgroup2 = new AccountGroup.UUID("subgroups2");
+
+ InternalGroupUpdate groupUpdate1 =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(members -> ImmutableSet.of(subgroup1, subgroup2))
+ .build();
+ updateGroup(groupUuid, groupUpdate1);
+
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(
+ members -> Sets.difference(members, ImmutableSet.of(subgroup1)))
+ .build();
+ updateGroup(groupUuid, groupUpdate2);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().subgroups().containsExactly(subgroup2);
+ }
+
+ @Test
+ public void createdGroupIsLoadedAutomatically() throws Exception {
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ Optional group = createGroup(groupCreation);
+
+ assertThat(group).isPresent();
+ }
+
+ @Test
+ public void loadedNewGroupWithMandatoryPropertiesDoesNotChangeOnReload() throws Exception {
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+
+ Optional createdGroup = createGroup(groupCreation);
+ Optional reloadedGroup = loadGroup(groupCreation.getGroupUUID());
assertThat(createdGroup).isEqualTo(reloadedGroup);
}
- private InternalGroupCreation.Builder getPrefilledGroupCreationBuilder() {
- return InternalGroupCreation.builder()
- .setGroupUUID(groupUuid)
- .setNameKey(groupName)
- .setId(groupId);
+ @Test
+ public void loadedNewGroupWithAllPropertiesDoesNotChangeOnReload() throws Exception {
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setDescription("A test group")
+ .setOwnerGroupUUID(new AccountGroup.UUID("another owner"))
+ .setVisibleToAll(true)
+ .setName(new AccountGroup.NameKey("Another name"))
+ .setUpdatedOn(new Timestamp(92900892))
+ .setMemberModification(members -> ImmutableSet.of(new Account.Id(1), new Account.Id(2)))
+ .setSubgroupModification(
+ subgroups -> ImmutableSet.of(new AccountGroup.UUID("subgroup")))
+ .build();
+
+ Optional createdGroup = createGroup(groupCreation, groupUpdate);
+ Optional reloadedGroup = loadGroup(groupCreation.getGroupUUID());
+
+ assertThat(createdGroup).isEqualTo(reloadedGroup);
+ }
+
+ @Test
+ public void loadedGroupAfterUpdatesForAllPropertiesDoesNotChangeOnReload() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setDescription("A test group")
+ .setOwnerGroupUUID(new AccountGroup.UUID("another owner"))
+ .setVisibleToAll(true)
+ .setName(new AccountGroup.NameKey("Another name"))
+ .setUpdatedOn(new Timestamp(92900892))
+ .setMemberModification(members -> ImmutableSet.of(new Account.Id(1), new Account.Id(2)))
+ .setSubgroupModification(
+ subgroups -> ImmutableSet.of(new AccountGroup.UUID("subgroup")))
+ .build();
+
+ Optional updatedGroup = updateGroup(groupUuid, groupUpdate);
+ Optional reloadedGroup = loadGroup(groupUuid);
+
+ assertThat(updatedGroup).isEqualTo(reloadedGroup);
+ }
+
+ @Test
+ public void loadedGroupWithAllPropertiesAndUpdateOfSinglePropertyDoesNotChangeOnReload()
+ throws Exception {
+ // Create a group with all properties set.
+ InternalGroupCreation groupCreation = getPrefilledGroupCreationBuilder().build();
+ InternalGroupUpdate initialGroupUpdate =
+ InternalGroupUpdate.builder()
+ .setDescription("A test group")
+ .setOwnerGroupUUID(new AccountGroup.UUID("another owner"))
+ .setVisibleToAll(true)
+ .setName(new AccountGroup.NameKey("Another name"))
+ .setUpdatedOn(new Timestamp(92900892))
+ .setMemberModification(members -> ImmutableSet.of(new Account.Id(1), new Account.Id(2)))
+ .setSubgroupModification(
+ subgroups -> ImmutableSet.of(new AccountGroup.UUID("subgroup")))
+ .build();
+ createGroup(groupCreation, initialGroupUpdate);
+
+ // Only update one of the properties.
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+
+ Optional updatedGroup = updateGroup(groupCreation.getGroupUUID(), groupUpdate);
+ Optional reloadedGroup = loadGroup(groupCreation.getGroupUUID());
+
+ assertThat(updatedGroup).isEqualTo(reloadedGroup);
+ }
+
+ @Test
+ public void groupConfigMayBeReusedForFurtherUpdates() throws Exception {
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).setId(groupId).build();
+ GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+ commit(groupConfig);
+
+ AccountGroup.NameKey name = new AccountGroup.NameKey("Robots");
+ InternalGroupUpdate groupUpdate1 = InternalGroupUpdate.builder().setName(name).build();
+ groupConfig.setGroupUpdate(groupUpdate1, auditLogFormatter);
+ commit(groupConfig);
+
+ String description = "Test group for robots";
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder().setDescription(description).build();
+ groupConfig.setGroupUpdate(groupUpdate2, auditLogFormatter);
+ commit(groupConfig);
+
+ Optional group = loadGroup(groupUuid);
+ assertThatGroup(group).value().id().isEqualTo(groupId);
+ assertThatGroup(group).value().nameKey().isEqualTo(name);
+ assertThatGroup(group).value().description().isEqualTo(description);
+ }
+
+ @Test
+ public void newGroupIsRepresentedByARefPointingToARootCommit() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ Ref ref = repository.exactRef(RefNames.refsGroups(groupUuid));
+ assertThat(ref.getObjectId()).isNotNull();
+
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ RevCommit revCommit = revWalk.parseCommit(ref.getObjectId());
+ assertThat(revCommit.getParentCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void updatedGroupIsRepresentedByARefPointingToACommitSequence() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ RevCommit commitAfterCreation = getLatestCommitForGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+ updateGroup(groupUuid, groupUpdate);
+
+ RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
+ assertThat(commitAfterUpdate).isNotEqualTo(commitAfterCreation);
+ assertThat(commitAfterUpdate.getParents()).asList().containsExactly(commitAfterCreation);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedForEmptyUpdate() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().build();
+
+ RevCommit commitBeforeUpdate = getLatestCommitForGroup(groupUuid);
+ updateGroup(groupUuid, groupUpdate);
+ RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterUpdate).isEqualTo(commitBeforeUpdate);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedForPureUpdatedOnUpdate() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ Timestamp updatedOn = toTimestamp(LocalDate.of(3017, Month.DECEMBER, 12).atTime(10, 21, 49));
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setUpdatedOn(updatedOn).build();
+
+ RevCommit commitBeforeUpdate = getLatestCommitForGroup(groupUuid);
+ updateGroup(groupUuid, groupUpdate);
+ RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterUpdate).isEqualTo(commitBeforeUpdate);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedForRedundantNameUpdate() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setName(groupName).build();
+ updateGroup(groupUuid, groupUpdate);
+
+ RevCommit commitBeforeUpdate = getLatestCommitForGroup(groupUuid);
+ updateGroup(groupUuid, groupUpdate);
+ RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterUpdate).isEqualTo(commitBeforeUpdate);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedForRedundantDescriptionUpdate() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setDescription("A test group").build();
+ updateGroup(groupUuid, groupUpdate);
+
+ RevCommit commitBeforeUpdate = getLatestCommitForGroup(groupUuid);
+ updateGroup(groupUuid, groupUpdate);
+ RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterUpdate).isEqualTo(commitBeforeUpdate);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedForRedundantVisibleToAllUpdate() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().setVisibleToAll(true).build();
+ updateGroup(groupUuid, groupUpdate);
+
+ RevCommit commitBeforeUpdate = getLatestCommitForGroup(groupUuid);
+ updateGroup(groupUuid, groupUpdate);
+ RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterUpdate).isEqualTo(commitBeforeUpdate);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedForRedundantOwnerGroupUuidUpdate() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setOwnerGroupUUID(new AccountGroup.UUID("Another owner"))
+ .build();
+ updateGroup(groupUuid, groupUpdate);
+
+ RevCommit commitBeforeUpdate = getLatestCommitForGroup(groupUuid);
+ updateGroup(groupUuid, groupUpdate);
+ RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterUpdate).isEqualTo(commitBeforeUpdate);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedForRedundantMemberUpdate() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setMemberModification(
+ members -> Sets.union(members, ImmutableSet.of(new Account.Id(10))))
+ .build();
+ updateGroup(groupUuid, groupUpdate);
+
+ RevCommit commitBeforeUpdate = getLatestCommitForGroup(groupUuid);
+ updateGroup(groupUuid, groupUpdate);
+ RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterUpdate).isEqualTo(commitBeforeUpdate);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedForRedundantSubgroupsUpdate() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(
+ subgroups ->
+ Sets.union(subgroups, ImmutableSet.of(new AccountGroup.UUID("subgroup"))))
+ .build();
+ updateGroup(groupUuid, groupUpdate);
+
+ RevCommit commitBeforeUpdate = getLatestCommitForGroup(groupUuid);
+ updateGroup(groupUuid, groupUpdate);
+ RevCommit commitAfterUpdate = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterUpdate).isEqualTo(commitBeforeUpdate);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedWhenCommittingGroupCreationTwice() throws Exception {
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).build();
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+
+ GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+ commit(groupConfig);
+
+ RevCommit commitBeforeSecondCommit = getLatestCommitForGroup(groupUuid);
+ commit(groupConfig);
+ RevCommit commitAfterSecondCommit = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterSecondCommit).isEqualTo(commitBeforeSecondCommit);
+ }
+
+ @Test
+ public void newCommitIsNotCreatedWhenCommittingGroupUpdateTwice() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setDescription("A test group").build();
+
+ GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+ commit(groupConfig);
+
+ RevCommit commitBeforeSecondCommit = getLatestCommitForGroup(groupUuid);
+ commit(groupConfig);
+ RevCommit commitAfterSecondCommit = getLatestCommitForGroup(groupUuid);
+
+ assertThat(commitAfterSecondCommit).isEqualTo(commitBeforeSecondCommit);
+ }
+
+ @Test
+ public void commitTimeMatchesDefaultCreatedOnOfNewGroup() throws Exception {
+ // Git timestamps are only precise to the second.
+ long testStartAsSecondsSinceEpoch = TimeUtil.nowTs().getTime() / 1000;
+
+ InternalGroupCreation groupCreation =
+ InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(groupName)
+ .setId(groupId)
+ .build();
+ createGroup(groupCreation);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getCommitTime()).isAtLeast((int) testStartAsSecondsSinceEpoch);
+ }
+
+ @Test
+ public void commitTimeMatchesSpecifiedCreatedOnOfNewGroup() throws Exception {
+ // Git timestamps are only precise to the second.
+ long createdOnAsSecondsSinceEpoch = 9082093;
+
+ InternalGroupCreation groupCreation =
+ InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(groupName)
+ .setId(groupId)
+ .build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setUpdatedOn(new Timestamp(createdOnAsSecondsSinceEpoch * 1000))
+ .build();
+ createGroup(groupCreation, groupUpdate);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getCommitTime()).isEqualTo(createdOnAsSecondsSinceEpoch);
+ }
+
+ @Test
+ public void timestampOfCommitterMatchesSpecifiedCreatedOnOfNewGroup() throws Exception {
+ Timestamp committerTimestamp =
+ toTimestamp(LocalDate.of(2017, Month.DECEMBER, 13).atTime(15, 5, 27));
+ Timestamp createdOn = toTimestamp(LocalDate.of(2016, Month.MARCH, 11).atTime(23, 49, 11));
+
+ InternalGroupCreation groupCreation =
+ InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(groupName)
+ .setId(groupId)
+ .build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setName(new AccountGroup.NameKey("Another name"))
+ .setUpdatedOn(createdOn)
+ .build();
+ GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+
+ PersonIdent committerIdent =
+ new PersonIdent("Jane", "Jane@gerritcodereview.com", committerTimestamp, timeZone);
+ try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
+ metaDataUpdate.getCommitBuilder().setCommitter(committerIdent);
+ groupConfig.commit(metaDataUpdate);
+ }
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getCommitterIdent().getWhen()).isEqualTo(createdOn);
+ assertThat(revCommit.getCommitterIdent().getTimeZone().getRawOffset())
+ .isEqualTo(timeZone.getRawOffset());
+ }
+
+ @Test
+ public void timestampOfAuthorMatchesSpecifiedCreatedOnOfNewGroup() throws Exception {
+ Timestamp authorTimestamp =
+ toTimestamp(LocalDate.of(2017, Month.DECEMBER, 13).atTime(15, 5, 27));
+ Timestamp createdOn = toTimestamp(LocalDate.of(2016, Month.MARCH, 11).atTime(23, 49, 11));
+
+ InternalGroupCreation groupCreation =
+ InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(groupName)
+ .setId(groupId)
+ .build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setName(new AccountGroup.NameKey("Another name"))
+ .setUpdatedOn(createdOn)
+ .build();
+ GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+
+ PersonIdent authorIdent =
+ new PersonIdent("Jane", "Jane@gerritcodereview.com", authorTimestamp, timeZone);
+ try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
+ metaDataUpdate.getCommitBuilder().setAuthor(authorIdent);
+ groupConfig.commit(metaDataUpdate);
+ }
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getAuthorIdent().getWhen()).isEqualTo(createdOn);
+ assertThat(revCommit.getAuthorIdent().getTimeZone().getRawOffset())
+ .isEqualTo(timeZone.getRawOffset());
+ }
+
+ @Test
+ public void commitTimeMatchesDefaultUpdatedOnOfUpdatedGroup() throws Exception {
+ // Git timestamps are only precise to the second.
+ long testStartAsSecondsSinceEpoch = TimeUtil.nowTs().getTime() / 1000;
+
+ createArbitraryGroup(groupUuid);
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+ updateGroup(groupUuid, groupUpdate);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getCommitTime()).isAtLeast((int) testStartAsSecondsSinceEpoch);
+ }
+
+ @Test
+ public void commitTimeMatchesSpecifiedUpdatedOnOfUpdatedGroup() throws Exception {
+ // Git timestamps are only precise to the second.
+ long updatedOnAsSecondsSinceEpoch = 9082093;
+
+ createArbitraryGroup(groupUuid);
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setName(new AccountGroup.NameKey("Another name"))
+ .setUpdatedOn(new Timestamp(updatedOnAsSecondsSinceEpoch * 1000))
+ .build();
+ updateGroup(groupUuid, groupUpdate);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getCommitTime()).isEqualTo(updatedOnAsSecondsSinceEpoch);
+ }
+
+ @Test
+ public void timestampOfCommitterMatchesSpecifiedUpdatedOnOfUpdatedGroup() throws Exception {
+ Timestamp committerTimestamp =
+ toTimestamp(LocalDate.of(2017, Month.DECEMBER, 13).atTime(15, 5, 27));
+ Timestamp updatedOn = toTimestamp(LocalDate.of(2016, Month.MARCH, 11).atTime(23, 49, 11));
+
+ createArbitraryGroup(groupUuid);
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setName(new AccountGroup.NameKey("Another name"))
+ .setUpdatedOn(updatedOn)
+ .build();
+ GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+
+ PersonIdent committerIdent =
+ new PersonIdent("Jane", "Jane@gerritcodereview.com", committerTimestamp, timeZone);
+ try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
+ metaDataUpdate.getCommitBuilder().setCommitter(committerIdent);
+ groupConfig.commit(metaDataUpdate);
+ }
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getCommitterIdent().getWhen()).isEqualTo(updatedOn);
+ assertThat(revCommit.getCommitterIdent().getTimeZone().getRawOffset())
+ .isEqualTo(timeZone.getRawOffset());
+ }
+
+ @Test
+ public void timestampOfAuthorMatchesSpecifiedUpdatedOnOfUpdatedGroup() throws Exception {
+ Timestamp authorTimestamp =
+ toTimestamp(LocalDate.of(2017, Month.DECEMBER, 13).atTime(15, 5, 27));
+ Timestamp updatedOn = toTimestamp(LocalDate.of(2016, Month.MARCH, 11).atTime(23, 49, 11));
+
+ createArbitraryGroup(groupUuid);
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setName(new AccountGroup.NameKey("Another name"))
+ .setUpdatedOn(updatedOn)
+ .build();
+ GroupConfig groupConfig = GroupConfig.loadForGroup(repository, groupUuid);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+
+ PersonIdent authorIdent =
+ new PersonIdent("Jane", "Jane@gerritcodereview.com", authorTimestamp, timeZone);
+ try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
+ metaDataUpdate.getCommitBuilder().setAuthor(authorIdent);
+ groupConfig.commit(metaDataUpdate);
+ }
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getAuthorIdent().getWhen()).isEqualTo(updatedOn);
+ assertThat(revCommit.getAuthorIdent().getTimeZone().getRawOffset())
+ .isEqualTo(timeZone.getRawOffset());
+ }
+
+ @Test
+ public void refStateOfLoadedGroupIsPopulatedWithCommitSha1() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ Optional group = loadGroup(groupUuid);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThatGroup(group).value().refState().isEqualTo(revCommit.copy());
+ }
+
+ @Test
+ public void groupCanBeLoadedAtASpecificRevision() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ AccountGroup.NameKey firstName = new AccountGroup.NameKey("Bots");
+ InternalGroupUpdate groupUpdate1 = InternalGroupUpdate.builder().setName(firstName).build();
+ updateGroup(groupUuid, groupUpdate1);
+
+ RevCommit commitAfterUpdate1 = getLatestCommitForGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Robots")).build();
+ updateGroup(groupUuid, groupUpdate2);
+
+ GroupConfig groupConfig =
+ GroupConfig.loadForGroupSnapshot(repository, groupUuid, commitAfterUpdate1.copy());
+ Optional group = groupConfig.getLoadedGroup();
+ assertThatGroup(group).value().nameKey().isEqualTo(firstName);
+ assertThatGroup(group).value().refState().isEqualTo(commitAfterUpdate1.copy());
+ }
+
+ @Test
+ public void commitMessageOfNewGroupWithoutMembersOrSubgroupsContainsNoFooters() throws Exception {
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).build();
+ createGroup(groupCreation);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage()).isEqualTo("Create group");
+ }
+
+ @Test
+ public void commitMessageOfNewGroupWithAdditionalNameSpecificationContainsNoFooters()
+ throws Exception {
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Another name")).build();
+ createGroup(groupCreation, groupUpdate);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage()).isEqualTo("Create group");
+ }
+
+ @Test
+ public void commitMessageOfNewGroupWithMembersContainsFooters() throws Exception {
+ Account account13 = createAccount(new Account.Id(13), "John");
+ Account account7 = createAccount(new Account.Id(7), "Jane");
+ ImmutableSet accounts = ImmutableSet.of(account13, account7);
+
+ AuditLogFormatter auditLogFormatter =
+ AuditLogFormatter.createBackedBy(accounts, ImmutableSet.of(), "server-id");
+
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setMemberModification(members -> ImmutableSet.of(account13.getId(), account7.getId()))
+ .build();
+
+ GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+ commit(groupConfig);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage())
+ .isEqualTo("Create group\n\nAdd: John <13@server-id>\nAdd: Jane <7@server-id>");
+ }
+
+ @Test
+ public void commitMessageOfNewGroupWithSubgroupsContainsFooters() throws Exception {
+ GroupDescription.Basic group1 = createGroup(new AccountGroup.UUID("129403"), "Bots");
+ GroupDescription.Basic group2 = createGroup(new AccountGroup.UUID("8903493"), "Verifiers");
+ ImmutableSet groups = ImmutableSet.of(group1, group2);
+
+ AuditLogFormatter auditLogFormatter =
+ AuditLogFormatter.createBackedBy(ImmutableSet.of(), groups, "serverId");
+
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setGroupUUID(groupUuid).build();
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(
+ subgroups -> ImmutableSet.of(group1.getGroupUUID(), group2.getGroupUUID()))
+ .build();
+ GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+ commit(groupConfig);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage())
+ .isEqualTo("Create group\n\nAdd-group: Bots <129403>\nAdd-group: Verifiers <8903493>");
+ }
+
+ @Test
+ public void commitMessageOfMemberAdditionContainsFooters() throws Exception {
+ Account account13 = createAccount(new Account.Id(13), "John");
+ Account account7 = createAccount(new Account.Id(7), "Jane");
+ ImmutableSet accounts = ImmutableSet.of(account13, account7);
+
+ createArbitraryGroup(groupUuid);
+
+ AuditLogFormatter auditLogFormatter =
+ AuditLogFormatter.createBackedBy(accounts, ImmutableSet.of(), "GerritServer1");
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setMemberModification(members -> ImmutableSet.of(account13.getId(), account7.getId()))
+ .build();
+ updateGroup(groupUuid, groupUpdate, auditLogFormatter);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage())
+ .isEqualTo("Update group\n\nAdd: John <13@GerritServer1>\nAdd: Jane <7@GerritServer1>");
+ }
+
+ @Test
+ public void commitMessageOfMemberRemovalContainsFooters() throws Exception {
+ Account account13 = createAccount(new Account.Id(13), "John");
+ Account account7 = createAccount(new Account.Id(7), "Jane");
+ ImmutableSet accounts = ImmutableSet.of(account13, account7);
+
+ createArbitraryGroup(groupUuid);
+
+ AuditLogFormatter auditLogFormatter =
+ AuditLogFormatter.createBackedBy(accounts, ImmutableSet.of(), "server-id");
+
+ InternalGroupUpdate groupUpdate1 =
+ InternalGroupUpdate.builder()
+ .setMemberModification(members -> ImmutableSet.of(account13.getId(), account7.getId()))
+ .build();
+ updateGroup(groupUuid, groupUpdate1, auditLogFormatter);
+
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder()
+ .setMemberModification(members -> ImmutableSet.of(account7.getId()))
+ .build();
+ updateGroup(groupUuid, groupUpdate2, auditLogFormatter);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage()).isEqualTo("Update group\n\nRemove: John <13@server-id>");
+ }
+
+ @Test
+ public void commitMessageOfSubgroupAdditionContainsFooters() throws Exception {
+ GroupDescription.Basic group1 = createGroup(new AccountGroup.UUID("129403"), "Bots");
+ GroupDescription.Basic group2 = createGroup(new AccountGroup.UUID("8903493"), "Verifiers");
+ ImmutableSet groups = ImmutableSet.of(group1, group2);
+
+ createArbitraryGroup(groupUuid);
+
+ AuditLogFormatter auditLogFormatter =
+ AuditLogFormatter.createBackedBy(ImmutableSet.of(), groups, "serverId");
+
+ InternalGroupUpdate groupUpdate =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(
+ subgroups -> ImmutableSet.of(group1.getGroupUUID(), group2.getGroupUUID()))
+ .build();
+ updateGroup(groupUuid, groupUpdate, auditLogFormatter);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage())
+ .isEqualTo("Update group\n\nAdd-group: Bots <129403>\nAdd-group: Verifiers <8903493>");
+ }
+
+ @Test
+ public void commitMessageOfSubgroupRemovalContainsFooters() throws Exception {
+ GroupDescription.Basic group1 = createGroup(new AccountGroup.UUID("129403"), "Bots");
+ GroupDescription.Basic group2 = createGroup(new AccountGroup.UUID("8903493"), "Verifiers");
+ ImmutableSet groups = ImmutableSet.of(group1, group2);
+
+ createArbitraryGroup(groupUuid);
+
+ AuditLogFormatter auditLogFormatter =
+ AuditLogFormatter.createBackedBy(ImmutableSet.of(), groups, "serverId");
+
+ InternalGroupUpdate groupUpdate1 =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(
+ subgroups -> ImmutableSet.of(group1.getGroupUUID(), group2.getGroupUUID()))
+ .build();
+ updateGroup(groupUuid, groupUpdate1, auditLogFormatter);
+
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder()
+ .setSubgroupModification(subgroups -> ImmutableSet.of(group1.getGroupUUID()))
+ .build();
+ updateGroup(groupUuid, groupUpdate2, auditLogFormatter);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage())
+ .isEqualTo("Update group\n\nRemove-group: Verifiers <8903493>");
+ }
+
+ @Test
+ public void commitMessageOfGroupRenameContainsFooters() throws Exception {
+ createArbitraryGroup(groupUuid);
+
+ InternalGroupUpdate groupUpdate1 =
+ InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("Old name")).build();
+ updateGroup(groupUuid, groupUpdate1);
+
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder().setName(new AccountGroup.NameKey("New name")).build();
+ updateGroup(groupUuid, groupUpdate2);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage())
+ .isEqualTo("Update group\n\nRename from Old name to New name");
+ }
+
+ @Test
+ public void commitMessageFootersCanBeMixed() throws Exception {
+ Account account13 = createAccount(new Account.Id(13), "John");
+ Account account7 = createAccount(new Account.Id(7), "Jane");
+ ImmutableSet accounts = ImmutableSet.of(account13, account7);
+ GroupDescription.Basic group1 = createGroup(new AccountGroup.UUID("129403"), "Bots");
+ GroupDescription.Basic group2 = createGroup(new AccountGroup.UUID("8903493"), "Verifiers");
+ ImmutableSet groups = ImmutableSet.of(group1, group2);
+
+ createArbitraryGroup(groupUuid);
+
+ AuditLogFormatter auditLogFormatter =
+ AuditLogFormatter.createBackedBy(accounts, groups, "serverId");
+
+ InternalGroupUpdate groupUpdate1 =
+ InternalGroupUpdate.builder()
+ .setName(new AccountGroup.NameKey("Old name"))
+ .setMemberModification(members -> ImmutableSet.of(account7.getId()))
+ .setSubgroupModification(subgroups -> ImmutableSet.of(group2.getGroupUUID()))
+ .build();
+ updateGroup(groupUuid, groupUpdate1, auditLogFormatter);
+
+ InternalGroupUpdate groupUpdate2 =
+ InternalGroupUpdate.builder()
+ .setName(new AccountGroup.NameKey("New name"))
+ .setMemberModification(members -> ImmutableSet.of(account13.getId()))
+ .setSubgroupModification(subgroups -> ImmutableSet.of(group1.getGroupUUID()))
+ .build();
+ updateGroup(groupUuid, groupUpdate2, auditLogFormatter);
+
+ RevCommit revCommit = getLatestCommitForGroup(groupUuid);
+ assertThat(revCommit.getFullMessage())
+ .isEqualTo(
+ "Update group\n"
+ + "\n"
+ + "Rename from Old name to New name\n"
+ + "Remove: Jane <7@serverId>\n"
+ + "Add: John <13@serverId>\n"
+ + "Remove-group: Verifiers <8903493>\n"
+ + "Add-group: Bots <129403>");
+ }
+
+ private static Timestamp toTimestamp(LocalDateTime localDateTime) {
+ return Timestamp.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
private void populateGroupConfig(AccountGroup.UUID uuid, String fileContent) throws Exception {
@@ -274,10 +1551,81 @@ public class GroupConfigTest {
.create();
}
+ private void populateMembersFile(AccountGroup.UUID uuid, String fileContent) throws Exception {
+ testRepository
+ .branch(RefNames.refsGroups(uuid))
+ .commit()
+ .message("Prepopulate members")
+ .add(GroupConfig.MEMBERS_FILE, fileContent)
+ .create();
+ }
+
+ private void populateSubgroupsFile(AccountGroup.UUID uuid, String fileContent) throws Exception {
+ testRepository
+ .branch(RefNames.refsGroups(uuid))
+ .commit()
+ .message("Prepopulate subgroups")
+ .add(GroupConfig.SUBGROUPS_FILE, fileContent)
+ .create();
+ }
+
+ private void createArbitraryGroup(AccountGroup.UUID uuid) throws Exception {
+ InternalGroupCreation groupCreation =
+ getPrefilledGroupCreationBuilder().setGroupUUID(uuid).build();
+ createGroup(groupCreation);
+ }
+
+ private InternalGroupCreation.Builder getPrefilledGroupCreationBuilder() {
+ return InternalGroupCreation.builder()
+ .setGroupUUID(groupUuid)
+ .setNameKey(groupName)
+ .setId(groupId);
+ }
+
+ private Optional createGroup(InternalGroupCreation groupCreation)
+ throws Exception {
+ GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+ commit(groupConfig);
+ return groupConfig.getLoadedGroup();
+ }
+
+ private Optional createGroup(
+ InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate) throws Exception {
+ GroupConfig groupConfig = GroupConfig.createForNewGroup(repository, groupCreation);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+ commit(groupConfig);
+ return groupConfig.getLoadedGroup();
+ }
+
+ private Optional updateGroup(
+ AccountGroup.UUID uuid, InternalGroupUpdate groupUpdate) throws Exception {
+ return updateGroup(uuid, groupUpdate, auditLogFormatter);
+ }
+
+ private Optional updateGroup(
+ AccountGroup.UUID uuid, InternalGroupUpdate groupUpdate, AuditLogFormatter auditLogFormatter)
+ throws Exception {
+ GroupConfig groupConfig = GroupConfig.loadForGroup(repository, uuid);
+ groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
+ commit(groupConfig);
+ return groupConfig.getLoadedGroup();
+ }
+
+ private Optional loadGroup(AccountGroup.UUID uuid) throws Exception {
+ GroupConfig groupConfig = GroupConfig.loadForGroup(repository, uuid);
+ return groupConfig.getLoadedGroup();
+ }
+
+ private void commit(GroupConfig groupConfig) throws IOException {
+ try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) {
+ groupConfig.commit(metaDataUpdate);
+ }
+ }
+
private MetaDataUpdate createMetaDataUpdate() {
- TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
PersonIdent serverIdent =
- new PersonIdent("Gerrit Server", "noreply@gerritcodereview.com", TimeUtil.nowTs(), tz);
+ new PersonIdent(
+ "Gerrit Server", "noreply@gerritcodereview.com", TimeUtil.nowTs(), timeZone);
MetaDataUpdate metaDataUpdate =
new MetaDataUpdate(
@@ -286,4 +1634,52 @@ public class GroupConfigTest {
metaDataUpdate.getCommitBuilder().setAuthor(serverIdent);
return metaDataUpdate;
}
+
+ private RevCommit getLatestCommitForGroup(AccountGroup.UUID uuid) throws IOException {
+ Ref ref = repository.exactRef(RefNames.refsGroups(uuid));
+ assertWithMessage("Precondition: Assumed that ref for group " + uuid + " exists.")
+ .that(ref.getObjectId())
+ .isNotNull();
+
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ return revWalk.parseCommit(ref.getObjectId());
+ }
+ }
+
+ private static Account createAccount(Account.Id id, String name) {
+ Account account = new Account(id, TimeUtil.nowTs());
+ account.setFullName(name);
+ return account;
+ }
+
+ private static GroupDescription.Basic createGroup(AccountGroup.UUID uuid, String name) {
+ return new GroupDescription.Basic() {
+ @Override
+ public AccountGroup.UUID getGroupUUID() {
+ return uuid;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Nullable
+ @Override
+ public String getEmailAddress() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getUrl() {
+ return null;
+ }
+ };
+ }
+
+ private static OptionalSubject assertThatGroup(
+ Optional loadedGroup) {
+ return assertThat(loadedGroup, InternalGroupSubject::assertThat);
+ }
}