@Test public void prepopulatedFields() throws Exception { assume().that(notesMigration.enabled()).isFalse(); TestRepository<Repo> repo = createProject("repo"); Change change = newChange(repo, null, null, null, null).insert(); db = new DisabledReviewDb(); requestContext.setContext(newRequestContext(userId)); // Use QueryProcessor directly instead of API so we get ChangeDatas back. List<ChangeData> cds = queryProcessor.queryChanges(queryBuilder.parse(change.getId().toString())).changes(); assertThat(cds).hasSize(1); ChangeData cd = cds.get(0); cd.change(); cd.patchSets(); cd.currentApprovals(); cd.changedLines(); cd.reviewedBy(); // TODO(dborowitz): Swap out GitRepositoryManager somehow? Will probably be // necessary for notedb anyway. cd.isMergeable(); // Don't use ExpectedException since that wouldn't distinguish between // failures here and on the previous calls. try { cd.messages(); } catch (AssertionError e) { assertThat(e.getMessage()).isEqualTo(DisabledReviewDb.MESSAGE); } }
@Test public void byCommentBy() throws Exception { TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); Change change2 = newChange(repo, null, null, null, null).insert(); int user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId().get(); ReviewInput input = new ReviewInput(); input.message = "toplevel"; ReviewInput.CommentInput comment = new ReviewInput.CommentInput(); comment.line = 1; comment.message = "inline"; input.comments = ImmutableMap.<String, List<ReviewInput.CommentInput>>of( Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput>of(comment)); gApi.changes().id(change1.getId().get()).current().review(input); input = new ReviewInput(); input.message = "toplevel"; gApi.changes().id(change2.getId().get()).current().review(input); assertQuery("commentby:" + userId.get(), change2, change1); assertQuery("commentby:" + user2); }
private Iterable<ChangeData> byCommitsOnBranchNotMergedFromDatabase( Repository repo, ReviewDb db, Branch.NameKey branch, List<String> hashes) throws OrmException, IOException { Set<Change.Id> changeIds = Sets.newHashSetWithExpectedSize(hashes.size()); String lastPrefix = null; for (Ref ref : repo.getRefDatabase().getRefs(RefNames.REFS_CHANGES).values()) { String r = ref.getName(); if ((lastPrefix != null && r.startsWith(lastPrefix)) || !hashes.contains(ref.getObjectId().name())) { continue; } Change.Id id = Change.Id.fromRef(r); if (id == null) { continue; } if (changeIds.add(id)) { lastPrefix = r.substring(0, r.lastIndexOf('/')); } } List<ChangeData> cds = new ArrayList<>(hashes.size()); for (Change c : db.changes().get(changeIds)) { if (c.getDest().equals(branch) && c.getStatus() != Change.Status.MERGED) { cds.add(changeDataFactory.create(db, c)); } } return cds; }
@Test public void limit() throws Exception { TestRepository<Repo> repo = createProject("repo"); Change last = null; int n = 5; for (int i = 0; i < n; i++) { last = newChange(repo, null, null, null, null).insert(); } for (int i = 1; i <= n + 2; i++) { int expectedSize; Boolean expectedMoreChanges; if (i < n) { expectedSize = i; expectedMoreChanges = true; } else { expectedSize = n; expectedMoreChanges = null; } String q = "status:new limit:" + i; List<ChangeInfo> results = newQuery(q).get(); assertThat(results).named(q).hasSize(expectedSize); assertThat(results.get(results.size() - 1)._moreChanges) .named(q) .isEqualTo(expectedMoreChanges); assertThat(results.get(0)._number).isEqualTo(last.getId().get()); } }
private static ChangeKind getChangeKindInternal( ChangeKindCache cache, ReviewDb db, Change change, PatchSet patch, ChangeData.Factory changeDataFactory, ProjectCache projectCache, GitRepositoryManager repoManager) { Repository repo = null; // TODO - dborowitz: add NEW_CHANGE type for default. ChangeKind kind = ChangeKind.REWORK; // Trivial case: if we're on the first patch, we don't need to open // the repository. if (patch.getId().get() > 1) { try { ProjectState projectState = projectCache.checkedGet(change.getProject()); repo = repoManager.openRepository(change.getProject()); ChangeData cd = changeDataFactory.create(db, change); Collection<PatchSet> patchSetCollection = cd.patches(); PatchSet priorPs = patch; for (PatchSet ps : patchSetCollection) { if (ps.getId().get() < patch.getId().get() && (ps.getId().get() > priorPs.getId().get() || priorPs == patch)) { // We only want the previous patch set, so walk until the last one priorPs = ps; } } // If we still think the previous patch is the current patch, // we only have one patch set. Return the default. // This can happen if a user creates a draft, uploads a second patch, // and deletes the draft. if (priorPs != patch) { kind = cache.getChangeKind( projectState, repo, ObjectId.fromString(priorPs.getRevision().get()), ObjectId.fromString(patch.getRevision().get())); } } catch (IOException | OrmException e) { // Do nothing; assume we have a complex change log.warn( "Unable to get change kind for patchSet " + patch.getPatchSetId() + "of change " + change.getChangeId(), e); } finally { if (repo != null) { repo.close(); } } } return kind; }
synchronized Set<Change> addAll(Collection<Change> changes, boolean force) { Set<Change> r = Sets.newLinkedHashSetWithExpectedSize(changes.size()); for (Change c : changes) { if (force ? forcePending.add(c.getId()) : pending.add(c.getId())) { r.add(c); } } return r; }
private void updateChangeStatus(OpenBranch ob, List<ChangeData> submitted, IdentifiedUser caller) throws ResourceConflictException { List<Change.Id> problemChanges = new ArrayList<>(submitted.size()); logDebug("Updating change status for {} changes", submitted.size()); for (ChangeData cd : submitted) { Change.Id id = cd.getId(); try { Change c = cd.change(); CodeReviewCommit commit = commits.get(id); CommitMergeStatus s = commit != null ? commit.getStatusCode() : null; logDebug("Status of change {} ({}) on {}: {}", id, commit.name(), c.getDest(), s); checkState(s != null, "status not set for change %s; expected to previously fail fast", id); setApproval(cd, caller); ObjectId mergeResultRev = ob.mergeTip != null ? ob.mergeTip.getMergeResults().get(commit) : null; String txt = s.getMessage(); // The change notes must be forcefully reloaded so that the SUBMIT // approval that we added earlier is visible commit.notes().reload(); if (s == CommitMergeStatus.CLEAN_MERGE) { setMerged(c, message(c, txt + getByAccountName(commit)), mergeResultRev); } else if (s == CommitMergeStatus.CLEAN_REBASE || s == CommitMergeStatus.CLEAN_PICK) { setMerged( c, message(c, txt + " as " + commit.name() + getByAccountName(commit)), mergeResultRev); } else if (s == CommitMergeStatus.ALREADY_MERGED) { setMerged(c, null, mergeResultRev); } else { throw new IllegalStateException( "unexpected status " + s + " for change " + c.getId() + "; expected to previously fail fast"); } } catch (OrmException | IOException err) { logWarn("Error updating change status for " + id, err); problemChanges.add(id); } } if (problemChanges.isEmpty()) { return; } StringBuilder msg = new StringBuilder("Error updating status of change"); if (problemChanges.size() == 1) { msg.append(' ').append(problemChanges.iterator().next()); } else { msg.append('s').append(Joiner.on(", ").join(problemChanges)); } throw new ResourceConflictException(msg.toString()); }
@Test public void byId() throws Exception { TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); Change change2 = newChange(repo, null, null, null, null).insert(); assertQuery("12345"); assertQuery(change1.getId().get(), change1); assertQuery(change2.getId().get(), change2); }
@Test public void byKey() throws Exception { TestRepository<Repo> repo = createProject("repo"); Change change = newChange(repo, null, null, null, null).insert(); String key = change.getKey().get(); assertQuery("I0000000000000000000000000000000000000000"); for (int i = 0; i <= 36; i++) { String q = key.substring(0, 41 - i); assertQuery(q, change); } }
private ChangeMessage message(Change c, String body) { String uuid; try { uuid = ChangeUtil.messageUUID(db); } catch (OrmException e) { return null; } ChangeMessage m = new ChangeMessage( new ChangeMessage.Key(c.getId(), uuid), null, TimeUtil.nowTs(), c.currentPatchSetId()); m.setMessage(body); return m; }
private void matchChange(Set<Change.Id> matched, Change change) { try { if (change != null && inProject(change) && changeControlFactory.controlFor(change, userProvider.get()).isVisible(db)) { matched.add(change.getId()); } } catch (NoSuchChangeException e) { // Ignore this change. } catch (OrmException e) { log.warn("Error reading change " + change.getId(), e); } }
protected Change newPatchSet(TestRepository<Repo> repo, Change c) throws Exception { // Add a new file so the patch set is not a trivial rebase, to avoid default // Code-Review label copying. int n = c.currentPatchSetId().get() + 1; RevCommit commit = repo.parseBody(repo.commit().message("message").add("file" + n, "contents " + n).create()); ChangeControl ctl = changeControlFactory.controlFor(c.getId(), user); return patchSetFactory .create(repo.getRepository(), repo.getRevWalk(), ctl, commit) .setSendMail(false) .setRunHooks(false) .setValidatePolicy(ValidatePolicy.NONE) .insert(); }
@Test public void byTopic() throws Exception { TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.getChange(); change1.setTopic("feature1"); ins1.insert(); ChangeInserter ins2 = newChange(repo, null, null, null, null); Change change2 = ins2.getChange(); change2.setTopic("feature2"); ins2.insert(); ChangeInserter ins3 = newChange(repo, null, null, null, null); Change change3 = ins3.getChange(); change3.setTopic("Cherrypick-feature2"); ins3.insert(); ChangeInserter ins4 = newChange(repo, null, null, null, null); Change change4 = ins4.getChange(); change4.setTopic("feature2-fixup"); ins4.insert(); Change change5 = newChange(repo, null, null, null, null).insert(); assertQuery("intopic:foo"); assertQuery("intopic:feature1", change1); assertQuery("intopic:feature2", change4, change3, change2); assertQuery("topic:feature2", change2); assertQuery("intopic:feature2", change4, change3, change2); assertQuery("intopic:fixup", change4); assertQuery("topic:\"\"", change5); assertQuery("intopic:\"\"", change5); }
private List<Change> setUpHashtagChanges() throws Exception { TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); Change change2 = newChange(repo, null, null, null, null).insert(); HashtagsInput in = new HashtagsInput(); in.add = ImmutableSet.of("foo"); gApi.changes().id(change1.getId().get()).setHashtags(in); in.add = ImmutableSet.of("foo", "bar", "a tag"); gApi.changes().id(change2.getId().get()).setHashtags(in); return ImmutableList.of(change1, change2); }
private void postAdd(Change change, PostResult result) throws OrmException, EmailException { if (result.reviewers.isEmpty()) { return; } // Execute hook for added reviewers // PatchSet patchSet = db.get().patchSets().get(change.currentPatchSetId()); for (AccountInfo info : result.reviewers) { Account account = accountCache.get(info._id).getAccount(); hooks.doReviewerAddedHook(change, account, patchSet, db.get()); } // Email the reviewers // // The user knows they added themselves, don't bother emailing them. List<Account.Id> added = Lists.newArrayListWithCapacity(result.reviewers.size()); for (AccountInfo info : result.reviewers) { if (!info._id.equals(currentUser.getAccountId())) { added.add(info._id); } } if (!added.isEmpty()) { AddReviewerSender cm; cm = addReviewerSenderFactory.create(change); cm.setFrom(currentUser.getAccountId()); cm.addReviewers(added); cm.send(); } }
/** * Evaluate the submit rules. * * @return List of {@link SubmitRecord} objects returned from the evaluated rules, including any * errors. */ public List<SubmitRecord> evaluate() { Change c = control.getChange(); if (!allowClosed && c.getStatus().isClosed()) { SubmitRecord rec = new SubmitRecord(); rec.status = SubmitRecord.Status.CLOSED; return Collections.singletonList(rec); } if (!allowDraft) { if (c.getStatus() == Change.Status.DRAFT) { return cannotSubmitDraft(); } try { initPatchSet(); } catch (OrmException e) { return ruleError("Error looking up patch set " + control.getChange().currentPatchSetId()); } if (patchSet.isDraft()) { return cannotSubmitDraft(); } } List<Term> results; try { results = evaluateImpl( "locate_submit_rule", "can_submit", "locate_submit_filter", "filter_submit_results", control.getUser()); } catch (RuleEvalException e) { return ruleError(e.getMessage(), e); } if (results.isEmpty()) { // This should never occur. A well written submit rule will always produce // at least one result informing the caller of the labels that are // required for this change to be submittable. Each label will indicate // whether or not that is actually possible given the permissions. return ruleError( String.format( "Submit rule '%s' for change %s of %s has " + "no solution.", getSubmitRuleName(), cd.getId(), getProjectName())); } return resultsToSubmitRecord(getSubmitRule(), results); }
private boolean inProject(Change change) { if (projectControl != null) { return projectControl.getProject().getNameKey().equals(change.getProject()); } else { // No --project option, so they want every project. return true; } }
@Test public void explicitVisibleTo() throws Exception { TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, userId.get(), null).insert(); ChangeInserter ins2 = newChange(repo, null, null, userId.get(), null); Change change2 = ins2.getChange(); change2.setStatus(Change.Status.DRAFT); ins2.insert(); String q = "project:repo"; assertQuery(q, change2, change1); // Second user cannot see first user's drafts. Account.Id user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId(); assertQuery(q + " visibleto:" + user2.get(), change1); }
@Test public void byStatus() throws Exception { TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.getChange(); change1.setStatus(Change.Status.NEW); ins1.insert(); ChangeInserter ins2 = newChange(repo, null, null, null, null); Change change2 = ins2.getChange(); change2.setStatus(Change.Status.MERGED); ins2.insert(); assertQuery("status:new", change1); assertQuery("status:NEW", change1); assertQuery("is:new", change1); assertQuery("status:merged", change2); assertQuery("is:merged", change2); }
private void setMerged(Change c, ChangeMessage msg, ObjectId mergeResultRev) throws OrmException, IOException { logDebug("Setting change {} merged", c.getId()); ChangeUpdate update = null; final PatchSetApproval submitter; PatchSet merged; try { db.changes().beginTransaction(c.getId()); // We must pull the patchset out of commits, because the patchset ID is // modified when using the cherry-pick merge strategy. CodeReviewCommit commit = commits.get(c.getId()); PatchSet.Id mergedId = commit.change().currentPatchSetId(); merged = db.patchSets().get(mergedId); c = setMergedPatchSet(c.getId(), mergedId); submitter = approvalsUtil.getSubmitter(db, commit.notes(), mergedId); ChangeControl control = commit.getControl(); update = updateFactory.create(control, c.getLastUpdatedOn()); // TODO(yyonas): we need to be able to change the author of the message // is not the person for whom the change was made. addMergedMessage // did this in the past. if (msg != null) { cmUtil.addChangeMessage(db, update, msg); } db.commit(); } finally { db.rollback(); } update.commit(); indexer.index(db, c); try { mergedSenderFactory .create(c.getId(), submitter != null ? submitter.getAccountId() : null) .sendAsync(); } catch (Exception e) { log.error("Cannot email merged notification for " + c.getId(), e); } if (submitter != null && mergeResultRev != null) { try { hooks.doChangeMergedHook( c, accountCache.get(submitter.getAccountId()).getAccount(), merged, db, mergeResultRev.name()); } catch (OrmException ex) { logError("Cannot run hook for submitted patch set " + c.getId(), ex); } } }
@Test public void byLabel() throws Exception { accountManager.authenticate(AuthRequest.forUser("anotheruser")); TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins = newChange(repo, null, null, null, null); Change change = ins.insert(); gApi.changes() .id(change.getId().get()) .current() .review(new ReviewInput().label("Code-Review", 1)); assertQuery("label:Code-Review=-2"); assertQuery("label:Code-Review-2"); assertQuery("label:Code-Review=-1"); assertQuery("label:Code-Review-1"); assertQuery("label:Code-Review=0"); assertQuery("label:Code-Review=+1", change); assertQuery("label:Code-Review=1", change); assertQuery("label:Code-Review+1", change); assertQuery("label:Code-Review=+2"); assertQuery("label:Code-Review=2"); assertQuery("label:Code-Review+2"); assertQuery("label:Code-Review>=0", change); assertQuery("label:Code-Review>0", change); assertQuery("label:Code-Review>=1", change); assertQuery("label:Code-Review>1"); assertQuery("label:Code-Review>=2"); assertQuery("label: Code-Review<=2", change); assertQuery("label: Code-Review<2", change); assertQuery("label: Code-Review<=1", change); assertQuery("label:Code-Review<1"); assertQuery("label:Code-Review<=0"); assertQuery("label:Code-Review=+1,anotheruser"); assertQuery("label:Code-Review=+1,user", change); assertQuery("label:Code-Review=+1,user=user", change); assertQuery("label:Code-Review=+1,Administrators", change); assertQuery("label:Code-Review=+1,group=Administrators", change); }
@Test public void byComment() throws Exception { TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins = newChange(repo, null, null, null, null); Change change = ins.insert(); ReviewInput input = new ReviewInput(); input.message = "toplevel"; ReviewInput.CommentInput comment = new ReviewInput.CommentInput(); comment.line = 1; comment.message = "inline"; input.comments = ImmutableMap.<String, List<ReviewInput.CommentInput>>of( Patch.COMMIT_MSG, ImmutableList.<ReviewInput.CommentInput>of(comment)); gApi.changes().id(change.getId().get()).current().review(input); assertQuery("comment:foo"); assertQuery("comment:toplevel", change); assertQuery("comment:inline", change); }
protected boolean isVisibleTo(Change change, CurrentUser user) throws OrmException { if (change == null) { return false; } ProjectState pe = projectCache.get(change.getProject()); if (pe == null) { return false; } ProjectControl pc = pe.controlFor(user); ReviewDb db = dbProvider.get(); return pc.controlFor(db, change).isVisible(db); }
@Test public void updatedOrderWithSubMinuteResolution() throws Exception { TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.insert(); Change change2 = newChange(repo, null, null, null, null).insert(); assertThat(lastUpdatedMs(change1)).isLessThan(lastUpdatedMs(change2)); assertQuery("status:new", change2, change1); gApi.changes().id(change1.getId().get()).current().review(new ReviewInput()); change1 = db.changes().get(change1.getId()); assertThat(lastUpdatedMs(change1)).isGreaterThan(lastUpdatedMs(change2)); assertThat(lastUpdatedMs(change1) - lastUpdatedMs(change2)) .isLessThan(MILLISECONDS.convert(1, MINUTES)); // change1 moved to the top. assertQuery("status:new", change1, change2); }
@Test public void byStatusClosed() throws Exception { TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.getChange(); change1.setStatus(Change.Status.MERGED); ins1.insert(); ChangeInserter ins2 = newChange(repo, null, null, null, null); Change change2 = ins2.getChange(); change2.setStatus(Change.Status.ABANDONED); ins2.insert(); ChangeInserter ins3 = newChange(repo, null, null, null, null); Change change3 = ins3.getChange(); change3.setStatus(Change.Status.NEW); ins3.insert(); Change[] expected = new Change[] {change2, change1}; assertQuery("status:closed", expected); assertQuery("status:CLOSED", expected); assertQuery("status:c", expected); assertQuery("status:cl", expected); assertQuery("status:clo", expected); assertQuery("status:clos", expected); assertQuery("status:close", expected); assertQuery("status:closed", expected); assertQuery("is:closed", expected); }
@Test public void byStatusOpen() throws Exception { TestRepository<Repo> repo = createProject("repo"); ChangeInserter ins1 = newChange(repo, null, null, null, null); Change change1 = ins1.getChange(); change1.setStatus(Change.Status.NEW); ins1.insert(); ChangeInserter ins2 = newChange(repo, null, null, null, null); Change change2 = ins2.getChange(); change2.setStatus(Change.Status.DRAFT); ins2.insert(); ChangeInserter ins3 = newChange(repo, null, null, null, null); Change change3 = ins3.getChange(); change3.setStatus(Change.Status.MERGED); ins3.insert(); Change[] expected = new Change[] {change2, change1}; assertQuery("status:open", expected); assertQuery("status:OPEN", expected); assertQuery("status:o", expected); assertQuery("status:op", expected); assertQuery("status:ope", expected); assertQuery("status:pending", expected); assertQuery("status:PENDING", expected); assertQuery("status:p", expected); assertQuery("status:pe", expected); assertQuery("status:pen", expected); assertQuery("is:open", expected); }
@Test public void byTriplet() throws Exception { TestRepository<Repo> repo = createProject("repo"); Change change = newChange(repo, null, null, null, "branch").insert(); String k = change.getKey().get(); assertQuery("repo~branch~" + k, change); assertQuery("change:repo~branch~" + k, change); assertQuery("repo~refs/heads/branch~" + k, change); assertQuery("change:repo~refs/heads/branch~" + k, change); assertQuery("repo~branch~" + k.substring(0, 10), change); assertQuery("change:repo~branch~" + k.substring(0, 10), change); assertQuery("foo~bar"); assertBadQuery("change:foo~bar"); assertQuery("otherrepo~branch~" + k); assertQuery("change:otherrepo~branch~" + k); assertQuery("repo~otherbranch~" + k); assertQuery("change:repo~otherbranch~" + k); assertQuery("repo~branch~I0000000000000000000000000000000000000000"); assertQuery("change:repo~branch~I0000000000000000000000000000000000000000"); }
private Multimap<ObjectId, PatchSet.Id> getRevisions(OpenRepo or, Collection<ChangeData> cds) throws IntegrationException { try { List<String> refNames = new ArrayList<>(cds.size()); for (ChangeData cd : cds) { Change c = cd.change(); if (c != null) { refNames.add(c.currentPatchSetId().toRefName()); } } Multimap<ObjectId, PatchSet.Id> revisions = HashMultimap.create(cds.size(), 1); for (Map.Entry<String, Ref> e : or.repo .getRefDatabase() .exactRef(refNames.toArray(new String[refNames.size()])) .entrySet()) { revisions.put(e.getValue().getObjectId(), PatchSet.Id.fromRef(e.getKey())); } return revisions; } catch (IOException | OrmException e) { throw new IntegrationException("Failed to validate changes", e); } }
public void fire(Change change, PatchSet ps, Account restorer, String reason, Timestamp when) { if (!listeners.iterator().hasNext()) { return; } try { fire( util.changeInfo(change), util.revisionInfo(change.getProject(), ps), util.accountInfo(restorer), reason, when); } catch (PatchListNotAvailableException | GpgException | IOException | OrmException e) { log.error("Couldn't fire event", e); } }
@Test public void reviewedBy() throws Exception { clockStepMs = MILLISECONDS.convert(2, MINUTES); TestRepository<Repo> repo = createProject("repo"); Change change1 = newChange(repo, null, null, null, null).insert(); Change change2 = newChange(repo, null, null, null, null).insert(); Change change3 = newChange(repo, null, null, null, null).insert(); gApi.changes().id(change1.getId().get()).current().review(new ReviewInput().message("comment")); Account.Id user2 = accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId(); requestContext.setContext(newRequestContext(user2)); gApi.changes().id(change2.getId().get()).current().review(new ReviewInput().message("comment")); PatchSet.Id ps3_1 = change3.currentPatchSetId(); change3 = newPatchSet(repo, change3); assertThat(change3.currentPatchSetId()).isNotEqualTo(ps3_1); // Response to previous patch set still counts as reviewing. gApi.changes() .id(change3.getId().get()) .revision(ps3_1.get()) .review(new ReviewInput().message("comment")); List<ChangeInfo> actual; actual = assertQuery(newQuery("is:reviewed").withOption(REVIEWED), change3, change2); assertThat(actual.get(0).reviewed).isTrue(); assertThat(actual.get(1).reviewed).isTrue(); actual = assertQuery(newQuery("-is:reviewed").withOption(REVIEWED), change1); assertThat(actual.get(0).reviewed).isNull(); actual = assertQuery("reviewedby:" + userId.get()); actual = assertQuery(newQuery("reviewedby:" + user2.get()).withOption(REVIEWED), change3, change2); assertThat(actual.get(0).reviewed).isTrue(); assertThat(actual.get(1).reviewed).isTrue(); }