AMQP-728: Make BrokerRunning More Configurable

JIRA: https://jira.spring.io/browse/AMQP-728
Resolves: #592

Add more options to configure the `BrokerRunning` JUnit `@Rule`.

Fix missing exclude from build.gradle for the junit subproject.

Polishing - PR Comments

More Polishing

**Cherry-pick to 1.7.x**
This commit is contained in:
Gary Russell
2017-03-16 13:47:00 -04:00
committed by Artem Bilan
parent 0f2c23d0bb
commit 38f38c4d35
6 changed files with 347 additions and 15 deletions

View File

@@ -244,7 +244,10 @@ project('spring-rabbit-junit') {
compile "org.springframework:spring-core:$springVersion"
compile "junit:junit:$junitVersion"
compile "com.rabbitmq:amqp-client:$rabbitmqVersion"
compile "com.rabbitmq:http-client:$rabbitmqHttpClientVersion"
compile ("com.rabbitmq:http-client:$rabbitmqHttpClientVersion") {
exclude group: 'org.springframework', module: 'spring-web'
}
compile "org.springframework:spring-web:$springVersion"
}

View File

@@ -79,17 +79,33 @@ import com.rabbitmq.http.client.Client;
*/
public final class BrokerRunning extends TestWatcher {
public static final String BROKER_ADMIN_URI = "RABBITMQ_TEST_ADMIN_URI";
public static final String BROKER_HOSTNAME = "RABBITMQ_TEST_HOSTNAME";
public static final String BROKER_PORT = "RABBITMQ_TEST_PORT";
public static final String BROKER_USER = "RABBITMQ_TEST_USER";
public static final String BROKER_PW = "RABBITMQ_TEST_PASSWORD";
public static final String BROKER_ADMIN_USER = "RABBITMQ_TEST_ADMIN_USER";
public static final String BROKER_ADMIN_PW = "RABBITMQ_TEST_ADMIN_PASSWORD";
public static final String BROKER_REQUIRED = "RABBITMQ_SERVER_REQUIRED";
private static final String DEFAULT_QUEUE_NAME = BrokerRunning.class.getName();
private static Log logger = LogFactory.getLog(BrokerRunning.class);
private static final Log logger = LogFactory.getLog(BrokerRunning.class);
// Static so that we only test once on failure: speeds up test suite
private static Map<Integer, Boolean> brokerOnline = new HashMap<Integer, Boolean>();
private static final Map<Integer, Boolean> brokerOnline = new HashMap<Integer, Boolean>();
// Static so that we only test once on failure
private static Map<Integer, Boolean> brokerOffline = new HashMap<Integer, Boolean>();
private static final Map<Integer, Boolean> brokerOffline = new HashMap<Integer, Boolean>();
private static final Map<String, String> environmentOverrides = new HashMap<>();
private final boolean assumeOnline;
@@ -99,14 +115,57 @@ public final class BrokerRunning extends TestWatcher {
private final String[] queues;
private final int defaultPort = BrokerTestUtils.getPort();
private final int defaultPort = fromEnvironment(BROKER_PORT, null) == null ? BrokerTestUtils.getPort()
: Integer.valueOf(fromEnvironment(BROKER_PORT, null));
private int port;
private String hostName = null;
private String hostName = fromEnvironment(BROKER_HOSTNAME, "localhost");
private String adminUri = fromEnvironment(BROKER_ADMIN_URI, null);
private ConnectionFactory connectionFactory;
private String user = fromEnvironment(BROKER_USER, "guest");
private String password = fromEnvironment(BROKER_PW, "guest");
private String adminUser = fromEnvironment(BROKER_ADMIN_USER, "guest");
private String adminPassword = fromEnvironment(BROKER_ADMIN_PW, "guest");
private String fromEnvironment(String key, String defaultValue) {
String environmentValue = environmentOverrides.get(key);
if (!StringUtils.hasText(environmentValue)) {
environmentValue = System.getenv(key);
}
if (StringUtils.hasText(environmentValue)) {
return environmentValue;
}
else {
return defaultValue;
}
}
/**
* Set environment variable overrides for host, port etc. Will override any real
* environment variables, if present.
* <p><b>The variables will only apply to rule instances that are created after this
* method is called.</b>
* The overrides will remain until
* @param environmentVariables the variables.
*/
public static void setEnvironmentVariableOverrides(Map<String, String> environmentVariables) {
environmentOverrides.putAll(environmentVariables);
}
/**
* Clear any environment variable overrides set in {@link #setEnvironmentVariableOverrides(Map)}.
*/
public static void clearEnvironmentVariableOverrides() {
environmentOverrides.clear();
}
/**
* Ensure the broker is running and has a empty queue(s) with the specified name(s) in the
* default exchange.
@@ -192,6 +251,105 @@ public final class BrokerRunning extends TestWatcher {
this.hostName = hostName;
}
/**
* Set the user for the amqp connection default "guest".
* @param user the user.
* @since 1.7.2
*/
public void setUser(String user) {
this.user = user;
}
/**
* Set the password for the amqp connection default "guest".
* @param password the password.
* @since 1.7.2
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Set the uri for the REST API.
* @param adminUri the uri.
* @since 1.7.2
*/
public void setAdminUri(String adminUri) {
this.adminUri = adminUri;
}
/**
* Set the user for the management REST API connection default "guest".
* @param user the user.
* @since 1.7.2
*/
public void setAdminUser(String user) {
this.adminUser = user;
}
/**
* Set the password for the management REST API connection default "guest".
* @param password the password.
* @since 1.7.2
*/
public void setAdminPassword(String password) {
this.adminPassword = password;
}
/**
* Return the port.
* @return the port.
* @since 1.7.2
*/
public int getPort() {
return this.port;
}
/**
* Return the port.
* @return the port.
* @since 1.7.2
*/
public String getHostName() {
return this.hostName;
}
/**
* Return the user.
* @return the user.
* @since 1.7.2
*/
public String getUser() {
return this.user;
}
/**
* Return the password.
* @return the password.
* @since 1.7.2
*/
public String getPassword() {
return this.password;
}
/**
* Return the admin user.
* @return the user.
* @since 1.7.2
*/
public String getAdminUser() {
return this.adminUser;
}
/**
* Return the admin password.
* @return the password.
* @since 1.7.2
*/
public String getAdminPassword() {
return this.adminPassword;
}
@Override
public Statement apply(Statement base, Description description) {
@@ -235,7 +393,7 @@ public final class BrokerRunning extends TestWatcher {
}
if (this.management) {
Client client = new Client("http://localhost:15672/api/", "guest", "guest");
Client client = new Client(getAdminUri(), this.adminUser, this.adminPassword);
if (!client.alivenessTest("/")) {
throw new RuntimeException("Aliveness test failed for localhost:15672 guest/quest; "
+ "management not available");
@@ -389,10 +547,29 @@ public final class BrokerRunning extends TestWatcher {
this.connectionFactory.setHost("localhost");
}
this.connectionFactory.setPort(this.port);
this.connectionFactory.setUsername(this.user);
this.connectionFactory.setPassword(this.password);
}
return this.connectionFactory;
}
/**
* Return the admin uri.
* @return the uri.
* @since 1.7.2
*/
public String getAdminUri() {
if (!StringUtils.hasText(this.adminUri)) {
if (!StringUtils.hasText(this.hostName)) {
this.adminUri = "http://localhost:15672/api/";
}
else {
this.adminUri = "http://" + this.hostName + ":15672/api/";
}
}
return this.adminUri;
}
private void closeResources(Connection connection, Channel channel) {
if (channel != null) {
try {

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2017 the original author or authors.
*
* 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 org.springframework.amqp.rabbit.junit;
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import com.rabbitmq.client.ConnectionFactory;
/**
* @author Gary Russell
* @since 1.7.2
*
*/
public class BrokerRunningTests {
@Test
public void testVars() {
BrokerRunning brokerRunning = BrokerRunning.isBrokerAndManagementRunning();
brokerRunning.setAdminPassword("foo");
brokerRunning.setAdminUser("bar");
brokerRunning.setHostName("baz");
brokerRunning.setPassword("qux");
brokerRunning.setPort(1234);
brokerRunning.setUser("fiz");
assertEquals("http://baz:15672/api/", brokerRunning.getAdminUri());
ConnectionFactory connectionFactory = brokerRunning.getConnectionFactory();
assertEquals("baz", connectionFactory.getHost());
assertEquals(1234, connectionFactory.getPort());
assertEquals("fiz", connectionFactory.getUsername());
assertEquals("qux", connectionFactory.getPassword());
}
@Test
public void testEnvironmentVars() {
Map<String, String> vars = new HashMap<>();
vars.put("RABBITMQ_TEST_ADMIN_PASSWORD", "FOO");
vars.put("RABBITMQ_TEST_ADMIN_URI", "http://foo/bar");
vars.put("RABBITMQ_TEST_ADMIN_USER", "BAR");
vars.put("RABBITMQ_TEST_HOSTNAME", "BAZ");
vars.put("RABBITMQ_TEST_PASSWORD", "QUX");
vars.put("RABBITMQ_TEST_PORT", "2345");
vars.put("RABBITMQ_TEST_USER", "FIZ");
BrokerRunning.setEnvironmentVariableOverrides(vars);
BrokerRunning brokerRunning = BrokerRunning.isBrokerAndManagementRunning();
assertEquals("http://foo/bar", brokerRunning.getAdminUri());
ConnectionFactory connectionFactory = brokerRunning.getConnectionFactory();
assertEquals("BAZ", connectionFactory.getHost());
assertEquals(2345, connectionFactory.getPort());
assertEquals("FIZ", connectionFactory.getUsername());
assertEquals("QUX", connectionFactory.getPassword());
DirectFieldAccessor dfa = new DirectFieldAccessor(brokerRunning);
assertEquals("BAR", dfa.getPropertyValue("adminUser"));
assertEquals("FOO", dfa.getPropertyValue("adminPassword"));
BrokerRunning.clearEnvironmentVariableOverrides();
}
}

View File

@@ -1165,7 +1165,10 @@ public class EnableRabbitIntegrationTests {
@Bean
public ConnectionFactory rabbitConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost("localhost");
connectionFactory.setHost(brokerRunning.getHostName());
connectionFactory.setPort(brokerRunning.getPort());
connectionFactory.setUsername(brokerRunning.getUser());
connectionFactory.setPassword(brokerRunning.getPassword());
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("rabbitClientThread-");
executor.afterPropertiesSet();
@@ -1386,7 +1389,10 @@ public class EnableRabbitIntegrationTests {
@Bean
public ConnectionFactory rabbitConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost("localhost");
connectionFactory.setHost(brokerRunning.getHostName());
connectionFactory.setPort(brokerRunning.getPort());
connectionFactory.setUsername(brokerRunning.getUser());
connectionFactory.setPassword(brokerRunning.getPassword());
return connectionFactory;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,6 @@ import org.apache.commons.logging.Log;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.amqp.utils.test.TestUtils;

View File

@@ -266,9 +266,11 @@ to suspend the test thread.
Spring AMQP _version 1.7_ provides an additional jar `spring-rabbit-junit`; this jar contains a couple of utility `@Rule` s for use when running JUnit tests.
`BrokerRunning` provides a mechanism to allow tests to succeed when a broker is not running on `localhost`.
===== BrokerRunning
It also has utility methods to delete queues and exchanges.
`BrokerRunning` provides a mechanism to allow tests to succeed when a broker is not running (on `localhost`, by default).
It also has utility methods to initialize/empty queues, and delete queues and exchanges.
Usage:
@@ -284,10 +286,74 @@ public static void tearDown() {
}
----
Of course, there are times when you want tests to fail if there is no broker, such as a nightly CI build.
There are several `isRunning...` static methods such as `isBrokerAndManagementRunning()` which verifies the broker has the management plugin enabled.
====== Configuring the Rule
There are times when you want tests to fail if there is no broker, such as a nightly CI build.
To disable the rule at runtime, set an environment variable `RABBITMQ_SERVER_REQUIRED` to `true`.
There are several `isRunning...` static methods such as `isBrokerAndManagementRunning()` which verifies the broker has the management plugin enabled.
You can override the broker properties, such as hostname in several ways:
- Setters
[source, java]
----
@ClassRule
public static BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues("foo", "bar");
static {
brokerRunning.setHostName("10.0.0.1")
}
@AfterClass
public static void tearDown() {
brokerRunning.removeTestQueues("some.other.queue.too") // removes foo, bar as well
}
----
- Environment Variables
The following environment variables are provided:
[source, java]
----
public static final String BROKER_ADMIN_URI = "RABBITMQ_TEST_ADMIN_URI";
public static final String BROKER_HOSTNAME = "RABBITMQ_TEST_HOSTNAME";
public static final String BROKER_PORT = "RABBITMQ_TEST_PORT";
public static final String BROKER_USER = "RABBITMQ_TEST_USER";
public static final String BROKER_PW = "RABBITMQ_TEST_PASSWORD";
public static final String BROKER_ADMIN_USER = "RABBITMQ_TEST_ADMIN_USER";
public static final String BROKER_ADMIN_PW = "RABBITMQ_TEST_ADMIN_PASSWORD";
----
These will override the default settings (`localhost:5672` for amqp and `http://localhost:15672/api/` for the management REST API).
Changing the host name affects both the amqp and management REST API connection (unless the admin uri is explicitly set).
`BrokerRunning` also provides a `static` method: `setEnvironmentVariableOverrides` where you can pass in a map containing these variables; they override system environment variables.
This might be useful if you wish to use different configuration for tests in multiple test suites.
IMPORTANT: The method must be called before invoking any of the `isRunning()` static methods that create the rule instance.
Variable values will be applied to all instances created after this.
Invoke `clearEnvironmentVariableOverrides()` to reset the rule to use defaults (including any actual environment variables).
In your test cases, you can use those properties when creating the connection factory:
[source, java]
----
@Bean
public ConnectionFactory rabbitConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(brokerRunning.getHostName());
connectionFactory.setPort(brokerRunning.getPort());
connectionFactory.setUsername(brokerRunning.getUser());
connectionFactory.setPassword(brokerRunning.getPassword());
return connectionFactory;
}
----
===== LongRunningIntegrationTest
`LongRunningIntegrationTest` is a rule that disables long running tests; you might want to use this on a developer system but ensure that the rule is disabled on, for example, nightly CI builds.