blob: 709be8ebec2ef75023fd047553b0076c94a6ce5d [file] [log] [blame]
// 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.schema;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.accounts.AccountInput;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroupById;
import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerId;
import com.google.gerrit.server.config.GerritServerIdProvider;
import com.google.gerrit.server.git.CommitUtil;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.group.db.AuditLogFormatter;
import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryTestEnvironment;
import com.google.gerrit.testing.TestTimeUtil;
import com.google.gerrit.testing.TestTimeUtil.TempClockStep;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
public class GroupRebuilderIT extends GerritBaseTests {
private static Config createConfigWithServerId() {
Config config = new Config();
config.setString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY, "1234567");
return config;
}
@Rule
public InMemoryTestEnvironment testEnv =
new InMemoryTestEnvironment(GroupRebuilderIT::createConfigWithServerId);
@Inject private GroupsMigration migration;
@Inject private GerritApi gApi;
@Inject private ReviewDb db;
@Inject private GitRepositoryManager repoManager;
@Inject private AllUsersName allUsersName;
@Inject private IdentifiedUser currentUser;
@Inject private @GerritServerId String serverId;
@Inject private AccountCache accountCache;
@Inject private @ServerInitiated AccountsUpdate accountsUpdate;
@Inject private GroupBackend groupBackend;
@Inject private GroupBundle.Factory bundleFactory;
@Inject private @GerritPersonIdent Provider<PersonIdent> serverIdent;
private GroupRebuilder rebuilder;
@Before
public void setup() throws Exception {
// This test is explicitly testing the migration from ReviewDb to NoteDb, and handles reading
// from NoteDb manually. It should work regardless of the value of noteDb.groups.write, however.
assume().that(migration.readFromNoteDb()).isFalse();
accountsUpdate.update(
"Set Name for CurrentUser", currentUser.getAccountId(), u -> u.setFullName("current"));
AuditLogFormatter auditLogFormatter =
AuditLogFormatter.createBackedBy(accountCache, groupBackend, serverId);
rebuilder = new GroupRebuilder(serverIdent.get(), allUsersName, auditLogFormatter);
}
@Before
public void setTimeForTesting() {
TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
}
@After
public void resetTime() {
TestTimeUtil.useSystemTime();
}
@Test
public void basicGroupProperties() throws Exception {
GroupInfo createdGroup = gApi.groups().create("group").get();
GroupBundle reviewDbBundle =
GroupBundle.Factory.fromReviewDb(db, new AccountGroup.UUID(createdGroup.id));
deleteGroupRefs(reviewDbBundle);
assertMigratedCleanly(rebuild(reviewDbBundle), reviewDbBundle);
}
@Test
public void logFormat() throws Exception {
AccountInfo user1 = createAccount("user1");
AccountInfo user2 = createAccount("user2");
GroupInfo group1 = gApi.groups().create("group1").get();
GroupInfo group2 = gApi.groups().create("group2").get();
try (TempClockStep step = TestTimeUtil.freezeClock()) {
gApi.groups()
.id(group1.id)
.addMembers(Integer.toString(user1._accountId), Integer.toString(user2._accountId));
}
TimeUtil.nowTs();
try (TempClockStep step = TestTimeUtil.freezeClock()) {
gApi.groups().id(group1.id).addGroups(group2.id, SystemGroupBackend.REGISTERED_USERS.get());
}
GroupBundle reviewDbBundle =
GroupBundle.Factory.fromReviewDb(db, new AccountGroup.UUID(group1.id));
deleteGroupRefs(reviewDbBundle);
GroupBundle noteDbBundle = rebuild(reviewDbBundle);
assertMigratedCleanly(noteDbBundle, reviewDbBundle);
ImmutableList<CommitInfo> log = log(group1);
assertThat(log).hasSize(4);
assertThat(log.get(0)).message().isEqualTo("Create group");
assertThat(log.get(0)).author().name().isEqualTo(serverIdent.get().getName());
assertThat(log.get(0)).author().email().isEqualTo(serverIdent.get().getEmailAddress());
assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.get().getTimeZoneOffset());
assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
assertThat(log.get(1))
.message()
.isEqualTo(
"Update group\n\nAdd: "
+ currentUser.getName()
+ " <"
+ currentUser.getAccountId()
+ "@"
+ serverId
+ ">");
assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
assertThat(log.get(2))
.message()
.isEqualTo(
"Update group\n"
+ "\n"
+ ("Add: user1 <" + user1._accountId + "@" + serverId + ">\n")
+ ("Add: user2 <" + user2._accountId + "@" + serverId + ">"));
assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
assertThat(log.get(3))
.message()
.isEqualTo(
"Update group\n"
+ "\n"
+ ("Add-group: " + group2.name + " <" + group2.id + ">\n")
+ ("Add-group: Registered Users <global:Registered-Users>"));
assertThat(log.get(3)).author().name().isEqualTo(currentUser.getName());
assertThat(log.get(3)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
assertThat(log.get(3)).committer().hasSameDateAs(log.get(3).author);
}
@Test
public void unknownGroupUuid() throws Exception {
GroupInfo group = gApi.groups().create("group").get();
AccountGroup.UUID subgroupUuid = new AccountGroup.UUID("mybackend:foo");
AccountGroupById byId =
new AccountGroupById(
new AccountGroupById.Key(new AccountGroup.Id(group.groupId), subgroupUuid));
assertThat(groupBackend.handles(byId.getIncludeUUID())).isFalse();
db.accountGroupById().insert(Collections.singleton(byId));
AccountGroupByIdAud audit =
new AccountGroupByIdAud(byId, currentUser.getAccountId(), TimeUtil.nowTs());
db.accountGroupByIdAud().insert(Collections.singleton(audit));
GroupBundle reviewDbBundle =
GroupBundle.Factory.fromReviewDb(db, new AccountGroup.UUID(group.id));
deleteGroupRefs(reviewDbBundle);
GroupBundle noteDbBundle = rebuild(reviewDbBundle);
assertMigratedCleanly(noteDbBundle, reviewDbBundle);
ImmutableList<CommitInfo> log = log(group);
assertThat(log).hasSize(3);
assertThat(log.get(0)).message().isEqualTo("Create group");
assertThat(log.get(1))
.message()
.isEqualTo(
"Update group\n\nAdd: "
+ currentUser.getName()
+ " <"
+ currentUser.getAccountId()
+ "@"
+ serverId
+ ">");
assertThat(log.get(2))
.message()
.isEqualTo("Update group\n\nAdd-group: mybackend:foo <mybackend:foo>");
}
private void deleteGroupRefs(GroupBundle bundle) throws Exception {
try (Repository repo = repoManager.openRepository(allUsersName)) {
String refName = RefNames.refsGroups(bundle.uuid());
RefUpdate ru = repo.updateRef(refName);
ru.setForceUpdate(true);
Ref oldRef = repo.exactRef(refName);
if (oldRef == null) {
return;
}
ru.setExpectedOldObjectId(oldRef.getObjectId());
ru.setNewObjectId(ObjectId.zeroId());
assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
}
}
private GroupBundle rebuild(GroupBundle reviewDbBundle) throws Exception {
try (Repository repo = repoManager.openRepository(allUsersName)) {
rebuilder.rebuild(repo, reviewDbBundle, null);
return bundleFactory.fromNoteDb(repo, reviewDbBundle.uuid());
}
}
private void assertMigratedCleanly(GroupBundle noteDbBundle, GroupBundle expectedReviewDbBundle) {
assertThat(GroupBundle.compareWithAudits(expectedReviewDbBundle, noteDbBundle)).isEmpty();
}
private AccountInfo createAccount(String name) throws RestApiException {
AccountInput accountInput = new AccountInput();
accountInput.username = name;
accountInput.name = name;
return gApi.accounts().create(accountInput).get();
}
private ImmutableList<CommitInfo> log(GroupInfo g) throws Exception {
ImmutableList.Builder<CommitInfo> result = ImmutableList.builder();
List<Date> commitDates = new ArrayList<>();
try (Repository repo = repoManager.openRepository(allUsersName);
RevWalk rw = new RevWalk(repo)) {
Ref ref = repo.exactRef(RefNames.refsGroups(new AccountGroup.UUID(g.id)));
if (ref != null) {
rw.sort(RevSort.REVERSE);
rw.setRetainBody(true);
rw.markStart(rw.parseCommit(ref.getObjectId()));
for (RevCommit c : rw) {
result.add(CommitUtil.toCommitInfo(c));
commitDates.add(c.getCommitterIdent().getWhen());
}
}
}
assertThat(commitDates).named("commit timestamps for %s", result).isOrdered();
return result.build();
}
}