Add regression test for event JSON format

Downstream consumers of stream-events have a reasonable expectation that
this format won't change arbitrarily over time. Add a regression test
so refactorings don't break it.

Change-Id: I990c7f4246c88f5192af49a3e1fc331aa9397b33
diff --git a/javatests/com/google/gerrit/server/events/EventJsonTest.java b/javatests/com/google/gerrit/server/events/EventJsonTest.java
new file mode 100644
index 0000000..fb7ffd4
--- /dev/null
+++ b/javatests/com/google/gerrit/server/events/EventJsonTest.java
@@ -0,0 +1,624 @@
+// Copyright (C) 2019 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.events;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.reviewdb.client.Change.Status.NEW;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.truth.MapSubject;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.data.AccountAttribute;
+import com.google.gerrit.server.data.ChangeAttribute;
+import com.google.gerrit.server.data.RefUpdateAttribute;
+import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.testing.TestTimeUtil;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+
+public class EventJsonTest extends GerritBaseTests {
+  private static final String BRANCH = "mybranch";
+  private static final String CHANGE_ID = "Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+  private static final int CHANGE_NUM = 1000;
+  private static final double CHANGE_NUM_DOUBLE = CHANGE_NUM;
+  private static final String COMMIT_MESSAGE = "This is a test commit message";
+  private static final String PROJECT = "myproject";
+  private static final String REF = "refs/heads/" + BRANCH;
+  private static final double TS1 = 1.2543444E9;
+  private static final double TS2 = 1.254344401E9;
+  private static final String URL = "http://somewhere.com";
+
+  // Must match StreamEvents#gson. (In master, the definition is refactored to be hared.)
+  private final Gson gson =
+      new GsonBuilder()
+          .registerTypeAdapter(Supplier.class, new SupplierSerializer())
+          .registerTypeAdapter(Project.NameKey.class, new ProjectNameKeySerializer())
+          .create();
+
+  @Before
+  public void setTimeForTesting() {
+    TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
+  }
+
+  @Test
+  public void refUpdatedEvent() {
+    RefUpdatedEvent event = new RefUpdatedEvent();
+
+    RefUpdateAttribute refUpdatedAttribute = new RefUpdateAttribute();
+    refUpdatedAttribute.refName = REF;
+    event.refUpdate = createSupplier(refUpdatedAttribute);
+    event.submitter = newAccount("submitter");
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "submitter",
+                    ImmutableMap.builder()
+                        .put("name", event.submitter.get().name)
+                        .put("email", event.submitter.get().email)
+                        .put("username", event.submitter.get().username)
+                        .build())
+                .put("refUpdate", ImmutableMap.of("refName", REF))
+                .put("type", "ref-updated")
+                .put("eventCreatedOn", TS1)
+                .build());
+  }
+
+  @Test
+  public void patchSetCreatedEvent() {
+    Change change = newChange();
+    PatchSetCreatedEvent event = new PatchSetCreatedEvent(change);
+    event.change = asChangeAttribute(change);
+    event.uploader = newAccount("uploader");
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "uploader",
+                    ImmutableMap.builder()
+                        .put("name", event.uploader.get().name)
+                        .put("email", event.uploader.get().email)
+                        .put("username", event.uploader.get().username)
+                        .build())
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "patchset-created")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void assigneeChangedEvent() {
+    Change change = newChange();
+    AssigneeChangedEvent event = new AssigneeChangedEvent(change);
+    event.change = asChangeAttribute(change);
+    event.changer = newAccount("changer");
+    event.oldAssignee = newAccount("oldAssignee");
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "changer",
+                    ImmutableMap.builder()
+                        .put("name", event.changer.get().name)
+                        .put("email", event.changer.get().email)
+                        .put("username", event.changer.get().username)
+                        .build())
+                .put(
+                    "oldAssignee",
+                    ImmutableMap.builder()
+                        .put("name", event.oldAssignee.get().name)
+                        .put("email", event.oldAssignee.get().email)
+                        .put("username", event.oldAssignee.get().username)
+                        .build())
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "assignee-changed")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void changeDeletedEvent() {
+    Change change = newChange();
+    ChangeDeletedEvent event = new ChangeDeletedEvent(change);
+    event.change = asChangeAttribute(change);
+    event.deleter = newAccount("deleter");
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "deleter",
+                    ImmutableMap.builder()
+                        .put("name", event.deleter.get().name)
+                        .put("email", event.deleter.get().email)
+                        .put("username", event.deleter.get().username)
+                        .build())
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "change-deleted")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void hashtagsChangedEvent() {
+    Change change = newChange();
+    HashtagsChangedEvent event = new HashtagsChangedEvent(change);
+    event.change = asChangeAttribute(change);
+    event.editor = newAccount("editor");
+    event.added = new String[] {"added"};
+    event.removed = new String[] {"removed"};
+    event.hashtags = new String[] {"hashtags"};
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "editor",
+                    ImmutableMap.builder()
+                        .put("name", event.editor.get().name)
+                        .put("email", event.editor.get().email)
+                        .put("username", event.editor.get().username)
+                        .build())
+                .put("added", list("added"))
+                .put("removed", list("removed"))
+                .put("hashtags", list("hashtags"))
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "hashtags-changed")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void changeAbandonedEvent() {
+    Change change = newChange();
+    ChangeAbandonedEvent event = new ChangeAbandonedEvent(change);
+    event.change = asChangeAttribute(change);
+    event.abandoner = newAccount("abandoner");
+    event.reason = "some reason";
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "abandoner",
+                    ImmutableMap.builder()
+                        .put("name", event.abandoner.get().name)
+                        .put("email", event.abandoner.get().email)
+                        .put("username", event.abandoner.get().username)
+                        .build())
+                .put("reason", "some reason")
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "change-abandoned")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void changeMergedEvent() {
+    Change change = newChange();
+    ChangeMergedEvent event = new ChangeMergedEvent(change);
+    event.change = asChangeAttribute(change);
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "change-merged")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void changeRestoredEvent() {
+    Change change = newChange();
+    ChangeRestoredEvent event = new ChangeRestoredEvent(change);
+    event.change = asChangeAttribute(change);
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "change-restored")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void commentAddedEvent() {
+    Change change = newChange();
+    CommentAddedEvent event = new CommentAddedEvent(change);
+    event.change = asChangeAttribute(change);
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "comment-added")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void privateStateChangedEvent() {
+    Change change = newChange();
+    PrivateStateChangedEvent event = new PrivateStateChangedEvent(change);
+    event.change = asChangeAttribute(change);
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "private-state-changed")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void reviewerAddedEvent() {
+    Change change = newChange();
+    ReviewerAddedEvent event = new ReviewerAddedEvent(change);
+    event.change = asChangeAttribute(change);
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "reviewer-added")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void reviewerDeletedEvent() {
+    Change change = newChange();
+    ReviewerDeletedEvent event = new ReviewerDeletedEvent(change);
+    event.change = asChangeAttribute(change);
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "reviewer-deleted")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void voteDeletedEvent() {
+    Change change = newChange();
+    VoteDeletedEvent event = new VoteDeletedEvent(change);
+    event.change = asChangeAttribute(change);
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "vote-deleted")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void workInProgressStateChangedEvent() {
+    Change change = newChange();
+    WorkInProgressStateChangedEvent event = new WorkInProgressStateChangedEvent(change);
+    event.change = asChangeAttribute(change);
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "wip-state-changed")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void topicChangedEvent() {
+    Change change = newChange();
+    TopicChangedEvent event = new TopicChangedEvent(change);
+    event.change = asChangeAttribute(change);
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put(
+                    "change",
+                    ImmutableMap.builder()
+                        .put("project", PROJECT)
+                        .put("branch", BRANCH)
+                        .put("id", CHANGE_ID)
+                        .put("number", CHANGE_NUM_DOUBLE)
+                        .put("url", URL)
+                        .put("commitMessage", COMMIT_MESSAGE)
+                        .put("createdOn", TS1)
+                        .put("status", NEW.name())
+                        .build())
+                .put("project", PROJECT)
+                .put("refName", REF)
+                .put("changeKey", map("id", CHANGE_ID))
+                .put("type", "topic-changed")
+                .put("eventCreatedOn", TS2)
+                .build());
+  }
+
+  @Test
+  public void projectCreatedEvent() {
+    ProjectCreatedEvent event = new ProjectCreatedEvent();
+    event.projectName = PROJECT;
+    event.headName = REF;
+
+    assertThatJsonMap(event)
+        .isEqualTo(
+            ImmutableMap.builder()
+                .put("projectName", PROJECT)
+                .put("headName", REF)
+                .put("type", "project-created")
+                .put("eventCreatedOn", TS1)
+                .build());
+  }
+
+  private Supplier<AccountAttribute> newAccount(String name) {
+    AccountAttribute account = new AccountAttribute();
+    account.name = name;
+    account.email = name + "@somewhere.com";
+    account.username = name;
+    return Suppliers.ofInstance(account);
+  }
+
+  private Change newChange() {
+    return new Change(
+        new Change.Key(CHANGE_ID),
+        new Change.Id(CHANGE_NUM),
+        new Account.Id(9999),
+        new Branch.NameKey(new Project.NameKey(PROJECT), BRANCH),
+        TimeUtil.nowTs());
+  }
+
+  private <T> Supplier<T> createSupplier(T value) {
+    return Suppliers.memoize(() -> value);
+  }
+
+  private Supplier<ChangeAttribute> asChangeAttribute(Change change) {
+    ChangeAttribute a = new ChangeAttribute();
+    a.project = change.getProject().get();
+    a.branch = change.getDest().getShortName();
+    a.topic = change.getTopic();
+    a.id = change.getKey().get();
+    a.number = change.getId().get();
+    a.subject = change.getSubject();
+    a.commitMessage = COMMIT_MESSAGE;
+    a.url = URL;
+    a.status = change.getStatus();
+    a.createdOn = change.getCreatedOn().getTime() / 1000L;
+    a.wip = change.isWorkInProgress() ? true : null;
+    a.isPrivate = change.isPrivate() ? true : null;
+    return Suppliers.ofInstance(a);
+  }
+
+  private MapSubject assertThatJsonMap(Object src) {
+    // Parse JSON into a raw Java map:
+    //  * Doesn't depend on field iteration order.
+    //  * Avoids excessively long string literals in asserts.
+    Map<Object, Object> map =
+        gson.fromJson(gson.toJson(src), new TypeToken<Map<Object, Object>>() {}.getType());
+    return assertThat(map);
+  }
+
+  private static ImmutableMap<Object, Object> map(Object k, Object v) {
+    return ImmutableMap.of(k, v);
+  }
+
+  private static ImmutableList<Object> list(Object... es) {
+    return ImmutableList.copyOf(es);
+  }
+}