diff --git a/README.md b/README.md index 32f4a29c..a98f651c 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ For a numbered account, ACCOUNT is your phone number in international format and should start with a "+" sign. (See [Wikipedia](https://en.wikipedia.org/wiki/List_of_country_calling_codes) for a list of all country codes.) For a linked account without a phone number, use its ACI (Account ID) instead. -See [numberless accounts](doc/numberless-accounts.md) for details. +See the [wiki](https://github.com/AsamK/signal-cli/wiki) for further documentation. * Link to an existing account diff --git a/doc/numberless-accounts.md b/doc/numberless-accounts.md deleted file mode 100644 index fa8c3b47..00000000 --- a/doc/numberless-accounts.md +++ /dev/null @@ -1,73 +0,0 @@ -# Linking an account without a phone number - -signal-cli can link as a secondary device to an account registered without a phone -number, such as those created in Signal Android 8.28. Creating or recovering a -primary numberless account using an Account Key is not supported. - -Run `signal-cli link -n signal-cli`, then scan the QR code from the mobile app's -Linked devices screen. The linking URI advertises `capabilities=nopni`. If linking -completes, the CLI prints the account's ACI instead of a phone number. - -Use that ACI with `-a` for subsequent commands: - -```sh -signal-cli -a YOUR_ACCOUNT_ACI receive -signal-cli -a YOUR_ACCOUNT_ACI listGroups -signal-cli -a YOUR_ACCOUNT_ACI send --note-to-self -m 'Numberless account test' -``` - -When only one local account exists, `-a` can be omitted. Numbered accounts continue -to accept their phone number or ACI. A numberless account cannot infer a country -code for a recipient's phone number; use a complete international number beginning -with `+`, an ACI, or a Signal username. - -In JSON-RPC, use the ACI in the `account` parameter. `listAccounts` and `finishLink` -return an `aci` field alongside `number`; `number` is null for a numberless account. -Receive events use the ACI in their `account` field when there is no phone number. -REST wrappers must understand those nullable numbers and ACI selectors before they -can be assumed compatible. - -Numberless accounts are also supported through D-Bus. Start -`signal-cli daemon --dbus`, then use the ACI with the D-Bus client: - -```sh -signal-cli --dbus -a YOUR_ACCOUNT_ACI listGroups -signal-cli --dbus -a YOUR_ACCOUNT_ACI send --note-to-self -m 'Numberless account test' -``` - -In multi-account mode, D-Bus exports numberless accounts under -`/org/asamk/Signal/ACI`, with the ACI's hyphens replaced by underscores. Numbered -accounts retain their phone-number-based paths. Use `SignalControl.listAccounts` -or `SignalControl.getAccount` to discover paths; `getAccount` accepts either a -phone number or an ACI. `Signal.getSelfNumber` returns an empty string when the -account has no phone number, and `Signal.getSelfACI` returns the ACI. -`Signal.listRecipientIdentifiers` includes contacts without a phone number; -`Signal.listNumbers` continues to return only phone numbers. - -## Validation - -Run `./gradlew test installDist` with JDK 25. Regression tests cover the linking -request (including ACI authentication and omission of PNI keys), numbered-account -linking, missing group credential salts, persisted account reload, account discovery -(including superseded accounts and legacy indexes), group membership lookup, -recipient-number formatting, and JSON account identifiers. - -D-Bus transport tests require a session bus. To run them on an isolated bus, use -`dbus-run-session -- ./gradlew --no-daemon :test --tests '*NumberlessDbusTest*' --rerun-tasks`. -These tests exercise the D-Bus server and client with a local test backend, including -mixed accounts, direct and group messages, self-directed operations, and account -removal. They do not contact Signal. Without a session bus, only the D-Bus unit -tests run and the transport tests are skipped. - -Live acceptance requires a real mobile account and is separate from these tests: - -1. Link a newly created secondary device using the numberless mobile account. -2. Restart signal-cli and verify that `listAccounts` still reports the ACI. -3. Send and receive a Note to Self message and a DM with a second account. -4. Receive a group message, list the group, and send a reply to that test group. -5. Run the JSON-RPC daemon, restart it, and repeat receiving and sending by ACI. -6. Repeat with `daemon --dbus` and the `--dbus -a YOUR_ACCOUNT_ACI` client, checking - direct messages, group messages, and Note to Self. - -Only one signal-cli process may open an account's state at a time. Stop the daemon -before running one-off commands against the same configuration directory. diff --git a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java index 15b60594..5d83819b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/MultiAccountManager.java @@ -8,8 +8,6 @@ import java.util.function.Consumer; public interface MultiAccountManager extends AutoCloseable { - List getAccountNumbers(); - List getManagers(); void addOnManagerAddedHandler(Consumer handler); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java index 6ada2fd7..210a5906 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java @@ -1,5 +1,6 @@ package org.asamk.signal.manager.helper; +import org.asamk.signal.manager.api.BadRequestException; import org.asamk.signal.manager.api.CaptchaRequiredException; import org.asamk.signal.manager.api.DeviceLinkUrl; import org.asamk.signal.manager.api.IncorrectPinException; @@ -18,7 +19,6 @@ import org.signal.core.models.ServiceId.ACI; import org.signal.core.models.ServiceId.PNI; import org.signal.core.util.Base64; import org.signal.core.util.crypto.DeviceNameCipher; -import org.signal.libsignal.net.RequestResult; import org.signal.libsignal.protocol.IdentityKeyPair; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.NoSessionException; @@ -28,7 +28,6 @@ import org.signal.libsignal.protocol.state.SignedPreKeyRecord; import org.signal.libsignal.protocol.util.KeyHelper; import org.signal.libsignal.usernames.BaseUsernameException; import org.signal.libsignal.usernames.Username; -import org.signal.network.api.AccountApiV2; import org.signal.network.api.AccountApiV2.SetAccountAttributesError; import org.signal.network.exceptions.NonSuccessfulResponseCodeException; import org.slf4j.Logger; @@ -60,13 +59,11 @@ import java.util.Objects; import java.util.UUID; import java.util.concurrent.TimeUnit; -import kotlin.Unit; import okio.ByteString; import static org.asamk.signal.manager.config.ServiceConfig.PREKEY_MAXIMUM_ID; import static org.asamk.signal.manager.util.Utils.handleResponseException; import static org.asamk.signal.manager.util.Utils.handleResponseExceptionSuspend; -import static org.asamk.signal.manager.util.Utils.runSuspendBlocking; import static org.whispersystems.signalservice.internal.util.Util.isEmpty; public class AccountHelper { @@ -575,17 +572,15 @@ public class AccountHelper { .setAccountAttributes(account.getAccountAttributes(null))); return; } - final var api = new AccountApiV2(dependencies.getAuthenticatedSignalWebSocket()); - final RequestResult result = runSuspendBlocking(cont -> api.setAccountAttributes( - account.getAccountAttributesV2(), - cont)); - if (result instanceof RequestResult.NonSuccess failure) { - if (failure.getError() instanceof SetAccountAttributesError.Unauthorized) { + try { + handleResponseExceptionSuspend(cont -> dependencies.getAccountApiV2() + .setAccountAttributes(account.getAccountAttributesV2(), cont)); + } catch (BadRequestException e) { + if (e.getError() instanceof SetAccountAttributesError.Unauthorized) { throw new AuthorizationFailedException(401, "Authorization failed!"); } throw new IOException("Account attribute update rate limited; try again later"); } - handleResponseException(result); } public void addDevice(DeviceLinkUrl deviceLinkInfo) throws IOException, org.asamk.signal.manager.api.DeviceLimitExceededException { diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/MultiAccountManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/MultiAccountManagerImpl.java index 0d6a60a8..c767afba 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/MultiAccountManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/MultiAccountManagerImpl.java @@ -39,13 +39,6 @@ public class MultiAccountManagerImpl implements MultiAccountManager { managers.forEach(m -> m.addClosedListener(() -> this.removeManager(m))); } - @Override - public List getAccountNumbers() { - synchronized (managers) { - return managers.stream().map(Manager::getSelfNumber).toList(); - } - } - @Override public List getManagers() { synchronized (managers) { diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ProvisioningManagerImpl.java index aca7e3f3..c92aec12 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ProvisioningManagerImpl.java @@ -19,6 +19,7 @@ package org.asamk.signal.manager.internal; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.Settings; +import org.asamk.signal.manager.api.BadRequestException; import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.storage.SignalAccount; @@ -29,7 +30,6 @@ import org.signal.core.models.ServiceId.ACI; import org.signal.core.models.ServiceId.PNI; import org.signal.core.models.backup.MediaRootBackupKey; import org.signal.core.util.crypto.DeviceNameCipher; -import org.signal.libsignal.net.RequestResult; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKeyPair; import org.signal.libsignal.protocol.ecc.ECPrivateKey; @@ -69,8 +69,7 @@ import kotlinx.coroutines.BuildersKt; import kotlinx.coroutines.CoroutineScope; import static org.asamk.signal.manager.util.KeyUtils.generatePreKeysForType; -import static org.asamk.signal.manager.util.Utils.handleResponseException; -import static org.asamk.signal.manager.util.Utils.runSuspendBlocking; +import static org.asamk.signal.manager.util.Utils.handleResponseExceptionSuspend; public class ProvisioningManagerImpl implements ProvisioningManager, Closeable { @@ -324,23 +323,19 @@ public class ProvisioningManagerImpl implements ProvisioningManager, Closeable { attrs.getPniRegistrationId(), attrs.getName(), attrs.getCapabilities()); - final RequestResult result = runSuspendBlocking(cont -> registrationApi.registerAsSecondaryDevice( - account.getAci(), - account.getPassword(), - provisioningCode, - deviceAttributes, - toRegistrationPreKeys(aciPreKeys), - toRegistrationPreKeys(pniPreKeys), - null, - cont)); - return getLinkedDeviceId(result); - } - - static int getLinkedDeviceId( - final RequestResult result - ) throws IOException { - if (result instanceof RequestResult.NonSuccess failure) { - throw switch (failure.getError()) { + try { + final LinkDeviceResponse result = handleResponseExceptionSuspend(cont -> registrationApi.registerAsSecondaryDevice( + account.getAci(), + account.getPassword(), + provisioningCode, + deviceAttributes, + toRegistrationPreKeys(aciPreKeys), + toRegistrationPreKeys(pniPreKeys), + null, + cont)); + return result.getDeviceId(); + } catch (BadRequestException e) { + throw switch (e.getError()) { case RegisterAsLinkedDeviceError.IncorrectVerification ignored -> new AuthorizationFailedException(403, "Device verification failed"); case RegisterAsLinkedDeviceError.MissingCapability ignored -> @@ -354,7 +349,6 @@ public class ProvisioningManagerImpl implements ProvisioningManager, Closeable { default -> new IOException("Unexpected device linking response"); }; } - return handleResponseException(result).getDeviceId(); } static RegistrationApiV2.PreKeyCollection toRegistrationPreKeys(final PreKeyCollection preKeys) { diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index 643e81df..7add6211 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -8,6 +8,7 @@ import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.signal.libsignal.net.Network; import org.signal.libsignal.protocol.SignalProtocolAddress; import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations; +import org.signal.network.api.AccountApiV2; import org.signal.network.api.AttachmentApi; import org.signal.network.api.CallingApi; import org.signal.network.api.CdsApi; @@ -73,6 +74,7 @@ public class SignalDependencies { private SignalServiceAccountManager accountManager; private AccountApi accountApi; + private AccountApiV2 accountApiV2; private RateLimitChallengeApi rateLimitChallengeApi; private CdsApi cdsApi; private UsernameApi usernameApi; @@ -221,6 +223,11 @@ public class SignalDependencies { return getOrCreate(() -> accountApi, () -> accountApi = new AccountApi(getAuthenticatedSignalWebSocket())); } + public AccountApiV2 getAccountApiV2() { + return getOrCreate(() -> accountApiV2, + () -> accountApiV2 = new AccountApiV2(getAuthenticatedSignalWebSocket())); + } + public RateLimitChallengeApi getRateLimitChallengeApi() { return getOrCreate(() -> rateLimitChallengeApi, () -> rateLimitChallengeApi = new RateLimitChallengeApi(getAuthenticatedSignalWebSocket())); diff --git a/lib/src/test/java/org/asamk/signal/manager/internal/NumberlessProvisioningTest.java b/lib/src/test/java/org/asamk/signal/manager/internal/NumberlessProvisioningTest.java index 6e143cb9..1510a871 100644 --- a/lib/src/test/java/org/asamk/signal/manager/internal/NumberlessProvisioningTest.java +++ b/lib/src/test/java/org/asamk/signal/manager/internal/NumberlessProvisioningTest.java @@ -10,11 +10,11 @@ import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.KeyUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.signal.core.models.ServiceId.ACI; import org.signal.core.models.ServiceId.PNI; -import org.signal.libsignal.net.RequestResult; import org.signal.network.api.RegistrationApiV2; -import org.signal.network.api.RegistrationApiV2.RegisterAsLinkedDeviceError; import org.signal.network.rest.SignalRestClient; import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; @@ -36,7 +36,7 @@ import okio.Buffer; import okio.ByteString; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -72,15 +72,35 @@ class NumberlessProvisioningTest { @Test void numberlessLinkRequestOmitsPniAndAuthenticatesWithAci() throws Exception { - checkLinkRequest(null, null); + checkLinkRequest(null, null, 200, null); } @Test void numberedLinkRequestStillIncludesPniKeys() throws Exception { - checkLinkRequest("+12025550123", PNI_ID); + checkLinkRequest("+12025550123", PNI_ID, 200, null); } - private void checkLinkRequest(final String number, final PNI pni) throws Exception { + @ParameterizedTest + @CsvSource({ + "422, Signal rejected the device linking request", + "403, [403] Device verification failed", + "409, Linked device is missing a required account capability", + "411, Account has reached its linked device limit", + "429, Device linking rate limited; try again later" + }) + void linkingErrorsAreReportedWithoutCrashingOrIncludingServerBody( + final int statusCode, + final String errorMessage + ) throws Exception { + checkLinkRequest(null, null, statusCode, errorMessage); + } + + private void checkLinkRequest( + final String number, + final PNI pni, + final int statusCode, + final String errorMessage + ) throws Exception { final var request = new AtomicReference(); final var body = new AtomicReference(); final var mapper = new ObjectMapper(); @@ -91,9 +111,10 @@ class NumberlessProvisioningTest { body.set(mapper.readTree(buffer.readUtf8())); return new Response.Builder().request(chain.request()) .protocol(Protocol.HTTP_1_1) - .code(200) - .message("OK") - .body(ResponseBody.create("{\"deviceId\":2}", MediaType.get("application/json"))) + .code(statusCode) + .message("Test response") + .body(ResponseBody.create(statusCode == 200 ? "{\"deviceId\":2}" : "secret payload", + MediaType.get("application/json"))) .build(); }).build(); final var config = ServiceConfig.getServiceEnvironmentConfig(ServiceEnvironment.STAGING, "signal-cli-test"); @@ -125,13 +146,21 @@ class NumberlessProvisioningTest { final var pniKeys = pni == null ? null : KeyUtils.generatePreKeysForType(account.getAccountData(ServiceIdType.PNI)); - final var deviceId = ProvisioningManagerImpl.registerLinkedDevice(api, - account, - "test-code", - aciKeys, - pniKeys); - - assertEquals(2, deviceId); + if (statusCode == 200) { + assertEquals(2, + ProvisioningManagerImpl.registerLinkedDevice(api, account, "test-code", aciKeys, pniKeys)); + } else { + final var error = assertThrows(IOException.class, + () -> ProvisioningManagerImpl.registerLinkedDevice(api, + account, + "test-code", + aciKeys, + pniKeys)); + assertEquals(errorMessage, error.getMessage()); + if (statusCode == 403) { + assertInstanceOf(AuthorizationFailedException.class, error); + } + } assertEquals("PUT", request.get().method()); assertEquals("/v1/devices/link", request.get().url().encodedPath()); assertEquals(Credentials.basic(ACI_ID.toString(), "test-password"), request.get().header("Authorization")); @@ -150,16 +179,4 @@ class NumberlessProvisioningTest { client.connectionPool().evictAll(); } } - - @Test - @SuppressWarnings("unchecked") - void linkingErrorsAreReportedWithoutCrashingOrIncludingServerBody() { - final var incorrectVerification = RegisterAsLinkedDeviceError.IncorrectVerification.INSTANCE; - assertThrows(AuthorizationFailedException.class, - () -> ProvisioningManagerImpl.getLinkedDeviceId(new RequestResult.NonSuccess<>(incorrectVerification))); - final var invalidRequest = new RegisterAsLinkedDeviceError.InvalidRequest("secret payload"); - final var error = assertThrows(IOException.class, - () -> ProvisioningManagerImpl.getLinkedDeviceId(new RequestResult.NonSuccess<>(invalidRequest))); - assertFalse(error.getMessage().contains("secret payload")); - } } diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index ffe9e2a6..375bfbb2 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -251,6 +251,10 @@ If you want to connect to another signal-cli instance, you can just use this URI If you want to link to an Android/iOS device, scan the QR code that signal-cli prints in the terminal with the Signal app. If signal-cli is not running in a terminal, only the URI is printed and you can create a QR code from it yourself (e.g. with qrencode). +Accounts without a phone number can also be linked. +After linking, use the account's ACI (Account ID) with `-a`; recipient phone numbers must include the international country code. +Creating or recovering a primary account without a phone number using an Account Key is not supported. + *-n* NAME, *--name* NAME:: Optionally specify a name to describe this new device. By default "cli" will be used. diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index d9c3cf98..091816cc 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -56,7 +56,6 @@ import org.asamk.signal.manager.api.UsernameLinkUrl; import org.asamk.signal.manager.api.UsernameStatus; import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.connections.impl.DBusConnection; -import org.freedesktop.dbus.errors.UnknownMethod; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.freedesktop.dbus.interfaces.DBusInterface; @@ -815,7 +814,8 @@ public class DbusManagerImpl implements Manager { final var identifiers = addresses.stream() .map(RecipientIdentifier.Single::getIdentifier) .collect(Collectors.toSet()); - return listRecipientIdentifiers().stream() + return signal.listRecipientIdentifiers() + .stream() .filter(n -> addresses.isEmpty() || identifiers.contains(n)) .map(n -> { final var contactBlocked = signal.isContactBlocked(n); @@ -1090,15 +1090,6 @@ public class DbusManagerImpl implements Manager { } } - private List listRecipientIdentifiers() { - try { - return signal.listRecipientIdentifiers(); - } catch (UnknownMethod e) { - // Older daemons only expose recipients with a known phone number. - return signal.listNumbers(); - } - } - private T getRemoteObject(final DBusPath path, final Class type) { try { return connection.getRemoteObject(busname, path.getPath(), type); diff --git a/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java index a0083270..da88343b 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusMultiAccountManagerImpl.java @@ -43,11 +43,6 @@ public class DbusMultiAccountManagerImpl implements MultiAccountManager { this.busname = busname; } - @Override - public List getAccountNumbers() { - return getManagers().stream().map(Manager::getSelfNumber).toList(); - } - @Override public List getManagers() { return signalControl.listAccounts() diff --git a/src/test/java/org/asamk/signal/dbus/NumberlessDbusTest.java b/src/test/java/org/asamk/signal/dbus/NumberlessDbusTest.java index c805a5b7..cf6520f5 100644 --- a/src/test/java/org/asamk/signal/dbus/NumberlessDbusTest.java +++ b/src/test/java/org/asamk/signal/dbus/NumberlessDbusTest.java @@ -24,7 +24,6 @@ import org.asamk.signal.manager.internal.MultiAccountManagerImpl; import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder; -import org.freedesktop.dbus.errors.UnknownMethod; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -94,25 +93,6 @@ class NumberlessDbusTest { : new RecipientIdentifier.Number(number)); } - @Test - void readsContactsFromOlderDaemonsWithoutTheNewRecipientListingMethod() { - final var account = new TestAccount(NUMBER, ACI); - account.recipients = List.of(Recipient.newBuilder().withAddress(new RecipientAddress(NUMBER)).build()); - final var signal = new DbusSignalImpl(account.manager, null, DbusConfig.getObjectPath(), true) { - @Override - public List listRecipientIdentifiers() { - throw new UnknownMethod("Not implemented by this daemon"); - } - }; - final var client = new DbusManagerImpl(signal, null, "org.asamk.Signal.Test"); - assertEquals(NUMBER, - client.getRecipients(false, Optional.empty(), Set.of(), Optional.empty()) - .getFirst() - .getAddress() - .number() - .orElseThrow()); - } - // Run with: dbus-run-session -- ./gradlew --no-daemon :test --tests '*NumberlessDbusTest*' --rerun-tasks @Nested @Timeout(20) @@ -148,7 +128,7 @@ class NumberlessDbusTest { final var client = new DbusMultiAccountManagerImpl(control, connection, busname); assertEquals(3, client.getManagers().size()); - assertEquals(2, client.getAccountNumbers().stream().filter(n -> n == null).count()); + assertEquals(2, client.getManagers().stream().filter(m -> m.getSelfNumber() == null).count()); assertNull(client.getManager(ACI).getSelfNumber()); assertEquals(NUMBER, client.getManager(OTHER_ACI).getSelfNumber()); diff --git a/src/test/java/org/asamk/signal/jsonrpc/SubscribeCallEventsTest.java b/src/test/java/org/asamk/signal/jsonrpc/SubscribeCallEventsTest.java index 80fc63e2..9ec1c87d 100644 --- a/src/test/java/org/asamk/signal/jsonrpc/SubscribeCallEventsTest.java +++ b/src/test/java/org/asamk/signal/jsonrpc/SubscribeCallEventsTest.java @@ -72,11 +72,6 @@ class SubscribeCallEventsTest { this.managers = new ArrayList<>(managers); } - @Override - public List getAccountNumbers() { - return managers.stream().map(Manager::getSelfNumber).toList(); - } - @Override public List getManagers() { return managers;