@Override
  public Mono<GetPreKeysResponse> getPreKeys(final GetPreKeysRequest request) {
    final AuthenticatedDevice authenticatedDevice = AuthenticationUtil.requireAuthenticatedDevice();

    final ServiceIdentifier targetIdentifier =
        ServiceIdentifierUtil.fromGrpcServiceIdentifier(request.getTargetIdentifier());

    final byte deviceId = DeviceIdUtil.validate(request.getDeviceId());

    final String rateLimitKey = authenticatedDevice.accountIdentifier() + "." +
        authenticatedDevice.deviceId() + "__" +
        targetIdentifier.uuid() + "." +
        deviceId;

    return rateLimiters.getPreKeysLimiter().validateReactive(rateLimitKey)
        .then(Mono.fromFuture(() -> accountsManager.getByServiceIdentifierAsync(targetIdentifier))
            .flatMap(Mono::justOrEmpty))
        .switchIfEmpty(Mono.error(Status.NOT_FOUND.asException()))
        .flatMap(targetAccount ->
            KeysGrpcHelper.getPreKeys(targetAccount, targetIdentifier.identityType(), deviceId, keysManager));
  }
@Test
  void getPreKeysRateLimited() {
    final Account targetAccount = mock(Account.class);
    when(targetAccount.getUuid()).thenReturn(UUID.randomUUID());
    when(targetAccount.getIdentityKey(IdentityType.ACI)).thenReturn(new IdentityKey(Curve.generateKeyPair().getPublicKey()));
    when(targetAccount.getDevices()).thenReturn(Collections.emptyList());
    when(targetAccount.getDevice(anyByte())).thenReturn(Optional.empty());

    when(accountsManager.getByServiceIdentifierAsync(any()))
        .thenReturn(CompletableFuture.completedFuture(Optional.of(targetAccount)));

    final Duration retryAfterDuration = Duration.ofMinutes(7);
    when(preKeysRateLimiter.validateReactive(anyString()))
        .thenReturn(Mono.error(new RateLimitExceededException(retryAfterDuration)));

    assertRateLimitExceeded(retryAfterDuration, () -> authenticatedServiceStub().getPreKeys(GetPreKeysRequest.newBuilder()
        .setTargetIdentifier(ServiceIdentifier.newBuilder()
            .setIdentityType(org.signal.chat.common.IdentityType.IDENTITY_TYPE_ACI)
            .setUuid(UUIDUtil.toByteString(UUID.randomUUID()))
            .build())
        .build()));
    verifyNoInteractions(accountsManager);
  }