blob: 7b41ee0fb455e8996b5c6df25b632daa4cc39efd [file] [log] [blame]
// Copyright (C) 2016 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.acceptance.server.notedb;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage.REVIEW_DB;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.RepoRefCache;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NoteDbChangeState;
import com.google.gerrit.testutil.NoteDbMode;
import com.google.gerrit.testutil.TestTimeUtil;
import com.google.gwtorm.server.OrmRuntimeException;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.Repository;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class NoteDbPrimaryIT extends AbstractDaemonTest {
@Inject
private AllUsersName allUsers;
@Before
public void setUp() throws Exception {
assume().that(NoteDbMode.get()).isEqualTo(NoteDbMode.READ_WRITE);
db = ReviewDbUtil.unwrapDb(db);
TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
}
@After
public void tearDown() {
TestTimeUtil.useSystemTime();
}
@Test
public void updateChange() throws Exception {
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
setNoteDbPrimary(id);
gApi.changes().id(id.get()).current().review(ReviewInput.approve());
gApi.changes().id(id.get()).current().submit();
ChangeInfo info = gApi.changes().id(id.get()).get();
assertThat(info.status).isEqualTo(ChangeStatus.MERGED);
ApprovalInfo approval =
Iterables.getOnlyElement(info.labels.get("Code-Review").all);
assertThat(approval._accountId).isEqualTo(admin.id.get());
assertThat(approval.value).isEqualTo(2);
assertThat(info.messages).hasSize(3);
assertThat(Iterables.getLast(info.messages).message)
.isEqualTo("Change has been successfully merged by " + admin.fullName);
ChangeNotes notes = notesFactory.create(db, project, id);
assertThat(notes.getChange().getStatus()).isEqualTo(Change.Status.MERGED);
assertThat(notes.getChange().getNoteDbState())
.isEqualTo(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
// Writes weren't reflected in ReviewDb.
assertThat(db.changes().get(id).getStatus()).isEqualTo(Change.Status.NEW);
assertThat(db.patchSetApprovals().byChange(id)).isEmpty();
assertThat(db.changeMessages().byChange(id)).hasSize(1);
}
@Test
public void deleteDraftComment() throws Exception {
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
setNoteDbPrimary(id);
DraftInput din = new DraftInput();
din.path = PushOneCommit.FILE_NAME;
din.line = 1;
din.message = "A comment";
gApi.changes().id(id.get()).current().createDraft(din);
CommentInfo di = Iterables.getOnlyElement(
gApi.changes().id(id.get()).current().drafts()
.get(PushOneCommit.FILE_NAME));
assertThat(di.message).isEqualTo(din.message);
assertThat(
db.patchComments().draftByChangeFileAuthor(id, din.path, admin.id))
.isEmpty();
gApi.changes().id(id.get()).current().draft(di.id).delete();
assertThat(gApi.changes().id(id.get()).current().drafts()).isEmpty();
}
@Test
public void deleteVote() throws Exception {
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
setNoteDbPrimary(id);
gApi.changes().id(id.get()).current().review(ReviewInput.approve());
List<ApprovalInfo> approvals =
gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
assertThat(approvals).hasSize(1);
assertThat(approvals.get(0).value).isEqualTo(2);
gApi.changes().id(id.get()).reviewer(admin.id.toString())
.deleteVote("Code-Review");
approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
assertThat(approvals).hasSize(1);
assertThat(approvals.get(0).value).isEqualTo(0);
}
@Test
public void deleteVoteViaReview() throws Exception {
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
setNoteDbPrimary(id);
gApi.changes().id(id.get()).current().review(ReviewInput.approve());
List<ApprovalInfo> approvals =
gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
assertThat(approvals).hasSize(1);
assertThat(approvals.get(0).value).isEqualTo(2);
gApi.changes().id(id.get()).current().review(ReviewInput.noScore());
approvals = gApi.changes().id(id.get()).get().labels.get("Code-Review").all;
assertThat(approvals).hasSize(1);
assertThat(approvals.get(0).value).isEqualTo(0);
}
@Test
public void deleteReviewer() throws Exception {
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
setNoteDbPrimary(id);
gApi.changes().id(id.get()).addReviewer(user.id.toString());
assertThat(getReviewers(id)).containsExactly(user.id);
gApi.changes().id(id.get()).reviewer(user.id.toString()).remove();
assertThat(getReviewers(id)).isEmpty();
}
@Test
public void readOnlyReviewDb() throws Exception {
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
testReadOnly(id);
}
@Test
public void readOnlyNoteDb() throws Exception {
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
setNoteDbPrimary(id);
testReadOnly(id);
}
private void testReadOnly(Change.Id id) throws Exception {
Timestamp before = TimeUtil.nowTs();
Timestamp until = new Timestamp(before.getTime() + 1000 * 3600);
// Set read-only.
Change c = db.changes().get(id);
assertThat(c).named("change " + id).isNotNull();
NoteDbChangeState state = NoteDbChangeState.parse(c);
state = state.withReadOnlyUntil(until);
c.setNoteDbState(state.toString());
db.changes().update(Collections.singleton(c));
assertThat(gApi.changes().id(id.get()).get().subject)
.isEqualTo(PushOneCommit.SUBJECT);
assertThat(gApi.changes().id(id.get()).get().topic).isNull();
try {
gApi.changes().id(id.get()).topic("a-topic");
assert_().fail("expected read-only exception");
} catch (RestApiException e) {
Optional<Throwable> oe = Throwables.getCausalChain(e).stream()
.filter(x -> x instanceof OrmRuntimeException).findFirst();
assertThat(oe.isPresent())
.named("OrmRuntimeException in causal chain of " + e)
.isTrue();
assertThat(oe.get().getMessage()).contains("read-only");
}
assertThat(gApi.changes().id(id.get()).get().topic).isNull();
TestTimeUtil.setClock(new Timestamp(until.getTime() + 1000));
assertThat(gApi.changes().id(id.get()).get().subject)
.isEqualTo(PushOneCommit.SUBJECT);
gApi.changes().id(id.get()).topic("a-topic");
assertThat(gApi.changes().id(id.get()).get().topic).isEqualTo("a-topic");
}
private void setNoteDbPrimary(Change.Id id) throws Exception {
Change c = db.changes().get(id);
assertThat(c).named("change " + id).isNotNull();
NoteDbChangeState state = NoteDbChangeState.parse(c);
assertThat(state.getPrimaryStorage())
.named("storage of " + id)
.isEqualTo(REVIEW_DB);
try (Repository changeRepo = repoManager.openRepository(c.getProject());
Repository allUsersRepo = repoManager.openRepository(allUsers)) {
assertThat(
state.isUpToDate(
new RepoRefCache(changeRepo), new RepoRefCache(allUsersRepo)))
.named("change " + id + " up to date")
.isTrue();
}
c.setNoteDbState(NoteDbChangeState.NOTE_DB_PRIMARY_STATE);
db.changes().update(Collections.singleton(c));
}
private List<Account.Id> getReviewers(Change.Id id) throws Exception {
return gApi.changes().id(id.get()).get()
.reviewers.values().stream()
.flatMap(Collection::stream)
.map(a -> new Account.Id(a._accountId))
.collect(toList());
}
}