package com.box.sdk; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import static com.github.tomakehurst.wiremock.client.WireMock.*; import com.github.tomakehurst.wiremock.http.Request; import com.github.tomakehurst.wiremock.http.RequestListener; import com.github.tomakehurst.wiremock.http.Response; import com.github.tomakehurst.wiremock.junit.WireMockRule; public class EventStreamTest { @Rule public final WireMockRule wireMockRule = new WireMockRule(8080); @Test @Category(IntegrationTest.class) public void receiveEventsForFolderCreateAndFolderDelete() throws InterruptedException { final LinkedBlockingQueue<BoxEvent> observedEvents = new LinkedBlockingQueue<BoxEvent>(); BoxAPIConnection api = new BoxAPIConnection(TestConfig.getAccessToken()); EventStream stream = new EventStream(api); stream.addListener(new EventListener() { @Override public void onEvent(BoxEvent event) { observedEvents.add(event); } @Override public void onNextPosition(long position) { return; } @Override public boolean onException(Throwable e) { return true; } }); stream.start(); BoxFolder rootFolder = BoxFolder.getRootFolder(api); BoxFolder childFolder = rootFolder.createFolder("[receiveEventsForFolderCreateAndFolderDelete] Child Folder") .getResource(); String expectedID = childFolder.getID(); childFolder.delete(false); boolean createdEventFound = false; boolean deletedEventFound = false; int timeouts = 0; while (timeouts < 3 && (!createdEventFound || !deletedEventFound)) { BoxEvent event = observedEvents.poll(1, TimeUnit.MINUTES); if (null == event) { timeouts++; System.out.println("Time outs: " + timeouts); continue; } BoxResource.Info sourceInfo = event.getSourceInfo(); // Some events may not have sourceInfo if (sourceInfo == null) { continue; } BoxResource source = sourceInfo.getResource(); if (source instanceof BoxFolder) { BoxFolder sourceFolder = (BoxFolder) source; if (sourceFolder.getID().equals(expectedID)) { if (event.getType() == BoxEvent.Type.ITEM_CREATE) { BoxFolder folder = (BoxFolder) event.getSourceInfo().getResource(); final String eventFolderID = folder.getID(); final String childFolderID = childFolder.getID(); assertThat(eventFolderID, is(equalTo(childFolderID))); createdEventFound = true; } if (event.getType() == BoxEvent.Type.ITEM_TRASH) { BoxFolder folder = (BoxFolder) event.getSourceInfo().getResource(); assertThat(folder.getID(), is(equalTo(childFolder.getID()))); deletedEventFound = true; } } } } assertThat(createdEventFound, is(true)); assertThat(deletedEventFound, is(true)); stream.stop(); } @Test @Category(UnitTest.class) public void canStopStreamWhileWaitingForAPIResponse() throws InterruptedException { final long streamPosition = 0; final String realtimeServerURL = "/realtimeServer?channel=0"; stubFor(options(urlEqualTo("/events")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody("{ \"entries\": [ { \"url\": \"http://localhost:8080" + realtimeServerURL + "\", " + "\"max_retries\": \"3\", \"retry_timeout\": 60000 } ] }"))); stubFor(get(urlMatching("/events\\?.*stream_position=now.*")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody("{ \"next_stream_position\": " + streamPosition + " }"))); BoxAPIConnection api = new BoxAPIConnection(""); api.setBaseURL("http://localhost:8080/"); final EventStream stream = new EventStream(api); final Object requestLock = new Object(); this.wireMockRule.addMockServiceRequestListener(new RequestListener() { @Override public void requestReceived(Request request, Response response) { String streamPositionURL = realtimeServerURL + "&stream_position=" + streamPosition; boolean requestUrlMatch = request.getUrl().contains(streamPositionURL); if (requestUrlMatch) { stream.stop(); synchronized (requestLock) { requestLock.notify(); } } } }); stream.start(); synchronized (requestLock) { requestLock.wait(); } assertThat(stream.isStarted(), is(false)); } @Test @Category(UnitTest.class) public void duplicateEventsAreNotReported() throws InterruptedException { final String realtimeServerURL = "/realtimeServer?channel=0"; stubFor(options(urlEqualTo("/events")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody("{ \"entries\": [ { \"url\": \"http://localhost:8080" + realtimeServerURL + "\", " + "\"max_retries\": \"3\", \"retry_timeout\": 60000 } ] }"))); stubFor(get(urlMatching("/events\\?.*stream_position=now.*")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody("{ \"next_stream_position\": 0 }"))); stubFor(get(urlMatching("/realtimeServer.*")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody("{ \"message\": \"new_change\" }"))); stubFor(get(urlMatching("/events\\?.*stream_position=0")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody("{ \"next_stream_position\": 1, \"entries\": [ { \"type\": \"event\", " + "\"event_id\": \"1\" } ] }"))); stubFor(get(urlMatching("/events\\?.*stream_position=1")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody("{ \"next_stream_position\": -1, \"entries\": [ { \"type\": \"event\", " + "\"event_id\": \"1\" } ] }"))); BoxAPIConnection api = new BoxAPIConnection(""); api.setBaseURL("http://localhost:8080/"); final EventStream stream = new EventStream(api); final EventListener eventListener = mock(EventListener.class); stream.addListener(eventListener); final Object requestLock = new Object(); this.wireMockRule.addMockServiceRequestListener(new RequestListener() { @Override public void requestReceived(Request request, Response response) { boolean requestUrlMatch = request.getUrl().contains("-1"); if (requestUrlMatch) { synchronized (requestLock) { requestLock.notify(); } } } }); stream.start(); synchronized (requestLock) { requestLock.wait(); } verify(eventListener).onEvent(any(BoxEvent.class)); } }