package com.mygeopay.stratumj; import com.mygeopay.stratumj.messages.CallMessage; import com.mygeopay.stratumj.messages.ResultMessage; import org.bitcoinj.utils.BriefLogFormatter; import com.google.common.base.Optional; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.json.JSONException; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.net.Socket; import java.util.Arrays; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nullable; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.doThrow; /** * @author John L. Jegutanis */ public class CommunicationsTest { private Socket socket; private StratumClient client; private ByteArrayOutputStream serverInput; private PipedOutputStream serverOutput; public CommunicationsTest() { // System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); BriefLogFormatter.init(); } @Before public void setup() throws IOException { socket = mock(Socket.class); serverInput = new ByteArrayOutputStream(); serverOutput = new PipedOutputStream(); PipedInputStream pipedServerOutput = new PipedInputStream(serverOutput); when(socket.getOutputStream()).thenReturn(serverInput); when(socket.getInputStream()).thenReturn(pipedServerOutput); when(socket.isConnected()).thenReturn(true); client = new StratumClient("not used", 1234) { @Override protected Socket createSocket() { return CommunicationsTest.this.socket; } }; } @Test public void testCallCommand() throws Exception { client.startAsync(); client.awaitRunning(5, TimeUnit.SECONDS); CallMessage call = new CallMessage("blockchain.address.get_history", Arrays.asList("mrx4EmF6zHXky3zDoeJ1K7KvYcuNn8Mmc4")); final ListenableFuture<ResultMessage> futureReply = client.call(call); // Check if server got the correct message Assert.assertEquals(call.toString(), new String(serverInput.toByteArray())); // Reply to the client String resultJson = "{\"id\": 0, \"result\": [{" + "\"tx_hash\": \"3aa2a5a9825ca767e092bcc19487aa13969eeb217fd0fba8492543bbb8c30954\", " + "\"height\": 260144}]}"; final ResultMessage serverResult = ResultMessage.fromJson(resultJson); serverResult.setId(call.getId()); serverOutput.write(serverResult.toString().getBytes()); ResultMessage result = futureReply.get(3, TimeUnit.SECONDS); Assert.assertEquals(serverResult.toString(), result.toString()); serverOutput.close(); } @Test public void testIoFail() throws TimeoutException, IOException, JSONException { final AtomicBoolean success = new AtomicBoolean(false); final Thread testThread = Thread.currentThread(); serverInput = mock(ByteArrayOutputStream.class); when(socket.getOutputStream()).thenReturn(serverInput); doThrow(IOException.class).when(serverInput).write(anyInt()); client.startAsync(); client.awaitRunning(5, TimeUnit.SECONDS); CallMessage call = CallMessage.fromJson("{}"); final ListenableFuture<ResultMessage> futureReply = client.call(call); Futures.addCallback(futureReply, new FutureCallback<ResultMessage>() { @Override public void onSuccess(@Nullable ResultMessage result) { Assert.fail(); } @Override public void onFailure(Throwable t) { Assert.assertTrue(t instanceof IOException); success.set(true); testThread.interrupt(); } }); try { Thread.sleep(5000); } catch (InterruptedException ignored) { } Assert.assertTrue(success.get()); } @Test public void testSubscribeCommand() throws IOException, TimeoutException, ExecutionException, InterruptedException, JSONException { client.startAsync(); client.awaitRunning(5, TimeUnit.SECONDS); final AtomicBoolean success = new AtomicBoolean(false); final Thread testThread = Thread.currentThread(); // Call message CallMessage call = new CallMessage("blockchain.address.subscribe", Arrays.asList("mrx4EmF6zHXky3zDoeJ1K7KvYcuNn8Mmc4")); String subscribeCallJson = "{\"params\": [\"mrx4EmF6zHXky3zDoeJ1K7KvYcuNn8Mmc4\"," + "\"29b083b2b25cfbc8cc4b46977b74512a8cc7bc152f4533c1b2e50f1489d6df67\"]," + "\"id\": null, \"method\": \"blockchain.address.subscribe\"}"; final CallMessage subscribeCall = CallMessage.fromJson(subscribeCallJson); // Subscribe ListenableFuture<ResultMessage> futureReply = client.subscribe(call, new StratumClient.SubscribeResult() { @Override public void handle(CallMessage message) { Assert.assertEquals(subscribeCall.toString(), message.toString()); success.set(true); testThread.interrupt(); } }); // Check if server got the correct message Assert.assertEquals(call.toString(), new String(serverInput.toByteArray())); // Result message String resultJson = "{\"id\": 0, \"result\":" + "\"e0dc94c40d4331306eb60ad55b537ee92446b8a8dc19f25dc3373d96c1904719\"}"; final ResultMessage serverResult = ResultMessage.fromJson(resultJson); serverResult.setId(call.getId()); // Reply to the client serverOutput.write(serverResult.toString().getBytes()); ResultMessage result = futureReply.get(3, TimeUnit.SECONDS); Assert.assertEquals(serverResult.toString(), result.toString()); // Send a message from server to the client serverOutput.write(subscribeCall.toString().getBytes()); try { Thread.sleep(5000); } catch (InterruptedException ignored) { } serverOutput.close(); Assert.assertTrue(success.get()); } }