Use a fake EmailSender in tests
Instead of disabling email entirely, allow sent emails to be buffered into a fake sender that can be inspected later. To facilitate making assertions over emails, make EmailHeader implementations more immutable, and implement equals/hashCode/toString. Change-Id: I483ea9f9216de1c5eb3635fa56f7ad9318c2bebf
This commit is contained in:
parent
87295b6fbb
commit
0d398d79c9
@ -28,6 +28,7 @@ import com.google.gerrit.server.git.SubmoduleOp;
|
||||
import com.google.gerrit.server.index.ChangeSchemas;
|
||||
import com.google.gerrit.server.ssh.NoSshModule;
|
||||
import com.google.gerrit.server.util.SocketUtil;
|
||||
import com.google.gerrit.testutil.FakeEmailSender;
|
||||
import com.google.gerrit.testutil.TempFileUtil;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
@ -115,6 +116,7 @@ public class GerritServer {
|
||||
}
|
||||
}
|
||||
});
|
||||
daemon.setEmailModuleForTesting(new FakeEmailSender.Module());
|
||||
|
||||
final File site;
|
||||
ExecutorService daemonService = null;
|
||||
|
@ -152,6 +152,7 @@ public class Daemon extends SiteProgram {
|
||||
private Path runFile;
|
||||
private boolean test;
|
||||
private AbstractModule luceneModule;
|
||||
private Module emailModule;
|
||||
|
||||
private Runnable serverStarted;
|
||||
|
||||
@ -261,6 +262,11 @@ public class Daemon extends SiteProgram {
|
||||
headless = true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setEmailModuleForTesting(Module module) {
|
||||
emailModule = module;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setLuceneModule(LuceneIndexModule m) {
|
||||
luceneModule = m;
|
||||
@ -322,7 +328,11 @@ public class Daemon extends SiteProgram {
|
||||
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
|
||||
modules.add(new InternalAccountDirectory.Module());
|
||||
modules.add(new DefaultCacheFactory.Module());
|
||||
modules.add(new SmtpEmailSender.Module());
|
||||
if (emailModule != null) {
|
||||
modules.add(emailModule);
|
||||
} else {
|
||||
modules.add(new SmtpEmailSender.Module());
|
||||
}
|
||||
modules.add(new SignedTokenEmailTokenVerifier.Module());
|
||||
modules.add(new PluginRestApiModule());
|
||||
modules.add(new RestCacheAdminModule());
|
||||
|
@ -101,10 +101,12 @@ java_library(
|
||||
'//lib:gwtorm',
|
||||
'//lib:h2',
|
||||
'//lib:junit',
|
||||
'//lib/auto:auto-value',
|
||||
'//lib/guice:guice',
|
||||
'//lib/guice:guice-servlet',
|
||||
'//lib/jgit:jgit',
|
||||
'//lib/jgit:junit',
|
||||
'//lib/log:api',
|
||||
'//lib/log:impl_log4j',
|
||||
'//lib/log:log4j',
|
||||
],
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
package com.google.gerrit.server.mail;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
@ -23,6 +25,7 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class EmailHeader {
|
||||
public abstract boolean isEmpty();
|
||||
@ -30,7 +33,7 @@ public abstract class EmailHeader {
|
||||
public abstract void write(Writer w) throws IOException;
|
||||
|
||||
public static class String extends EmailHeader {
|
||||
private java.lang.String value;
|
||||
private final java.lang.String value;
|
||||
|
||||
public String(java.lang.String v) {
|
||||
value = v;
|
||||
@ -53,6 +56,22 @@ public abstract class EmailHeader {
|
||||
w.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof String)
|
||||
&& Objects.equals(value, ((String) o).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.lang.String toString() {
|
||||
return MoreObjects.toStringHelper(this).addValue(value).toString();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean needsQuotedPrintable(java.lang.String value) {
|
||||
@ -113,7 +132,7 @@ public abstract class EmailHeader {
|
||||
}
|
||||
|
||||
public static class Date extends EmailHeader {
|
||||
private java.util.Date value;
|
||||
private final java.util.Date value;
|
||||
|
||||
public Date(java.util.Date v) {
|
||||
value = v;
|
||||
@ -135,6 +154,22 @@ public abstract class EmailHeader {
|
||||
fmt = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
|
||||
w.write(fmt.format(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof Date)
|
||||
&& Objects.equals(value, ((Date) o).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.lang.String toString() {
|
||||
return MoreObjects.toStringHelper(this).addValue(value).toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AddressList extends EmailHeader {
|
||||
@ -191,5 +226,21 @@ public abstract class EmailHeader {
|
||||
needComma = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof AddressList)
|
||||
&& Objects.equals(list, ((AddressList) o).list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.lang.String toString() {
|
||||
return MoreObjects.toStringHelper(this).addValue(list).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,119 @@
|
||||
// Copyright (C) 2015 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.testutil;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gerrit.common.errors.EmailException;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.mail.Address;
|
||||
import com.google.gerrit.server.mail.EmailHeader;
|
||||
import com.google.gerrit.server.mail.EmailSender;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Email sender implementation that records messages in memory.
|
||||
* <p>
|
||||
* This class is mostly threadsafe. The only exception is that not all {@link
|
||||
* EmailHeader} subclasses are immutable. In particular, if a caller holds a
|
||||
* reference to an {@code AddressList} and mutates it after sending, the message
|
||||
* returned by {@link #getMessages()} may or may not reflect mutations.
|
||||
*/
|
||||
@Singleton
|
||||
public class FakeEmailSender implements EmailSender {
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(FakeEmailSender.class);
|
||||
|
||||
public static class Module extends AbstractModule {
|
||||
@Override
|
||||
public void configure() {
|
||||
bind(EmailSender.class).to(FakeEmailSender.class);
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class Message {
|
||||
private static Message create(Address from, Collection<Address> rcpt,
|
||||
Map<String, EmailHeader> headers, String body) {
|
||||
return new AutoValue_FakeEmailSender_Message(from,
|
||||
ImmutableList.copyOf(rcpt), ImmutableMap.copyOf(headers), body);
|
||||
}
|
||||
|
||||
public abstract Address from();
|
||||
public abstract ImmutableList<Address> rcpt();
|
||||
public abstract ImmutableMap<String, EmailHeader> headers();
|
||||
public abstract String body();
|
||||
}
|
||||
|
||||
private final WorkQueue workQueue;
|
||||
private final List<Message> messages;
|
||||
|
||||
@Inject
|
||||
FakeEmailSender(WorkQueue workQueue) {
|
||||
this.workQueue = workQueue;
|
||||
messages = Collections.synchronizedList(new ArrayList<Message>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEmail(String address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Address from, Collection<Address> rcpt,
|
||||
Map<String, EmailHeader> headers, String body) throws EmailException {
|
||||
messages.add(Message.create(from, rcpt, headers, body));
|
||||
}
|
||||
|
||||
public ImmutableList<Message> getMessages() {
|
||||
waitForEmails();
|
||||
synchronized (messages) {
|
||||
return ImmutableList.copyOf(messages);
|
||||
}
|
||||
}
|
||||
|
||||
private void waitForEmails() {
|
||||
// TODO(dborowitz): This is brittle; consider forcing emails to use
|
||||
// a single thread in tests (tricky because most callers just use the
|
||||
// default executor).
|
||||
for (WorkQueue.Task<?> task : workQueue.getTasks()) {
|
||||
if (task.toString().contains("send-email")) {
|
||||
try {
|
||||
task.get();
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
log.warn("error finishing email task", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -46,7 +46,6 @@ import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.index.ChangeSchemas;
|
||||
import com.google.gerrit.server.index.IndexModule.IndexType;
|
||||
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
|
||||
import com.google.gerrit.server.mail.SmtpEmailSender;
|
||||
import com.google.gerrit.server.patch.DiffExecutor;
|
||||
import com.google.gerrit.server.schema.DataSourceType;
|
||||
import com.google.gerrit.server.schema.SchemaCreator;
|
||||
@ -88,7 +87,6 @@ public class InMemoryModule extends FactoryModule {
|
||||
cfg.setString("gerrit", null, "allProjects", "Test-Projects");
|
||||
cfg.setString("user", null, "name", "Gerrit Code Review");
|
||||
cfg.setString("user", null, "email", "gerrit@localhost");
|
||||
cfg.setBoolean("sendemail", null, "enable", false);
|
||||
cfg.setString("cache", null, "directory", null);
|
||||
cfg.setString("index", null, "type", "lucene");
|
||||
cfg.setBoolean("index", "lucene", "testInmemory", true);
|
||||
@ -179,7 +177,7 @@ public class InMemoryModule extends FactoryModule {
|
||||
}
|
||||
});
|
||||
install(new DefaultCacheFactory.Module());
|
||||
install(new SmtpEmailSender.Module());
|
||||
install(new FakeEmailSender.Module());
|
||||
install(new SignedTokenEmailTokenVerifier.Module());
|
||||
|
||||
IndexType indexType = null;
|
||||
|
Loading…
Reference in New Issue
Block a user