diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/annotations/TikTokEventObserver.java b/API/src/main/java/io/github/jwdeveloper/tiktok/annotations/TikTokEventObserver.java index 7daee1b..08f5cb8 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/annotations/TikTokEventObserver.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/annotations/TikTokEventObserver.java @@ -26,17 +26,16 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) -public @interface TikTokEventObserver { - +public @interface TikTokEventObserver +{ /** - * When more than one method listen for certain Even, you can specify the method priority - * @return + * When more than one method listen for certain Event, you can specify the method priority + * @see Priority */ Priority priority() default Priority.NORMAL; /** - * When true, action is invoked on the another thread - * @return + * When true, action is invoked on a thread, from the threads pool */ boolean async() default false; } diff --git a/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/LiveClientBuilder.java b/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/LiveClientBuilder.java index 2459d31..8e343c9 100644 --- a/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/LiveClientBuilder.java +++ b/API/src/main/java/io/github/jwdeveloper/tiktok/live/builder/LiveClientBuilder.java @@ -32,10 +32,11 @@ import java.util.function.Consumer; public interface LiveClientBuilder extends EventsBuilder { + /** * This method is triggered after default mappings are registered * It could be used to OVERRIDE behaviour of mappings and implement custom behaviour - * + *

* Be aware if for example you override WebcastGiftEvent, onGiftEvent() will not be working * * @param onCustomMappings lambda method @@ -43,18 +44,22 @@ public interface LiveClientBuilder extends EventsBuilder { */ LiveClientBuilder mappings(Consumer onCustomMappings); + @Deprecated(forRemoval = true, since = "1.8.2 use `mappings` method instead") + LiveClientBuilder onMappings(Consumer onCustomMappings); /** * Configuration of client settings - * @see LiveClientSettings + * * @param onConfigure * @return + * @see LiveClientSettings */ LiveClientBuilder configure(Consumer onConfigure); /** * Adding events listener class, its fancy way to register events without using lamda method * but actual method in class that implements TikTokEventListener + * * @return */ LiveClientBuilder addListener(Object listener); @@ -64,7 +69,6 @@ public interface LiveClientBuilder extends EventsBuilder { * Allows you to use own implementation of internal TikTokLive dependencies, * when the default implementation does not meet your needs * - * * @param onCustomizeDependencies access to dependency container * @return */ @@ -72,18 +76,19 @@ public interface LiveClientBuilder extends EventsBuilder { /** * Builds new instance of the LiveClient + * * @return LiveClient object */ LiveClient build(); /** * Builds new instance of the LiveClient and connects to live + * * @return LiveClient object */ LiveClient buildAndConnect(); /** - * * @return LiveClient object and connects to TikTok live asynchronously */ CompletableFuture buildAndConnectAsync(); diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java index c418fa5..653f184 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClient.java @@ -78,17 +78,6 @@ public class TikTokLiveClient implements LiveClient this.logger = logger; } - public void connectAsync(Consumer onConnection) { - connectAsync().thenAccept(onConnection); - } - - public CompletableFuture connectAsync() { - return CompletableFuture.supplyAsync(() -> { - connect(); - return this; - }); - } - public void connect() { try { tryConnect(); @@ -199,4 +188,15 @@ public class TikTokLiveClient implements LiveClient messageHandler.handleSingleMessage(this, message); } + + public void connectAsync(Consumer onConnection) { + connectAsync().thenAccept(onConnection); + } + + public CompletableFuture connectAsync() { + return CompletableFuture.supplyAsync(() -> { + connect(); + return this; + }); + } } \ No newline at end of file diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java index a01d5dc..8c01568 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/TikTokLiveClientBuilder.java @@ -70,6 +70,12 @@ public class TikTokLiveClientBuilder implements LiveClientBuilder { return this; } + @Override + public LiveClientBuilder onMappings(Consumer onCustomMappings) { + mappings(onCustomMappings); + return this; + } + public TikTokLiveClientBuilder configure(Consumer onConfigure) { onConfigure.accept(clientSettings); return this; diff --git a/Client/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokListenersManager.java b/Client/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokListenersManager.java index a99a212..1f482d9 100644 --- a/Client/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokListenersManager.java +++ b/Client/src/main/java/io/github/jwdeveloper/tiktok/listener/TikTokListenersManager.java @@ -25,6 +25,7 @@ package io.github.jwdeveloper.tiktok.listener; import io.github.jwdeveloper.dependance.api.DependanceContainer; import io.github.jwdeveloper.tiktok.annotations.TikTokEventObserver; +import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent; import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; import io.github.jwdeveloper.tiktok.exceptions.TikTokEventListenerMethodException; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; @@ -40,14 +41,14 @@ import java.util.concurrent.atomic.AtomicReference; public class TikTokListenersManager implements ListenersManager { private final Map> listeners; - private final LiveEventsHandler eventObserver; + private final LiveEventsHandler eventsHandler; private final ExecutorService executorService; private final DependanceContainer dependanceContainer; public TikTokListenersManager(LiveEventsHandler tikTokEventHandler, DependanceContainer dependanceContainer) { - this.eventObserver = tikTokEventHandler; + this.eventsHandler = tikTokEventHandler; this.dependanceContainer = dependanceContainer; this.listeners = new HashMap<>(); executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); @@ -66,7 +67,7 @@ public class TikTokListenersManager implements ListenersManager { var methodsInfo = getMethodsInfo(listener); for (var methodInfo : methodsInfo) { - eventObserver.subscribe(methodInfo.getEventType(), methodInfo.getAction()); + eventsHandler.subscribe(methodInfo.getEventType(), methodInfo.getAction()); } listeners.put(listener, methodsInfo); } @@ -78,7 +79,7 @@ public class TikTokListenersManager implements ListenersManager { } var methodsInfo = listeners.get(listener); for (var methodInfo : methodsInfo) { - eventObserver.unsubscribe(methodInfo.getEventType(), methodInfo.getAction()); + eventsHandler.unsubscribe(methodInfo.getEventType(), methodInfo.getAction()); } listeners.remove(listener); } @@ -149,7 +150,7 @@ public class TikTokListenersManager implements ListenersManager { var parameters = methodContainer.resolveParameters(method); method.invoke(listener, parameters); } catch (Exception e) { - throw new TikTokEventListenerMethodException(e); + eventsHandler.publish(liveClient, new TikTokErrorEvent(new TikTokEventListenerMethodException(e))); } }; } diff --git a/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokLiveClientTests.java b/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokLiveClientTests.java index 0510619..5fc1989 100644 --- a/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokLiveClientTests.java +++ b/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokLiveClientTests.java @@ -1,110 +1,51 @@ package io.github.jwdeveloper.tiktok; -import io.github.jwdeveloper.tiktok.data.requests.LiveData; -import io.github.jwdeveloper.tiktok.data.settings.LiveClientSettings; +import io.github.jwdeveloper.dependance.implementation.DependanceContainerBuilder; +import io.github.jwdeveloper.tiktok.data.events.TikTokConnectedEvent; +import io.github.jwdeveloper.tiktok.data.events.TikTokDisconnectedEvent; +import io.github.jwdeveloper.tiktok.data.events.TikTokErrorEvent; +import io.github.jwdeveloper.tiktok.data.events.control.TikTokConnectingEvent; +import io.github.jwdeveloper.tiktok.data.events.control.TikTokPreConnectionEvent; +import io.github.jwdeveloper.tiktok.data.events.http.TikTokRoomDataResponseEvent; +import io.github.jwdeveloper.tiktok.data.events.room.TikTokRoomInfoEvent; import io.github.jwdeveloper.tiktok.exceptions.TikTokLiveException; -import io.github.jwdeveloper.tiktok.http.LiveHttpClient; -import io.github.jwdeveloper.tiktok.listener.ListenersManager; -import io.github.jwdeveloper.tiktok.live.GiftsManager; -import io.github.jwdeveloper.tiktok.live.LiveClient; -import io.github.jwdeveloper.tiktok.live.LiveEventsHandler; -import io.github.jwdeveloper.tiktok.live.LiveMessagesHandler; +import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder; import io.github.jwdeveloper.tiktok.models.ConnectionState; -import io.github.jwdeveloper.tiktok.websocket.LiveSocketClient; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; -import java.util.logging.Logger; +public class TikTokLiveClientTests extends TikTokTestBase { -public class TikTokLiveClientTests { + @Override + public void onBeforeEachTest(LiveClientBuilder liveClientBuilder, + DependanceContainerBuilder containerBuilder) { - private LiveClient sut; - LiveMessagesHandler messageHandler; - GiftsManager giftsManager; - TikTokRoomInfo tikTokLiveMeta; - LiveHttpClient tiktokHttpClient; - LiveSocketClient webSocketClient; - LiveEventsHandler tikTokEventHandler; - LiveClientSettings clientSettings; - ListenersManager listenersManager; - Logger logger; - - @Before - public void onBefore() { - messageHandler = Mockito.mock(LiveMessagesHandler.class); - giftsManager = Mockito.mock(GiftsManager.class); - tikTokLiveMeta = Mockito.mock(TikTokRoomInfo.class); - tiktokHttpClient = Mockito.mock(LiveHttpClient.class); - webSocketClient = Mockito.mock(LiveSocketClient.class); - tikTokEventHandler = Mockito.mock(LiveEventsHandler.class); - clientSettings = Mockito.mock(LiveClientSettings.class); - listenersManager = Mockito.mock(ListenersManager.class); - logger = Mockito.mock(Logger.class); - - sut = new TikTokLiveClient(messageHandler, - giftsManager, - tikTokLiveMeta, - tiktokHttpClient, - webSocketClient, - tikTokEventHandler, - clientSettings, - listenersManager, - logger); + liveClientBuilder.onMappings() } - @Test public void shouldThrownWhenAlreadyConnected() { - tikTokLiveMeta.setConnectionState(ConnectionState.CONNECTED); + roomInfoMock().setConnectionState(ConnectionState.CONNECTED); Assert.assertThrows(TikTokLiveException.class, () -> { - sut.connect(); + liveClient().connect(); }); + Assert.assertEquals(ConnectionState.DISCONNECTED, roomInfoMock().getConnectionState()); + AssertEvents( + TikTokErrorEvent.class, + TikTokDisconnectedEvent.class + ); } - @Test - public void shouldThrowWhenUserIsOffline() { - - var request = new LiveData.Request("X"); - var response = new LiveData.Response(); - response.setLiveStatus(LiveData.LiveStatus.HostOffline); - Mockito.when(tiktokHttpClient.fetchLiveData(request)).thenReturn(response); - Assert.assertThrows(TikTokLiveException.class, () -> - { - sut.connect(); - }); - } - - @Test - public void shouldThrowWhenUserNotFound() - { - var request = new LiveData.Request("X"); - var response = new LiveData.Response(); - response.setLiveStatus(LiveData.LiveStatus.HostNotFound); - Mockito.when(tiktokHttpClient.fetchLiveData(request)).thenReturn(response); - Assert.assertThrows(TikTokLiveException.class, () -> - { - sut.connect(); - }); - } - - @Test - public void shouldThrowWhenAgeRestricted() - { - Mockito.when(tiktokHttpClient.fetchLiveData(new LiveData.Request("X"))) - .thenReturn(new LiveData.Response()); - Assert.assertThrows(TikTokLiveException.class, () -> - { - sut.connect(); - }); - } - - @Test public void shouldConnect() { - // sut.connect(); + liveClient().connect(); + Assert.assertEquals(ConnectionState.CONNECTED, roomInfoMock().getConnectionState()); + AssertEvents( + TikTokConnectingEvent.class, + TikTokRoomDataResponseEvent.class, + TikTokPreConnectionEvent.class, + TikTokConnectedEvent.class, + TikTokRoomInfoEvent.class); } - } diff --git a/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokTestBase.java b/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokTestBase.java new file mode 100644 index 0000000..af6e434 --- /dev/null +++ b/Client/src/test/java/io/github/jwdeveloper/tiktok/TikTokTestBase.java @@ -0,0 +1,53 @@ +package io.github.jwdeveloper.tiktok; + +import io.github.jwdeveloper.dependance.implementation.DependanceContainerBuilder; +import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; +import io.github.jwdeveloper.tiktok.live.LiveClient; +import io.github.jwdeveloper.tiktok.live.LiveEventsHandler; +import io.github.jwdeveloper.tiktok.live.builder.LiveClientBuilder; +import io.github.jwdeveloper.tiktok.mocks.EventsHandlerMock; +import lombok.Getter; +import lombok.experimental.Accessors; +import org.junit.Before; + +/** + * Base class for the unit tests + */ + +@Getter +@Accessors(fluent = true) +public abstract class TikTokTestBase { + + private LiveClient liveClient; + + private EventsHandlerMock eventsHandlerMock; + + private TikTokRoomInfo roomInfoMock; + + public void AssertEvents(Class... events) { + eventsHandlerMock.assertEvents(events); + } + + @Before + public void setup() { + + var builder = TikTokLive.newClient("test"); + eventsHandlerMock = new EventsHandlerMock(); + roomInfoMock = new TikTokRoomInfo(); + roomInfoMock.setHostName("test"); + liveClient = builder + .configure(liveClientSettings -> + { + liveClientSettings.setOffline(true); + liveClientSettings.setFetchGifts(false); + }) + .customize(containerBuilder -> + { + containerBuilder.registerSingleton(LiveEventsHandler.class, eventsHandlerMock); + containerBuilder.registerSingleton(TikTokRoomInfo.class, roomInfoMock); + onBeforeEachTest(builder, containerBuilder); + }).build(); + } + + public abstract void onBeforeEachTest(LiveClientBuilder liveClientBuilder, DependanceContainerBuilder containerBuilder); +} diff --git a/Client/src/test/java/io/github/jwdeveloper/tiktok/mocks/EventsHandlerMock.java b/Client/src/test/java/io/github/jwdeveloper/tiktok/mocks/EventsHandlerMock.java new file mode 100644 index 0000000..e1123c2 --- /dev/null +++ b/Client/src/test/java/io/github/jwdeveloper/tiktok/mocks/EventsHandlerMock.java @@ -0,0 +1,46 @@ +package io.github.jwdeveloper.tiktok.mocks; + +import io.github.jwdeveloper.tiktok.TikTokLiveEventHandler; +import io.github.jwdeveloper.tiktok.data.events.common.TikTokEvent; +import io.github.jwdeveloper.tiktok.live.LiveClient; + +import java.util.ArrayList; +import java.util.List; + +/** + * Cache published events, + */ +public class EventsHandlerMock extends TikTokLiveEventHandler { + private final List publishedEvents = new ArrayList(); + + + @Override + public void publish(LiveClient tikTokLiveClient, TikTokEvent tikTokEvent) { + super.publish(tikTokLiveClient, tikTokEvent); + publishedEvents.add(tikTokEvent); + } + + @SafeVarargs + public final void assertEvents(Class... events) { + + if (events.length == 0 && !publishedEvents.isEmpty()) { + var classNames = publishedEvents.stream() + .map(e -> e.getClass().getSimpleName()) + .toList(); + var invokedEvents = String.join("\n", classNames); + throw new IllegalArgumentException("Not events should be invoked but there was: \n" + invokedEvents); + } + + + for (var i = 0; i < events.length; i++) { + var expectedEvent = events[i]; + var invokedEvent = publishedEvents.get(i); + if (expectedEvent.equals(invokedEvent.getClass())) { + continue; + } + throw new RuntimeException("Expected event was " + expectedEvent + " but acctuall was " + invokedEvent.getClass()); + } + + + } +}