package org.jooby.consul;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.name.Names;
import com.orbitz.consul.AgentClient;
import com.orbitz.consul.Consul;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValueFactory;
import javaslang.control.Try.CheckedRunnable;
import org.jooby.Env;
import org.jooby.Route;
import org.jooby.Router;
import org.jooby.test.MockUnit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@RunWith(PowerMockRunner.class)
@PrepareForTest({Consul.class, Consul.Builder.class})
public class ConsulbyTest {
@Test
public void defaults() throws Exception {
Config config = resolvedConfig();
mockUnit()
.expect(envServiceKeyIsRequested())
.expect(consulIsBuiltAndBound("default", config))
.expect(serviceIsRegistered("default", config))
.expect(healthCheckRouteIsBound("default", config))
.run(createAndConfigureModule(config));
}
@Test
public void defaultsWithExtraBuilder() throws Exception {
Config config = resolvedConfig();
mockUnit()
.expect(envServiceKeyIsRequested())
.expect(consulIsBuiltAndBound("default", config))
.expect(serviceIsRegistered("default", config))
.expect(healthCheckRouteIsBound("default", config))
.run(createWithExtraBuilderAndConfigureModule(config));
}
@Test
public void defaultsWithoutCheck() throws Exception {
Config config = resolvedConfig()
.withoutPath("consul.default.register.check");
mockUnit()
.expect(envServiceKeyIsRequested())
.expect(consulIsBuiltAndBound("default", config))
.expect(serviceIsRegistered("default", config))
.run(createAndConfigureModule(config));
}
@Test
public void defaultsWithoutRegister() throws Exception {
Config config = resolvedConfig()
.withoutPath("consul.default.register");
mockUnit()
.expect(envServiceKeyIsRequested())
.expect(consulIsBuiltAndBound("default", config))
.run(createAndConfigureModule(config));
}
@Test
public void custom() throws Exception {
Config config = resolvedConfig()
.withValue("consul.custom.register.name", ConfigValueFactory.fromAnyRef("custom"));
mockUnit()
.expect(envServiceKeyIsRequested())
.expect(consulIsBuiltAndBound("custom", config))
.expect(serviceIsRegistered("custom", config))
.expect(healthCheckRouteIsBound("custom", config))
.run(createAndConfigureModule("custom", config));
}
@Test
public void defaultConfig() throws Exception {
assertEquals(consulConfig(), new Consulby().config());
}
private MockUnit mockUnit() {
return new MockUnit(Env.class, Binder.class, Consul.class);
}
private MockUnit.Block envServiceKeyIsRequested() {
return unit -> {
Env env = unit.get(Env.class);
expect(env.serviceKey()).andReturn(new Env.ServiceKey());
};
}
private MockUnit.Block consulIsBuiltAndBound(String name, Config config) {
return unit -> {
Config consulConfig = consulConfigWithFallback(name, config);
Consul consul = unit.get(Consul.class);
Consul.Builder consulBuilder = unit.mock(Consul.Builder.class);
expect(consulBuilder.withUrl(consulConfig.getString("url"))).andReturn(consulBuilder);
expect(consulBuilder.build()).andReturn(consul);
unit.mockStatic(Consul.class);
expect(Consul.builder()).andReturn(consulBuilder);
Env env = unit.get(Env.class);
expect(env.onStop(isA(CheckedRunnable.class))).andReturn(env);
//noinspection unchecked
AnnotatedBindingBuilder<Consul> consulABB = unit.mock(AnnotatedBindingBuilder.class);
consulABB.toInstance(consul);
consulABB.toInstance(consul);
Binder binder = unit.get(Binder.class);
expect(binder.bind(Key.get(Consul.class))).andReturn(consulABB);
expect(binder.bind(Key.get(Consul.class, Names.named(name)))).andReturn(consulABB);
};
}
@SuppressWarnings("unused")
private MockUnit.Block serviceIsRegistered(String name, Config config) {
return unit -> {
AgentClient agentClient = unit.mock(AgentClient.class);
Consul consul = unit.get(Consul.class);
expect(consul.agentClient()).andReturn(agentClient);
Env env = unit.get(Env.class);
expect(env.onStarted(isA(CheckedRunnable.class))).andReturn(env);
expect(env.onStop(isA(CheckedRunnable.class))).andReturn(env);
};
}
private MockUnit.Block healthCheckRouteIsBound(String name, Config config) {
return unit -> {
Config consulConfig = consulConfigWithFallback(name, config);
Router router = unit.mock(Router.class);
Env env = unit.get(Env.class);
expect(env.router()).andReturn(router);
String path = consulConfig.getString("register.check.path");
Route.Definition definition = unit.mock(Route.Definition.class);
expect(router.get(eq(path), isA(Route.ZeroArgHandler.class))).andReturn(definition);
};
}
private MockUnit.Block createAndConfigureModule(Config config) {
return unit -> new Consulby().configure(unit.get(Env.class), config, unit.get(Binder.class));
}
private MockUnit.Block createWithExtraBuilderAndConfigureModule(Config config) {
return unit -> {
AtomicBoolean consulBuilderConsumerCalled = new AtomicBoolean(false);
AtomicBoolean registrationBuilderConsumerCalled = new AtomicBoolean(false);
new Consulby()
.withConsulBuilder(builder -> consulBuilderConsumerCalled.set(true))
.withRegistrationBuilder(builder -> registrationBuilderConsumerCalled.set(true))
.configure(unit.get(Env.class), config, unit.get(Binder.class));
assertTrue("Consul Builder Consumer should be called", consulBuilderConsumerCalled.get());
assertTrue("Registration Builder Consumer should be called", registrationBuilderConsumerCalled.get());
};
}
@SuppressWarnings("SameParameterValue")
private MockUnit.Block createAndConfigureModule(String name, Config config) {
return unit -> new Consulby(name).configure(unit.get(Env.class), config, unit.get(Binder.class));
}
private Config resolvedConfig() {
return consulConfig()
.withValue("application.name", ConfigValueFactory.fromAnyRef("testapp"))
.withValue("application.host", ConfigValueFactory.fromAnyRef("localhost"))
.withValue("application.port", ConfigValueFactory.fromAnyRef("8080"))
.withValue("application.version", ConfigValueFactory.fromAnyRef("1.0.0-SNAPSHOT"))
.resolve();
}
private Config consulConfigWithFallback(String name, Config config) {
Config consulConfig = config.getConfig("consul.default");
if (!name.equals("default") && config.hasPath("consul." + name)) {
consulConfig = config.getConfig("consul." + name).withFallback(consulConfig);
}
return consulConfig;
}
private Config consulConfig() {
return ConfigFactory.parseResources(getClass(), "consul.conf");
}
}