From 4f4a4b9fb4ef9a4d9bfdbdedc19f438587a6c156 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 12 Jul 2026 10:21:37 +0200 Subject: [PATCH] Refactor manager mock --- .../signal/http/SseInitialFlushTest.java | 473 +------------- .../jsonrpc/SubscribeCallEventsTest.java | 605 ++---------------- .../asamk/signal/testutil/ManagerMock.java | 122 ++++ 3 files changed, 166 insertions(+), 1034 deletions(-) create mode 100644 src/test/java/org/asamk/signal/testutil/ManagerMock.java diff --git a/src/test/java/org/asamk/signal/http/SseInitialFlushTest.java b/src/test/java/org/asamk/signal/http/SseInitialFlushTest.java index 042a0cf6..d285ca1e 100644 --- a/src/test/java/org/asamk/signal/http/SseInitialFlushTest.java +++ b/src/test/java/org/asamk/signal/http/SseInitialFlushTest.java @@ -1,51 +1,15 @@ package org.asamk.signal.http; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.api.CallInfo; -import org.asamk.signal.manager.api.CallOffer; -import org.asamk.signal.manager.api.Configuration; -import org.asamk.signal.manager.api.Device; -import org.asamk.signal.manager.api.DeviceLinkUrl; -import org.asamk.signal.manager.api.Group; -import org.asamk.signal.manager.api.GroupId; -import org.asamk.signal.manager.api.GroupInviteLinkUrl; -import org.asamk.signal.manager.api.Identity; -import org.asamk.signal.manager.api.IdentityVerificationCode; -import org.asamk.signal.manager.api.Message; -import org.asamk.signal.manager.api.MessageEnvelope; -import org.asamk.signal.manager.api.Pair; -import org.asamk.signal.manager.api.ReceiveConfig; -import org.asamk.signal.manager.api.Recipient; -import org.asamk.signal.manager.api.RecipientIdentifier; -import org.asamk.signal.manager.api.SendGroupMessageResults; -import org.asamk.signal.manager.api.SendMessageResult; -import org.asamk.signal.manager.api.SendMessageResults; -import org.asamk.signal.manager.api.StickerPack; -import org.asamk.signal.manager.api.StickerPackId; -import org.asamk.signal.manager.api.StickerPackUrl; -import org.asamk.signal.manager.api.TurnServer; -import org.asamk.signal.manager.api.TypingAction; -import org.asamk.signal.manager.api.UpdateGroup; -import org.asamk.signal.manager.api.UpdateProfile; -import org.asamk.signal.manager.api.UserStatus; -import org.asamk.signal.manager.api.UsernameLinkUrl; -import org.asamk.signal.manager.api.UsernameStatus; +import org.asamk.signal.testutil.ManagerMock; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.io.File; -import java.io.InputStream; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.URI; -import java.time.Duration; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -77,7 +41,8 @@ class SseInitialFlushTest { @BeforeEach void setUp() throws Exception { port = freePort(); - handler = new HttpServerHandler(new InetSocketAddress("127.0.0.1", port), new MinimalStubManager()); + final Manager manager = ManagerMock.create("+10000000000"); + handler = new HttpServerHandler(new InetSocketAddress("127.0.0.1", port), manager); handler.init(); } @@ -111,436 +76,4 @@ class SseInitialFlushTest { }, "SSE endpoint did not return the initial response within 2 seconds"); } - // ------------------------------------------------------------------------- - // Minimal Manager stub — only receive-handler methods need real behaviour; - // everything else is a no-op stub. - // ------------------------------------------------------------------------- - - private static final class MinimalStubManager implements Manager { - - @Override - public String getSelfNumber() { - return "+10000000000"; - } - - @Override - public String getSelfACI() { - return "00000000-0000-0000-0000-000000000000"; - } - - @Override - public void addReceiveHandler(ReceiveMessageHandler handler, boolean isWeakListener) { - // no-op - } - - @Override - public void removeReceiveHandler(ReceiveMessageHandler handler) { - // no-op - } - - @Override - public boolean isReceiving() { - return false; - } - - @Override - public void receiveMessages(Optional timeout, Optional maxMessages, ReceiveMessageHandler handler) { - } - - @Override - public void stopReceiveMessages() { - } - - @Override - public void setReceiveConfig(ReceiveConfig receiveConfig) { - } - - @Override - public Map getUserStatus(Set numbers) { - return Map.of(); - } - - @Override - public Map getUsernameStatus(Set usernames) { - return Map.of(); - } - - @Override - public void updateAccountAttributes(String deviceName, Boolean unidentifiedDeliveryIndicators, Boolean discoverableByNumber, Boolean numberSharing) { - } - - @Override - public Configuration getConfiguration() { - return null; - } - - @Override - public void updateConfiguration(Configuration configuration) { - } - - @Override - public void updateProfile(UpdateProfile updateProfile) { - } - - @Override - public String getUsername() { - return null; - } - - @Override - public UsernameLinkUrl getUsernameLink() { - return null; - } - - @Override - public void setUsername(String username) { - } - - @Override - public void deleteUsername() { - } - - @Override - public void startChangeNumber(String newNumber, boolean voiceVerification, String captcha) { - } - - @Override - public void finishChangeNumber(String newNumber, String verificationCode, String pin) { - } - - @Override - public void unregister() { - } - - @Override - public void deleteAccount() { - } - - @Override - public void submitRateLimitRecaptchaChallenge(String challenge, String captcha) { - } - - @Override - public List getLinkedDevices() { - return List.of(); - } - - @Override - public void updateLinkedDevice(int deviceId, String name) { - } - - @Override - public void removeLinkedDevices(int deviceId) { - } - - @Override - public void addDeviceLink(DeviceLinkUrl deviceLinkUrl) { - } - - @Override - public void setRegistrationLockPin(Optional pin) { - } - - @Override - public List getGroups() { - return List.of(); - } - - @Override - public List getGroups(Collection groupIds) { - return List.of(); - } - - @Override - public SendGroupMessageResults quitGroup(GroupId groupId, Set administrators) { - return null; - } - - @Override - public void deleteGroup(GroupId groupId) { - } - - @Override - public Pair createGroup(String name, Set members, String avatarFile) { - return null; - } - - @Override - public SendGroupMessageResults updateGroup(GroupId groupId, UpdateGroup updateGroup) { - return null; - } - - @Override - public Pair joinGroup(GroupInviteLinkUrl inviteLinkUrl) { - return null; - } - - @Override - public SendMessageResults sendTypingMessage(TypingAction action, Set recipients) { - return null; - } - - @Override - public SendMessageResults sendReadReceipt(RecipientIdentifier.Single sender, List messageIds) { - return null; - } - - @Override - public SendMessageResults sendViewedReceipt(RecipientIdentifier.Single sender, List messageIds) { - return null; - } - - @Override - public SendMessageResults sendMessage(Message message, Set recipients, boolean notifySelf) { - return null; - } - - @Override - public SendMessageResults sendEditMessage(Message message, Set recipients, long targetSentTimestamp) { - return null; - } - - @Override - public SendMessageResults sendRemoteDeleteMessage(long targetSentTimestamp, Set recipients) { - return null; - } - - @Override - public SendMessageResults sendMessageReaction(String emoji, boolean remove, RecipientIdentifier.Single targetAuthor, long targetSentTimestamp, Set recipients, boolean notifySelf, boolean story) { - return null; - } - - @Override - public SendMessageResults sendAdminDelete(RecipientIdentifier.Single targetAuthor, long targetSentTimestamp, Set recipients, boolean notifySelf, boolean story) { - return null; - } - - @Override - public SendMessageResults sendPinMessage(int duration, RecipientIdentifier.Single targetAuthor, long targetSentTimestamp, Set recipients, boolean notifySelf, boolean story) { - return null; - } - - @Override - public SendMessageResults sendUnpinMessage(RecipientIdentifier.Single targetAuthor, long targetSentTimestamp, Set recipients, boolean notifySelf, boolean story) { - return null; - } - - @Override - public SendMessageResults sendPaymentNotificationMessage(byte[] receipt, String note, RecipientIdentifier.Single recipient) { - return null; - } - - @Override - public void sendEndSessionMessage(Set recipients) { - } - - @Override - public SendMessageResults sendMessageRequestResponse(MessageEnvelope.Sync.MessageRequestResponse.Type type, Set recipients) { - return null; - } - - @Override - public SendMessageResults sendPollCreateMessage(String question, boolean multipleChoice, List options, Set recipients, boolean notifySelf) { - return null; - } - - @Override - public SendMessageResults sendPollVoteMessage(RecipientIdentifier.Single author, long timestamp, List optionIds, int version, Set recipients, boolean notifySelf) { - return null; - } - - @Override - public SendMessageResults sendPollTerminateMessage(long timestamp, Set recipients, boolean notifySelf) { - return null; - } - - @Override - public SendMessageResults sendStory(String attachment, boolean allowsReplies) { - return new SendMessageResults(0, Map.of()); - } - - @Override - public void hideRecipient(RecipientIdentifier.Single recipient) { - } - - @Override - public void deleteRecipient(RecipientIdentifier.Single recipient) { - } - - @Override - public void deleteContact(RecipientIdentifier.Single recipient) { - } - - @Override - public void setContactName(RecipientIdentifier.Single recipient, String givenName, String familyName, String newGivenName, String newFamilyName, String nick) { - } - - @Override - public void setContactsBlocked(Collection recipients, boolean blocked) { - } - - @Override - public void setGroupsBlocked(Collection groupIds, boolean blocked) { - } - - @Override - public void setExpirationTimer(RecipientIdentifier.Single recipient, int messageExpirationTimer) { - } - - @Override - public StickerPackUrl uploadStickerPack(File path) { - return null; - } - - @Override - public void installStickerPack(StickerPackUrl url) { - } - - @Override - public List getStickerPacks() { - return List.of(); - } - - @Override - public void requestAllSyncData() { - } - - @Override - public boolean isContactBlocked(RecipientIdentifier.Single recipient) { - return false; - } - - @Override - public void sendContacts() { - } - - @Override - public List getRecipients(boolean onlyWithProfile, Optional blocked, Collection addresses, Optional name) { - return List.of(); - } - - @Override - public String getContactOrProfileName(RecipientIdentifier.Single recipient) { - return null; - } - - @Override - public Group getGroup(GroupId groupId) { - return null; - } - - @Override - public List getIdentities() { - return List.of(); - } - - @Override - public List getIdentities(RecipientIdentifier.Single recipient) { - return List.of(); - } - - @Override - public boolean trustIdentityVerified(RecipientIdentifier.Single recipient, IdentityVerificationCode verificationCode) { - return false; - } - - @Override - public boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient) { - return false; - } - - @Override - public void addAddressChangedListener(Runnable listener) { - } - - @Override - public void addClosedListener(Runnable listener) { - } - - @Override - public InputStream retrieveAttachment(String id) { - return null; - } - - @Override - public InputStream retrieveContactAvatar(RecipientIdentifier.Single recipient) { - return null; - } - - @Override - public InputStream retrieveProfileAvatar(RecipientIdentifier.Single recipient) { - return null; - } - - @Override - public InputStream retrieveGroupAvatar(GroupId groupId) { - return null; - } - - @Override - public InputStream retrieveSticker(StickerPackId stickerPackId, int stickerId) { - return null; - } - - @Override - public CallInfo startCall(RecipientIdentifier.Single recipient) { - return null; - } - - @Override - public CallInfo acceptCall(long callId) { - return null; - } - - @Override - public void hangupCall(long callId) { - } - - @Override - public SendMessageResult rejectCall(long callId) { - return null; - } - - @Override - public List listActiveCalls() { - return List.of(); - } - - @Override - public void sendCallOffer(RecipientIdentifier.Single recipient, CallOffer callOffer) { - } - - @Override - public void sendCallAnswer(RecipientIdentifier.Single recipient, long callId, byte[] answer) { - } - - @Override - public void sendIceUpdate(RecipientIdentifier.Single recipient, long callId, List iceCandidates) { - } - - @Override - public void sendHangup(RecipientIdentifier.Single recipient, long callId, MessageEnvelope.Call.Hangup.Type type) { - } - - @Override - public void sendBusy(RecipientIdentifier.Single recipient, long callId) { - } - - @Override - public List getTurnServerInfo() { - return List.of(); - } - - @Override - public void close() { - } - - @Override - public void addCallEventListener(CallEventListener listener) { - } - - @Override - public void removeCallEventListener(CallEventListener listener) { - } - } } diff --git a/src/test/java/org/asamk/signal/jsonrpc/SubscribeCallEventsTest.java b/src/test/java/org/asamk/signal/jsonrpc/SubscribeCallEventsTest.java index d468fafe..df496707 100644 --- a/src/test/java/org/asamk/signal/jsonrpc/SubscribeCallEventsTest.java +++ b/src/test/java/org/asamk/signal/jsonrpc/SubscribeCallEventsTest.java @@ -4,52 +4,16 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.MultiAccountManager; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.RegistrationManager; -import org.asamk.signal.manager.api.CallInfo; -import org.asamk.signal.manager.api.CallOffer; -import org.asamk.signal.manager.api.Configuration; -import org.asamk.signal.manager.api.Device; -import org.asamk.signal.manager.api.DeviceLinkUrl; -import org.asamk.signal.manager.api.Group; -import org.asamk.signal.manager.api.GroupId; -import org.asamk.signal.manager.api.GroupInviteLinkUrl; -import org.asamk.signal.manager.api.Identity; -import org.asamk.signal.manager.api.IdentityVerificationCode; -import org.asamk.signal.manager.api.Message; -import org.asamk.signal.manager.api.MessageEnvelope; -import org.asamk.signal.manager.api.Pair; -import org.asamk.signal.manager.api.ReceiveConfig; -import org.asamk.signal.manager.api.Recipient; -import org.asamk.signal.manager.api.RecipientIdentifier; -import org.asamk.signal.manager.api.SendGroupMessageResults; -import org.asamk.signal.manager.api.SendMessageResult; -import org.asamk.signal.manager.api.SendMessageResults; -import org.asamk.signal.manager.api.StickerPack; -import org.asamk.signal.manager.api.StickerPackId; -import org.asamk.signal.manager.api.StickerPackUrl; -import org.asamk.signal.manager.api.TurnServer; -import org.asamk.signal.manager.api.TypingAction; -import org.asamk.signal.manager.api.UpdateGroup; -import org.asamk.signal.manager.api.UpdateProfile; -import org.asamk.signal.manager.api.UserStatus; -import org.asamk.signal.manager.api.UsernameLinkUrl; -import org.asamk.signal.manager.api.UsernameStatus; import org.asamk.signal.output.JsonWriter; +import org.asamk.signal.testutil.ManagerMock; import org.junit.jupiter.api.Test; -import java.io.File; -import java.io.InputStream; import java.net.URI; -import java.time.Duration; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.Queue; -import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -89,499 +53,12 @@ class SubscribeCallEventsTest { } } - /** - * Minimal Manager stub that tracks call event listener add/remove calls. - */ - private static class StubManager implements Manager { + private record ManagedManager(Manager manager, ManagerMock.State state) { + } - final List listeners = new ArrayList<>(); - final AtomicInteger addCount = new AtomicInteger(0); - final AtomicInteger removeCount = new AtomicInteger(0); - final String selfNumber; - - StubManager(String selfNumber) { - this.selfNumber = selfNumber; - } - - @Override - public void addCallEventListener(CallEventListener listener) { - addCount.incrementAndGet(); - listeners.add(listener); - } - - @Override - public void removeCallEventListener(CallEventListener listener) { - removeCount.incrementAndGet(); - listeners.remove(listener); - } - - @Override - public String getSelfNumber() { - return selfNumber; - } - - @Override - public String getSelfACI() { - return "00000000-0000-0000-0000-000000000000"; - } - - // --- Stubs for remaining Manager interface methods --- - @Override - public Map getUserStatus(Set n) { - return Map.of(); - } - - @Override - public Map getUsernameStatus(Set u) { - return Map.of(); - } - - @Override - public void updateAccountAttributes(String d, Boolean u, Boolean dn, Boolean ns) { - } - - @Override - public Configuration getConfiguration() { - return null; - } - - @Override - public void updateConfiguration(Configuration c) { - } - - @Override - public void updateProfile(UpdateProfile u) { - } - - @Override - public String getUsername() { - return null; - } - - @Override - public UsernameLinkUrl getUsernameLink() { - return null; - } - - @Override - public void setUsername(String u) { - } - - @Override - public void deleteUsername() { - } - - @Override - public void startChangeNumber(String n, boolean v, String c) { - } - - @Override - public void finishChangeNumber(String n, String v, String p) { - } - - @Override - public void unregister() { - } - - @Override - public void deleteAccount() { - } - - @Override - public void submitRateLimitRecaptchaChallenge(String c, String cap) { - } - - @Override - public List getLinkedDevices() { - return List.of(); - } - - @Override - public void updateLinkedDevice(int d, String n) { - } - - @Override - public void removeLinkedDevices(int d) { - } - - @Override - public void addDeviceLink(DeviceLinkUrl u) { - } - - @Override - public void setRegistrationLockPin(Optional p) { - } - - @Override - public List getGroups() { - return List.of(); - } - - @Override - public List getGroups(Collection g) { - return List.of(); - } - - @Override - public SendGroupMessageResults quitGroup(GroupId g, Set a) { - return null; - } - - @Override - public void deleteGroup(GroupId g) { - } - - @Override - public Pair createGroup( - String n, - Set m, - String a - ) { - return null; - } - - @Override - public SendGroupMessageResults updateGroup(GroupId g, UpdateGroup u) { - return null; - } - - @Override - public Pair joinGroup(GroupInviteLinkUrl u) { - return null; - } - - @Override - public SendMessageResults sendTypingMessage(TypingAction a, Set r) { - return null; - } - - @Override - public SendMessageResults sendReadReceipt(RecipientIdentifier.Single s, List m) { - return null; - } - - @Override - public SendMessageResults sendViewedReceipt(RecipientIdentifier.Single s, List m) { - return null; - } - - @Override - public SendMessageResults sendMessage(Message m, Set r, boolean n) { - return null; - } - - @Override - public SendMessageResults sendEditMessage(Message m, Set r, long t) { - return null; - } - - @Override - public SendMessageResults sendRemoteDeleteMessage(long t, Set r) { - return null; - } - - @Override - public SendMessageResults sendMessageReaction( - String e, - boolean rm, - RecipientIdentifier.Single a, - long t, - Set r, - boolean n, - boolean s - ) { - return null; - } - - @Override - public SendMessageResults sendAdminDelete( - RecipientIdentifier.Single a, - long t, - Set r, - boolean n, - boolean s - ) { - return null; - } - - @Override - public SendMessageResults sendPinMessage( - int d, - RecipientIdentifier.Single a, - long t, - Set r, - boolean n, - boolean s - ) { - return null; - } - - @Override - public SendMessageResults sendUnpinMessage( - RecipientIdentifier.Single a, - long t, - Set r, - boolean n, - boolean s - ) { - return null; - } - - @Override - public SendMessageResults sendPaymentNotificationMessage(byte[] r, String n, RecipientIdentifier.Single re) { - return null; - } - - @Override - public void sendEndSessionMessage(Set r) { - } - - @Override - public SendMessageResults sendMessageRequestResponse( - MessageEnvelope.Sync.MessageRequestResponse.Type t, - Set r - ) { - return null; - } - - @Override - public SendMessageResults sendPollCreateMessage( - String q, - boolean a, - List o, - Set r, - boolean n - ) { - return null; - } - - @Override - public SendMessageResults sendPollVoteMessage( - RecipientIdentifier.Single a, - long t, - List o, - int v, - Set r, - boolean n - ) { - return null; - } - - @Override - public SendMessageResults sendPollTerminateMessage(long t, Set r, boolean n) { - return null; - } - - @Override - public SendMessageResults sendStory(String attachment, boolean allowsReplies) { - return new SendMessageResults(0, Map.of()); - } - - @Override - public void hideRecipient(RecipientIdentifier.Single r) { - } - - @Override - public void deleteRecipient(RecipientIdentifier.Single r) { - } - - @Override - public void deleteContact(RecipientIdentifier.Single r) { - } - - @Override - public void setContactName(RecipientIdentifier.Single r, String g, String f, String ng, String nf, String n) { - } - - @Override - public void setContactsBlocked(Collection r, boolean b) { - } - - @Override - public void setGroupsBlocked(Collection g, boolean b) { - } - - @Override - public void setExpirationTimer(RecipientIdentifier.Single r, int t) { - } - - @Override - public StickerPackUrl uploadStickerPack(File p) { - return null; - } - - @Override - public void installStickerPack(StickerPackUrl u) { - } - - @Override - public List getStickerPacks() { - return List.of(); - } - - @Override - public void requestAllSyncData() { - } - - @Override - public void addReceiveHandler(ReceiveMessageHandler h, boolean w) { - } - - @Override - public void removeReceiveHandler(ReceiveMessageHandler h) { - } - - @Override - public boolean isReceiving() { - return false; - } - - @Override - public void receiveMessages(Optional t, Optional m, ReceiveMessageHandler h) { - } - - @Override - public void stopReceiveMessages() { - } - - @Override - public void setReceiveConfig(ReceiveConfig r) { - } - - @Override - public boolean isContactBlocked(RecipientIdentifier.Single r) { - return false; - } - - @Override - public void sendContacts() { - } - - @Override - public List getRecipients( - boolean o, - Optional b, - Collection a, - Optional n - ) { - return List.of(); - } - - @Override - public String getContactOrProfileName(RecipientIdentifier.Single r) { - return null; - } - - @Override - public Group getGroup(GroupId g) { - return null; - } - - @Override - public List getIdentities() { - return List.of(); - } - - @Override - public List getIdentities(RecipientIdentifier.Single r) { - return List.of(); - } - - @Override - public boolean trustIdentityVerified(RecipientIdentifier.Single r, IdentityVerificationCode v) { - return false; - } - - @Override - public boolean trustIdentityAllKeys(RecipientIdentifier.Single r) { - return false; - } - - @Override - public void addAddressChangedListener(Runnable l) { - } - - @Override - public void addClosedListener(Runnable l) { - } - - @Override - public InputStream retrieveAttachment(String id) { - return null; - } - - @Override - public InputStream retrieveContactAvatar(RecipientIdentifier.Single r) { - return null; - } - - @Override - public InputStream retrieveProfileAvatar(RecipientIdentifier.Single r) { - return null; - } - - @Override - public InputStream retrieveGroupAvatar(GroupId g) { - return null; - } - - @Override - public InputStream retrieveSticker(StickerPackId s, int i) { - return null; - } - - @Override - public CallInfo startCall(RecipientIdentifier.Single r) { - return null; - } - - @Override - public CallInfo acceptCall(long c) { - return null; - } - - @Override - public void hangupCall(long c) { - } - - @Override - public SendMessageResult rejectCall(long c) { - return null; - } - - @Override - public List listActiveCalls() { - return List.of(); - } - - @Override - public void sendCallOffer(RecipientIdentifier.Single r, CallOffer o) { - } - - @Override - public void sendCallAnswer(RecipientIdentifier.Single r, long c, byte[] a) { - } - - @Override - public void sendIceUpdate(RecipientIdentifier.Single r, long c, List i) { - } - - @Override - public void sendHangup(RecipientIdentifier.Single r, long c, MessageEnvelope.Call.Hangup.Type t) { - } - - @Override - public void sendBusy(RecipientIdentifier.Single r, long c) { - } - - @Override - public List getTurnServerInfo() { - return List.of(); - } - - @Override - public void close() { - } + private static ManagedManager createManager(final String selfNumber) { + final var state = new ManagerMock.State(); + return new ManagedManager(ManagerMock.create(selfNumber, state), state); } /** @@ -652,21 +129,21 @@ class SubscribeCallEventsTest { @Test void callEventsNotSubscribedByDefault() { - var manager = new StubManager("+15551234567"); + var manager = createManager("+15551234567"); var feeder = new LineFeeder(); var writer = new CapturingJsonWriter(); // Send no subscribeCallEvents, just end the connection var handler = new SignalJsonRpcDispatcherHandler(writer, feeder::getLine, true); - handler.handleConnection(manager); + handler.handleConnection(manager.manager()); // No listeners should have been added - assertEquals(0, manager.addCount.get(), "call events should not be auto-subscribed"); + assertEquals(0, manager.state().addCallEventListenerCount.get(), "call events should not be auto-subscribed"); } @Test void subscribeCallEventsAddsListener() { - var manager = new StubManager("+15551234567"); + var manager = createManager("+15551234567"); var feeder = new LineFeeder(); var writer = new CapturingJsonWriter(); @@ -674,17 +151,17 @@ class SubscribeCallEventsTest { // null terminates the read loop var handler = new SignalJsonRpcDispatcherHandler(writer, feeder::getLine, true); - handler.handleConnection(manager); + handler.handleConnection(manager.manager()); - assertEquals(1, manager.addCount.get(), "subscribeCallEvents should add one listener"); + assertEquals(1, manager.state().addCallEventListenerCount.get(), "subscribeCallEvents should add one listener"); // Cleanup in finally block should remove it - assertEquals(1, manager.removeCount.get(), "cleanup should remove the listener"); - assertEquals(0, manager.listeners.size(), "no listeners should remain after cleanup"); + assertEquals(1, manager.state().removeCallEventListenerCount.get(), "cleanup should remove the listener"); + assertEquals(0, manager.state().callEventListeners.size(), "no listeners should remain after cleanup"); } @Test void subscribeCallEventsCanBeCalledMultipleTimes() { - var manager = new StubManager("+15551234567"); + var manager = createManager("+15551234567"); var feeder = new LineFeeder(); var writer = new CapturingJsonWriter(); @@ -692,15 +169,15 @@ class SubscribeCallEventsTest { feeder.addLine(jsonRpcCall(2, "subscribeCallEvents")); var handler = new SignalJsonRpcDispatcherHandler(writer, feeder::getLine, true); - handler.handleConnection(manager); + handler.handleConnection(manager.manager()); // The implementation allows multiple subscriptions, so two calls add two listeners - assertEquals(2, manager.addCount.get(), "multiple subscribeCallEvents should add multiple listeners"); + assertEquals(2, manager.state().addCallEventListenerCount.get(), "multiple subscribeCallEvents should add multiple listeners"); } @Test void unsubscribeCallEventsRemovesListener() { - var manager = new StubManager("+15551234567"); + var manager = createManager("+15551234567"); var feeder = new LineFeeder(); var writer = new CapturingJsonWriter(); @@ -708,37 +185,37 @@ class SubscribeCallEventsTest { feeder.addLine(jsonRpcCall(2, "unsubscribeCallEvents", "{\"subscription\":0}")); var handler = new SignalJsonRpcDispatcherHandler(writer, feeder::getLine, true); - handler.handleConnection(manager); + handler.handleConnection(manager.manager()); - assertEquals(1, manager.addCount.get(), "should have subscribed once"); + assertEquals(1, manager.state().addCallEventListenerCount.get(), "should have subscribed once"); // removeCount: 1 from explicit unsubscribe. The finally block's unsubscribeAllCallEvents // iterates an empty list so adds 0 more. - assertEquals(1, manager.removeCount.get(), "should have unsubscribed once"); - assertEquals(0, manager.listeners.size()); + assertEquals(1, manager.state().removeCallEventListenerCount.get(), "should have unsubscribed once"); + assertEquals(0, manager.state().callEventListeners.size()); } @Test void unsubscribeWithoutSubscribeIsNoOp() { - var manager = new StubManager("+15551234567"); + var manager = createManager("+15551234567"); var feeder = new LineFeeder(); var writer = new CapturingJsonWriter(); feeder.addLine(jsonRpcCall(1, "unsubscribeCallEvents")); var handler = new SignalJsonRpcDispatcherHandler(writer, feeder::getLine, true); - handler.handleConnection(manager); + handler.handleConnection(manager.manager()); - assertEquals(0, manager.addCount.get()); - assertEquals(0, manager.removeCount.get()); + assertEquals(0, manager.state().addCallEventListenerCount.get()); + assertEquals(0, manager.state().removeCallEventListenerCount.get()); } // --- Multi-account mode tests --- @Test void multiAccountSubscribeCallEventsSubscribesAllManagers() { - var manager1 = new StubManager("+15551111111"); - var manager2 = new StubManager("+15552222222"); - var multi = new StubMultiAccountManager(List.of(manager1, manager2)); + var manager1 = createManager("+15551111111"); + var manager2 = createManager("+15552222222"); + var multi = new StubMultiAccountManager(List.of(manager1.manager(), manager2.manager())); var feeder = new LineFeeder(); var writer = new CapturingJsonWriter(); @@ -748,17 +225,17 @@ class SubscribeCallEventsTest { var handler = new SignalJsonRpcDispatcherHandler(writer, feeder::getLine, true); handler.handleConnection(multi); - assertEquals(1, manager1.addCount.get(), "manager1 should have one listener"); - assertEquals(1, manager2.addCount.get(), "manager2 should have one listener"); + assertEquals(1, manager1.state().addCallEventListenerCount.get(), "manager1 should have one listener"); + assertEquals(1, manager2.state().addCallEventListenerCount.get(), "manager2 should have one listener"); // Also registers an onManagerAdded handler for receive and one for call events assertEquals(2, multi.addedHandlers.size(), "should register onManagerAdded handlers"); } @Test void multiAccountUnsubscribeCallEventsCleansUpAll() { - var manager1 = new StubManager("+15551111111"); - var manager2 = new StubManager("+15552222222"); - var multi = new StubMultiAccountManager(List.of(manager1, manager2)); + var manager1 = createManager("+15551111111"); + var manager2 = createManager("+15552222222"); + var multi = new StubMultiAccountManager(List.of(manager1.manager(), manager2.manager())); var feeder = new LineFeeder(); var writer = new CapturingJsonWriter(); @@ -769,16 +246,16 @@ class SubscribeCallEventsTest { var handler = new SignalJsonRpcDispatcherHandler(writer, feeder::getLine, true); handler.handleConnection(multi); - assertEquals(1, manager1.addCount.get()); - assertEquals(1, manager2.addCount.get()); - assertEquals(1, manager1.removeCount.get(), "manager1 listener should be removed"); - assertEquals(1, manager2.removeCount.get(), "manager2 listener should be removed"); + assertEquals(1, manager1.state().addCallEventListenerCount.get()); + assertEquals(1, manager2.state().addCallEventListenerCount.get()); + assertEquals(1, manager1.state().removeCallEventListenerCount.get(), "manager1 listener should be removed"); + assertEquals(1, manager2.state().removeCallEventListenerCount.get(), "manager2 listener should be removed"); } @Test void multiAccountCallEventsNotSubscribedByDefault() { - var manager1 = new StubManager("+15551111111"); - var multi = new StubMultiAccountManager(List.of(manager1)); + var manager1 = createManager("+15551111111"); + var multi = new StubMultiAccountManager(List.of(manager1.manager())); var feeder = new LineFeeder(); var writer = new CapturingJsonWriter(); @@ -786,6 +263,6 @@ class SubscribeCallEventsTest { var handler = new SignalJsonRpcDispatcherHandler(writer, feeder::getLine, true); handler.handleConnection(multi); - assertEquals(0, manager1.addCount.get(), "call events should not be auto-subscribed in multi mode"); + assertEquals(0, manager1.state().addCallEventListenerCount.get(), "call events should not be auto-subscribed in multi mode"); } } diff --git a/src/test/java/org/asamk/signal/testutil/ManagerMock.java b/src/test/java/org/asamk/signal/testutil/ManagerMock.java new file mode 100644 index 00000000..48901fc9 --- /dev/null +++ b/src/test/java/org/asamk/signal/testutil/ManagerMock.java @@ -0,0 +1,122 @@ +package org.asamk.signal.testutil; + +import org.asamk.signal.manager.Manager; + +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +public final class ManagerMock { + + private static final String DEFAULT_ACI = "00000000-0000-0000-0000-000000000000"; + + private ManagerMock() { + } + + public static final class State { + + public final List callEventListeners = new ArrayList<>(); + public final AtomicInteger addCallEventListenerCount = new AtomicInteger(0); + public final AtomicInteger removeCallEventListenerCount = new AtomicInteger(0); + + public final List receiveHandlers = new ArrayList<>(); + public final AtomicInteger addReceiveHandlerCount = new AtomicInteger(0); + public final AtomicInteger removeReceiveHandlerCount = new AtomicInteger(0); + } + + public static Manager create(final String selfNumber) { + return create(selfNumber, new State()); + } + + public static Manager create(final String selfNumber, final State state) { + return (Manager) Proxy.newProxyInstance( + Manager.class.getClassLoader(), + new Class[]{Manager.class}, + (proxy, method, args) -> { + final var methodName = method.getName(); + switch (methodName) { + case "getSelfNumber": + return selfNumber; + case "getSelfACI": + return DEFAULT_ACI; + case "addCallEventListener": + state.addCallEventListenerCount.incrementAndGet(); + state.callEventListeners.add((Manager.CallEventListener) args[0]); + return null; + case "removeCallEventListener": + state.removeCallEventListenerCount.incrementAndGet(); + state.callEventListeners.remove((Manager.CallEventListener) args[0]); + return null; + case "addReceiveHandler": + state.addReceiveHandlerCount.incrementAndGet(); + state.receiveHandlers.add((Manager.ReceiveMessageHandler) args[0]); + return null; + case "removeReceiveHandler": + state.removeReceiveHandlerCount.incrementAndGet(); + state.receiveHandlers.remove((Manager.ReceiveMessageHandler) args[0]); + return null; + case "equals": + return proxy == args[0]; + case "hashCode": + return System.identityHashCode(proxy); + case "toString": + return "ManagerMock{" + selfNumber + "}"; + default: + return defaultValue(method.getReturnType()); + } + } + ); + } + + private static Object defaultValue(final Class returnType) { + if (returnType == Void.TYPE) { + return null; + } + if (returnType == Boolean.TYPE) { + return false; + } + if (returnType == Byte.TYPE) { + return (byte) 0; + } + if (returnType == Short.TYPE) { + return (short) 0; + } + if (returnType == Integer.TYPE) { + return 0; + } + if (returnType == Long.TYPE) { + return 0L; + } + if (returnType == Float.TYPE) { + return 0F; + } + if (returnType == Double.TYPE) { + return 0D; + } + if (returnType == Character.TYPE) { + return '\0'; + } + if (returnType == Optional.class) { + return Optional.empty(); + } + if (returnType == List.class) { + return List.of(); + } + if (returnType == Set.class) { + return Set.of(); + } + if (returnType == Collection.class) { + return List.of(); + } + if (returnType == Map.class) { + return Map.of(); + } + + return null; + } +}