blob: 344646fd4f479146795170a498f861e1941fcb03 [file] [log] [blame]
// Copyright (C) 2021 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.googlesource.gerrit.plugins.eventseiffel.mapping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestPlugin;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.GitPerson;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.events.RevisionCreatedListener;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.eventseiffel.EiffelEventsTest;
import com.googlesource.gerrit.plugins.eventseiffel.config.EventMappingConfig;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelArtifactCreatedEventInfo;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelChangeInfo;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelCompositionDefinedEventInfo;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelEventType;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelGitIdentifierInfo;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelLinkInfo;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelLinkType;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelMetaInfo;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelPersonInfo;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelSourceChangeCreatedEventInfo;
import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelSourceChangeSubmittedEventInfo;
import com.googlesource.gerrit.plugins.eventseiffel.parsing.PatchsetCreationData;
import java.net.URLEncoder;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
@TestPlugin(
name = "events-eiffel",
sysModule = "com.googlesource.gerrit.plugins.eventseiffel.mapping.EventMappingIT$TestModule")
public class EventMappingIT extends EiffelEventsTest {
private static final String COMMIT_SHA1 = "58117b5a121eb516c2cdca463c51250ad3de9cd3";
private static final String REPO_NAME = "repo/name";
private static final String BRANCH = "main";
private static final int CHANGE_NUMBER = 1234;
private static final String AUTHOR_EMAIL = "jane.doe@company.com";
private static final String AUTHOR_NAME = "Jane Doe";
private static final Long EPOCH_MILLIS = 978307261000l;
private static final List<UUID> PARENT_UUIDS = Lists.newArrayList(UUID.randomUUID());
private static final UUID SCC_UUID = UUID.randomUUID();
private static final UUID CD_UUID = UUID.randomUUID();
private static final UUID SCS_UUID = UUID.randomUUID();
EiffelEventMapper mapper;
@Before
public void setUp() {
mapper = plugin.getSysInjector().getInstance(EiffelEventMapper.class);
}
@Test
public void sccFromPatchsetCreationData() throws Exception {
EiffelSourceChangeCreatedEventInfo event =
mapper.toScc(new PatchsetCreationData(new TestEvent()), PARENT_UUIDS);
assertPersonInfo(event.data.author, AUTHOR_NAME, AUTHOR_EMAIL, null);
assertGitIdentifier(event.data.gitIdentifier, REPO_NAME, BRANCH, COMMIT_SHA1);
assertChangeInfo(event.data.change, String.valueOf(CHANGE_NUMBER), REPO_NAME, COMMIT_SHA1);
assertSccMeta(event.meta, EPOCH_MILLIS);
assertSccLinks(event.links);
}
@Test
public void sccFromRevCommitWhereAuthorDoesNotHaveAccount() throws Exception {
RevCommit accountlessAuthor =
commitBuilder().author(new PersonIdent(AUTHOR_NAME, AUTHOR_EMAIL)).create();
EiffelSourceChangeCreatedEventInfo event =
mapper.toScc(accountlessAuthor, project.get(), "master", PARENT_UUIDS);
assertPersonInfo(event.data.author, AUTHOR_NAME, AUTHOR_EMAIL, null);
assertGitIdentifier(
event.data.gitIdentifier, project.get(), "master", accountlessAuthor.getName());
assertChangeInfo(event.data.change, null, project.get(), accountlessAuthor.getName());
assertSccMeta(event.meta, commitTimeInEpochMillis(accountlessAuthor));
assertSccLinks(event.links);
}
@Test
public void sccFromRevCommitWhereCommitterHasAccount() throws Exception {
RevCommit authorWithAccount =
commitBuilder().author(new PersonIdent(admin.fullName(), admin.email())).create();
EiffelSourceChangeCreatedEventInfo event =
mapper.toScc(authorWithAccount, project.get(), "master", PARENT_UUIDS);
assertPersonInfo(event.data.author, admin.fullName(), admin.email(), admin.username());
assertGitIdentifier(
event.data.gitIdentifier, project.get(), "master", authorWithAccount.getName());
assertChangeInfo(event.data.change, null, project.get(), authorWithAccount.getName());
assertSccMeta(event.meta, commitTimeInEpochMillis(authorWithAccount));
assertSccLinks(event.links);
}
@Test
public void sccFromChangeRevCommit() throws Exception {
PushOneCommit.Result res = createChange();
sccFromChangeRevCommit(res);
}
@Test
public void sccFromChangeRevCommitThatIsNotCurrent() throws Exception {
PushOneCommit.Result res = createChange();
amendChange(res.getChangeId());
sccFromChangeRevCommit(res);
}
private void sccFromChangeRevCommit(PushOneCommit.Result result) throws Exception {
EiffelSourceChangeCreatedEventInfo event =
mapper.toScc(result.getCommit(), project.get(), "master", PARENT_UUIDS);
assertPersonInfo(event.data.author, admin.fullName(), admin.email(), admin.username());
assertGitIdentifier(
event.data.gitIdentifier, project.get(), "master", result.getCommit().getName());
assertChangeInfo(
event.data.change,
String.valueOf(result.getPatchSetId().changeId().get()),
project.get(),
result.getCommit().getName());
assertSccMeta(
event.meta, Long.valueOf(result.getChange().currentPatchSet().createdOn().toEpochMilli()));
assertSccLinks(event.links);
}
@Test
public void scsFromRevCommitWithoutSubmitter() throws Exception {
/* The initial commit is created by the server that doesn't have an account */
RevCommit revCommit =
commitBuilder().committer(new PersonIdent(AUTHOR_NAME, AUTHOR_EMAIL)).create();
EiffelSourceChangeSubmittedEventInfo event =
mapper.toScs(revCommit, project.get(), "master", null, null, PARENT_UUIDS, SCC_UUID);
assertPersonInfo(event.data.submitter, AUTHOR_NAME, AUTHOR_EMAIL, null);
assertGitIdentifier(event.data.gitIdentifier, project.get(), "master", revCommit.getName());
assertScsMeta(event.meta, commitTimeInEpochMillis(revCommit));
assertScsLinks(event.links);
}
@Test
public void scsFromChangeRevCommitWithoutSubmitter() throws Exception {
/* The initial commit is created by the server that doesn't have an account */
PushOneCommit.Result res = createChange();
ChangeInfo change = approveAndMerge(res);
EiffelSourceChangeSubmittedEventInfo event =
mapper.toScs(res.getCommit(), project.get(), "master", null, null, PARENT_UUIDS, SCC_UUID);
assertPersonInfo(event.data.submitter, admin.fullName(), admin.email(), admin.username());
assertGitIdentifier(
event.data.gitIdentifier, project.get(), "master", res.getCommit().getName());
assertScsMeta(event.meta, Long.valueOf(change.submitted.getTime()));
}
@Test
public void scsFromRevCommitWithSubmitterAndSubmittedAt() throws Exception {
/* The initial commit is created by the server that doesn't have an account */
RevCommit revCommit =
commitBuilder().committer(new PersonIdent(AUTHOR_NAME, AUTHOR_EMAIL)).create();
AccountInfo submitter = gApi.accounts().id(user.username()).get();
Long submittedAt = Instant.now().toEpochMilli();
EiffelSourceChangeSubmittedEventInfo event =
mapper.toScs(
revCommit, project.get(), "master", submitter, submittedAt, PARENT_UUIDS, SCC_UUID);
assertPersonInfo(event.data.submitter, submitter.name, submitter.email, submitter.username);
assertGitIdentifier(event.data.gitIdentifier, project.get(), "master", revCommit.getName());
assertScsMeta(event.meta, submittedAt);
assertScsLinks(event.links);
}
@Test
public void scsFromRevCommitWithSubmitterButNotSubmittedAt() throws Exception {
/* The initial commit is created by the server that doesn't have an account */
RevCommit revCommit =
commitBuilder().committer(new PersonIdent(AUTHOR_NAME, AUTHOR_EMAIL)).create();
AccountInfo submitter = gApi.accounts().id(user.username()).get();
EiffelSourceChangeSubmittedEventInfo event =
mapper.toScs(revCommit, project.get(), "master", submitter, null, PARENT_UUIDS, SCC_UUID);
assertPersonInfo(event.data.submitter, submitter.name, submitter.email, submitter.username);
assertGitIdentifier(event.data.gitIdentifier, project.get(), "master", revCommit.getName());
assertScsMeta(event.meta, commitTimeInEpochMillis(revCommit));
assertScsLinks(event.links);
}
@Test
public void artcTest() throws Exception {
EiffelArtifactCreatedEventInfo event =
mapper.toArtc(REPO_NAME, TAG_NAME, EPOCH_MILLIS, CD_UUID);
assertEquals(tagPURL(REPO_NAME, TAG_NAME), event.data.identity);
assertArtcMeta(event.meta, EPOCH_MILLIS);
assertArtcLink(event.links);
}
@Test
public void cdTest() throws Exception {
EiffelCompositionDefinedEventInfo event =
mapper.toCd(REPO_NAME, TAG_NAME, EPOCH_MILLIS, SCS_UUID);
assertEquals(tagCompositionName(REPO_NAME), event.data.name);
assertEquals(TAG_NAME, event.data.version);
assertCdMeta(event.meta, EPOCH_MILLIS);
assertCdLink(event.links);
}
private void assertSccMeta(EiffelMetaInfo meta, Long time) {
assertMeta(meta, EiffelEventType.SCC, EiffelSourceChangeCreatedEventInfo.EVENT_VERSION, time);
}
private void assertScsMeta(EiffelMetaInfo meta, Long time) {
assertMeta(meta, EiffelEventType.SCS, EiffelSourceChangeSubmittedEventInfo.EVENT_VERSION, time);
}
private void assertArtcMeta(EiffelMetaInfo meta, Long time) {
assertMeta(
meta, EiffelEventType.ARTC, EiffelSourceChangeSubmittedEventInfo.EVENT_VERSION, time);
}
private void assertCdMeta(EiffelMetaInfo meta, Long time) {
assertMeta(meta, EiffelEventType.CD, EiffelSourceChangeSubmittedEventInfo.EVENT_VERSION, time);
}
private void assertMeta(EiffelMetaInfo meta, EiffelEventType type, String version, Long time) {
assertEquals(type, meta.type);
assertEquals(version, meta.version);
assertEquals(time, meta.time);
assertNotNull(meta.id);
assertEquals("Gerrit", meta.source.domainId);
assertEquals(HOST_NAME, meta.source.host);
assertEquals(PLUGIN_NAME, meta.source.name);
assertEquals(
String.format("pkg:maven/com.googlesource.gerrit.plugins/%s@%s", PLUGIN_NAME, VERSION),
meta.source.serializer);
}
private void assertChangeInfo(
EiffelChangeInfo change, String changeNumber, String repoName, String sha1) {
assertEquals(changeNumber, change.id);
assertEquals("Gerrit", change.tracker);
assertEquals(
String.format(
"%s/a/projects/%s/commits/%s",
WEB_URL, URLEncoder.encode(repoName, Charsets.UTF_8), sha1),
change.details);
}
private void assertGitIdentifier(
EiffelGitIdentifierInfo git, String repoName, String branch, String sha1) {
assertEquals(repoName, git.repoName);
assertEquals(branch, git.branch);
assertEquals(sha1, git.commitId);
assertEquals(String.format("ssh://%s:%d/%s", HOST_NAME, SSH_PORT, repoName), git.repoUri);
}
private void assertPersonInfo(
EiffelPersonInfo person, String name, String email, String username) {
assertEquals(name, person.name);
assertEquals(email, person.email);
assertEquals(username, person.id);
}
private ChangeInfo approveAndMerge(PushOneCommit.Result res) throws RestApiException {
RevisionApi revision = gApi.changes().id(res.getChangeId()).current();
revision.review(ReviewInput.approve());
revision.submit();
return gApi.changes().id(res.getChangeId()).get();
}
private void assertSccLinks(EiffelLinkInfo... links) throws Exception {
assertSccLinks(PARENT_UUIDS, links);
}
private void assertScsLinks(EiffelLinkInfo... links) throws Exception {
assertScsLinks(PARENT_UUIDS, SCC_UUID, links);
}
private void assertArtcLink(EiffelLinkInfo... links) throws Exception {
assertEquals(1, links.length);
assertEquals(EiffelLinkType.COMPOSITION, links[0].type);
assertEquals(CD_UUID, links[0].target);
}
private void assertCdLink(EiffelLinkInfo... links) throws Exception {
assertEquals(1, links.length);
assertEquals(EiffelLinkType.ELEMENT, links[0].type);
assertEquals(SCS_UUID, links[0].target);
}
static class TestEvent implements RevisionCreatedListener.Event {
@Override
public RevisionInfo getRevision() {
CommitInfo cInfo = new CommitInfo();
cInfo.commit = COMMIT_SHA1;
GitPerson author = new GitPerson();
author.email = AUTHOR_EMAIL;
author.name = AUTHOR_NAME;
cInfo.author = author;
cInfo.parents = List.of();
RevisionInfo rInfo = new RevisionInfo();
rInfo.commit = cInfo;
return rInfo;
}
@Override
public ChangeInfo getChange() {
ChangeInfo cInfo = new ChangeInfo();
cInfo.project = REPO_NAME;
cInfo.branch = BRANCH;
cInfo._number = CHANGE_NUMBER;
return cInfo;
}
@Override
public AccountInfo getWho() {
throw new NotImplementedException();
}
@Override
public Instant getWhen() {
return Instant.ofEpochMilli(EPOCH_MILLIS);
}
@Override
public NotifyHandling getNotify() {
throw new NotImplementedException();
}
}
public static class TestModule extends AbstractModule {
@Override
protected void configure() {
bind(EventMappingConfig.class).toProvider(EventMappingConfig.Provider.class);
bind(EiffelEventFactory.class)
.toProvider(EiffelEventsTest.TestEventFactoryProvider.class)
.in(Singleton.class);
bind(EiffelEventMapper.class);
}
}
}