/** * Copyright 2013-2014 David Rusek <dave dot rusek at gmail dot com> * * 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.robotninjas.barge.jaxrs; import com.google.common.base.Throwables; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.ClassRule; import org.junit.Test; import org.robotninjas.barge.ClusterConfig; import org.robotninjas.barge.NotLeaderException; import org.robotninjas.barge.api.AppendEntriesResponse; import org.robotninjas.barge.api.RequestVoteResponse; import org.robotninjas.barge.state.Raft; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; import java.net.URISyntaxException; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** */ public class BargeResourceTest extends JerseyTest { private static final ClusterConfig CLUSTER_CONFIG = HttpClusterConfig.from(new HttpReplica(uri("http://localhost:123")), new HttpReplica(uri("http://localhost:234"))); @ClassRule public static MuteJUL muteJUL = new MuteJUL(); private Raft raftService; @Test public void onPOSTRequestVoteReturn200WithResponseGivenServiceReturnsResponse() throws Exception { when(raftService.requestVote(Model.vote)).thenReturn(Model.voteResponse); RequestVoteResponse actual = client().target("/vote") .request() .post(Entity.entity(Model.vote, MediaType.APPLICATION_JSON_TYPE)) .readEntity(RequestVoteResponse.class); assertThat(actual).isEqualTo(Model.voteResponse); } @Test public void onPOSTAppendEntriesReturn200WithResponseGivenServiceReturnsResponse() throws Exception { when(raftService.appendEntries(Model.entries)).thenReturn(Model.entriesResponse); AppendEntriesResponse actual = client().target("/entries") .request() .post(Entity.entity(Model.entries, MediaType.APPLICATION_JSON_TYPE)) .readEntity(AppendEntriesResponse.class); assertThat(actual).isEqualTo(Model.entriesResponse); } @Test public void onPOSTCommitReturn204GivenServiceReturnsResponse() throws Exception { when(raftService.commitOperation("foo".getBytes())).thenReturn(Futures.<Object>immediateFuture("42")); Response value = client().target("/commit") .request() .post(Entity.entity("foo".getBytes(), MediaType.APPLICATION_OCTET_STREAM)); assertThat(value.getStatus()).isEqualTo(204); } @Test public void onPOSTCommitReturn302WithLeaderURIGivenRaftThrowsNotLeaderException() throws Exception { URI leaderURI = new URI("http://localhost:1234"); when(raftService.commitOperation("foo".getBytes())).thenThrow(new NotLeaderException(new HttpReplica(leaderURI))); Response value = client().target("/commit") .request() .post(Entity.entity("foo".getBytes(), MediaType.APPLICATION_OCTET_STREAM)); assertThat(value.getStatus()).isEqualTo(302); assertThat(value.getLocation()).isEqualTo(leaderURI); } @Test public void onGETTypeReturnsTheCurrentStateOfRaftService() throws Exception { when(raftService.type()).thenReturn(Raft.StateType.LEADER); assertThat(client().target("/state").request().get(Raft.StateType.class)).isEqualTo(Raft.StateType.LEADER); } @Test public void onGETConfigReturnsCurrentClusterConfiguration() throws Exception { assertThat(client().target("/config").request().get(HttpClusterConfig.class)).isEqualTo(CLUSTER_CONFIG); } @Test public void onPOSTInitThenItSynchronouslyInitRaftService() throws Exception { ListenableFuture<Raft.StateType> future = Futures.immediateFuture(Raft.StateType.START); when(raftService.init()).thenReturn(future); assertThat(client().target("/init").request().post(Entity.json("")).readEntity(Raft.StateType.class)).isEqualTo( Raft.StateType.START); } @Override protected void configureClient(ClientConfig config) { super.configureClient(config.property(ClientProperties.FOLLOW_REDIRECTS, false).register(Jackson.customJacksonProvider())); } @Override protected Application configure() { raftService = mock(Raft.class); ResourceConfig resourceConfig = ResourceConfig.forApplication(new Application() { @Override public Set<Object> getSingletons() { return Sets.newHashSet((Object) new BargeResource(raftService, CLUSTER_CONFIG)); } }); resourceConfig.register(Jackson.customJacksonProvider()); return resourceConfig; } private static URI uri(String uri) { try { return new URI(uri); } catch (URISyntaxException e) { throw Throwables.propagate(e); } } }