mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 23:38:03 +01:00
Tune the "finish push notification experiment" command
This commit is contained in:
@@ -206,7 +206,7 @@ class NotifyIdleDevicesWithoutMessagesPushNotificationExperimentTest {
|
||||
|
||||
final DeviceLastSeenState state = new DeviceLastSeenState(true, 0, true, 0, tokenType);
|
||||
final PushNotificationExperimentSample<DeviceLastSeenState> sample =
|
||||
new PushNotificationExperimentSample<>(inExperimentGroup, state, state);
|
||||
new PushNotificationExperimentSample<>(UUID.randomUUID(), Device.PRIMARY_ID, inExperimentGroup, state, state);
|
||||
|
||||
assertEquals(expectedPopulation, NotifyIdleDevicesWithoutMessagesPushNotificationExperiment.getPopulation(sample));
|
||||
}
|
||||
@@ -234,7 +234,7 @@ class NotifyIdleDevicesWithoutMessagesPushNotificationExperimentTest {
|
||||
final NotifyIdleDevicesWithoutMessagesPushNotificationExperiment.Outcome expectedOutcome) {
|
||||
|
||||
final PushNotificationExperimentSample<DeviceLastSeenState> sample =
|
||||
new PushNotificationExperimentSample<>(true, initialState, finalState);
|
||||
new PushNotificationExperimentSample<>(UUID.randomUUID(), Device.PRIMARY_ID, true, initialState, finalState);
|
||||
|
||||
assertEquals(expectedOutcome, NotifyIdleDevicesWithoutMessagesPushNotificationExperiment.getOutcome(sample));
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.time.Clock;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -19,7 +21,7 @@ import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamoDbExtension;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
import reactor.util.function.Tuples;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
|
||||
import software.amazon.awssdk.services.dynamodb.model.GetItemResponse;
|
||||
@@ -63,7 +65,7 @@ class PushNotificationExperimentSamplesTest {
|
||||
.join(),
|
||||
"Attempt to record an initial state should succeed for entirely new records");
|
||||
|
||||
assertEquals(new PushNotificationExperimentSample<>(inExperimentGroup, new TestDeviceState(bounciness), null),
|
||||
assertEquals(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, inExperimentGroup, new TestDeviceState(bounciness), null),
|
||||
getSample(accountIdentifier, deviceId, experimentName, TestDeviceState.class));
|
||||
|
||||
assertTrue(pushNotificationExperimentSamples.recordInitialState(accountIdentifier,
|
||||
@@ -74,7 +76,7 @@ class PushNotificationExperimentSamplesTest {
|
||||
.join(),
|
||||
"Attempt to re-record an initial state should succeed for existing-but-unchanged records");
|
||||
|
||||
assertEquals(new PushNotificationExperimentSample<>(inExperimentGroup, new TestDeviceState(bounciness), null),
|
||||
assertEquals(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, inExperimentGroup, new TestDeviceState(bounciness), null),
|
||||
getSample(accountIdentifier, deviceId, experimentName, TestDeviceState.class),
|
||||
"Recorded initial state should be unchanged after repeated write");
|
||||
|
||||
@@ -86,7 +88,7 @@ class PushNotificationExperimentSamplesTest {
|
||||
.join(),
|
||||
"Attempt to record a conflicting initial state should fail");
|
||||
|
||||
assertEquals(new PushNotificationExperimentSample<>(inExperimentGroup, new TestDeviceState(bounciness), null),
|
||||
assertEquals(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, inExperimentGroup, new TestDeviceState(bounciness), null),
|
||||
getSample(accountIdentifier, deviceId, experimentName, TestDeviceState.class),
|
||||
"Recorded initial state should be unchanged after unsuccessful write");
|
||||
|
||||
@@ -98,7 +100,7 @@ class PushNotificationExperimentSamplesTest {
|
||||
.join(),
|
||||
"Attempt to record a new group assignment should fail");
|
||||
|
||||
assertEquals(new PushNotificationExperimentSample<>(inExperimentGroup, new TestDeviceState(bounciness), null),
|
||||
assertEquals(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, inExperimentGroup, new TestDeviceState(bounciness), null),
|
||||
getSample(accountIdentifier, deviceId, experimentName, TestDeviceState.class),
|
||||
"Recorded initial state should be unchanged after unsuccessful write");
|
||||
|
||||
@@ -118,7 +120,7 @@ class PushNotificationExperimentSamplesTest {
|
||||
.join(),
|
||||
"Attempt to record an initial state should fail for samples with final states");
|
||||
|
||||
assertEquals(new PushNotificationExperimentSample<>(inExperimentGroup, new TestDeviceState(bounciness), new TestDeviceState(finalBounciness)),
|
||||
assertEquals(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, inExperimentGroup, new TestDeviceState(bounciness), new TestDeviceState(finalBounciness)),
|
||||
getSample(accountIdentifier, deviceId, experimentName, TestDeviceState.class),
|
||||
"Recorded initial state should be unchanged after unsuccessful write");
|
||||
}
|
||||
@@ -148,7 +150,7 @@ class PushNotificationExperimentSamplesTest {
|
||||
.join();
|
||||
|
||||
final PushNotificationExperimentSample<TestDeviceState> expectedSample =
|
||||
new PushNotificationExperimentSample<>(inExperimentGroup,
|
||||
new PushNotificationExperimentSample<>(accountIdentifier, deviceId, inExperimentGroup,
|
||||
new TestDeviceState(initialBounciness),
|
||||
new TestDeviceState(finalBounciness));
|
||||
|
||||
@@ -190,92 +192,65 @@ class PushNotificationExperimentSamplesTest {
|
||||
? SystemMapper.jsonMapper().readValue(response.item().get(PushNotificationExperimentSamples.ATTR_FINAL_STATE).s(), stateClass)
|
||||
: null;
|
||||
|
||||
return new PushNotificationExperimentSample<>(inExperimentGroup, initialState, finalState);
|
||||
return new PushNotificationExperimentSample<>(accountIdentifier, deviceId, inExperimentGroup, initialState, finalState);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDevicesPendingFinalState() throws JsonProcessingException {
|
||||
void getSamples() throws JsonProcessingException {
|
||||
final String experimentName = "test-experiment";
|
||||
final UUID accountIdentifier = UUID.randomUUID();
|
||||
final byte deviceId = (byte) ThreadLocalRandom.current().nextInt(Device.MAXIMUM_DEVICE_ID);
|
||||
final boolean inExperimentGroup = ThreadLocalRandom.current().nextBoolean();
|
||||
final int initialBounciness = ThreadLocalRandom.current().nextInt();
|
||||
final UUID initialSampleAccountIdentifier = UUID.randomUUID();
|
||||
final byte initialSampleDeviceId = (byte) ThreadLocalRandom.current().nextInt(Device.MAXIMUM_DEVICE_ID);
|
||||
final boolean initialSampleInExperimentGroup = ThreadLocalRandom.current().nextBoolean();
|
||||
|
||||
//noinspection DataFlowIssue
|
||||
assertTrue(pushNotificationExperimentSamples.getDevicesPendingFinalState(experimentName).collectList().block().isEmpty());
|
||||
final UUID finalSampleAccountIdentifier = UUID.randomUUID();
|
||||
final byte finalSampleDeviceId = (byte) ThreadLocalRandom.current().nextInt(Device.MAXIMUM_DEVICE_ID);
|
||||
final boolean finalSampleInExperimentGroup = ThreadLocalRandom.current().nextBoolean();
|
||||
|
||||
pushNotificationExperimentSamples.recordInitialState(accountIdentifier,
|
||||
deviceId,
|
||||
experimentName,
|
||||
inExperimentGroup,
|
||||
new TestDeviceState(initialBounciness))
|
||||
.join();
|
||||
|
||||
pushNotificationExperimentSamples.recordInitialState(accountIdentifier,
|
||||
(byte) (deviceId + 1),
|
||||
experimentName + "-different",
|
||||
inExperimentGroup,
|
||||
new TestDeviceState(initialBounciness))
|
||||
.join();
|
||||
|
||||
assertEquals(List.of(Tuples.of(accountIdentifier, deviceId)),
|
||||
pushNotificationExperimentSamples.getDevicesPendingFinalState(experimentName).collectList().block());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getFinishedSamples() throws JsonProcessingException {
|
||||
final String experimentName = "test-experiment";
|
||||
final UUID accountIdentifier = UUID.randomUUID();
|
||||
final byte deviceId = (byte) ThreadLocalRandom.current().nextInt(Device.MAXIMUM_DEVICE_ID);
|
||||
final boolean inExperimentGroup = ThreadLocalRandom.current().nextBoolean();
|
||||
final int initialBounciness = ThreadLocalRandom.current().nextInt();
|
||||
final int finalBounciness = initialBounciness + 17;
|
||||
|
||||
//noinspection DataFlowIssue
|
||||
assertTrue(pushNotificationExperimentSamples.getFinishedSamples(experimentName, TestDeviceState.class).collectList().block().isEmpty());
|
||||
|
||||
pushNotificationExperimentSamples.recordInitialState(accountIdentifier,
|
||||
deviceId,
|
||||
pushNotificationExperimentSamples.recordInitialState(initialSampleAccountIdentifier,
|
||||
initialSampleDeviceId,
|
||||
experimentName,
|
||||
inExperimentGroup,
|
||||
initialSampleInExperimentGroup,
|
||||
new TestDeviceState(initialBounciness))
|
||||
.join();
|
||||
|
||||
//noinspection DataFlowIssue
|
||||
assertTrue(pushNotificationExperimentSamples.getFinishedSamples(experimentName, TestDeviceState.class).collectList().block().isEmpty(),
|
||||
"Publisher should not return unfinished samples");
|
||||
|
||||
pushNotificationExperimentSamples.recordFinalState(accountIdentifier,
|
||||
deviceId,
|
||||
experimentName,
|
||||
new TestDeviceState(finalBounciness))
|
||||
.join();
|
||||
|
||||
final List<PushNotificationExperimentSample<TestDeviceState>> expectedSamples =
|
||||
List.of(new PushNotificationExperimentSample<>(inExperimentGroup, new TestDeviceState(initialBounciness), new TestDeviceState(finalBounciness)));
|
||||
|
||||
assertEquals(
|
||||
expectedSamples,
|
||||
pushNotificationExperimentSamples.getFinishedSamples(experimentName, TestDeviceState.class).collectList().block(),
|
||||
"Publisher should return finished samples");
|
||||
|
||||
pushNotificationExperimentSamples.recordInitialState(accountIdentifier,
|
||||
deviceId,
|
||||
experimentName + "-different",
|
||||
inExperimentGroup,
|
||||
pushNotificationExperimentSamples.recordInitialState(finalSampleAccountIdentifier,
|
||||
finalSampleDeviceId,
|
||||
experimentName,
|
||||
finalSampleInExperimentGroup,
|
||||
new TestDeviceState(initialBounciness))
|
||||
.join();
|
||||
|
||||
pushNotificationExperimentSamples.recordFinalState(accountIdentifier,
|
||||
deviceId,
|
||||
experimentName + "-different",
|
||||
pushNotificationExperimentSamples.recordFinalState(finalSampleAccountIdentifier,
|
||||
finalSampleDeviceId,
|
||||
experimentName,
|
||||
new TestDeviceState(finalBounciness))
|
||||
.join();
|
||||
|
||||
assertEquals(
|
||||
expectedSamples,
|
||||
pushNotificationExperimentSamples.getFinishedSamples(experimentName, TestDeviceState.class).collectList().block(),
|
||||
"Publisher should return finished samples only from named experiment");
|
||||
pushNotificationExperimentSamples.recordInitialState(UUID.randomUUID(),
|
||||
(byte) ThreadLocalRandom.current().nextInt(Device.MAXIMUM_DEVICE_ID),
|
||||
experimentName + "-different",
|
||||
ThreadLocalRandom.current().nextBoolean(),
|
||||
new TestDeviceState(ThreadLocalRandom.current().nextInt()))
|
||||
.join();
|
||||
|
||||
final Set<PushNotificationExperimentSample<TestDeviceState>> expectedSamples = Set.of(
|
||||
new PushNotificationExperimentSample<>(initialSampleAccountIdentifier,
|
||||
initialSampleDeviceId,
|
||||
initialSampleInExperimentGroup,
|
||||
new TestDeviceState(initialBounciness),
|
||||
null),
|
||||
|
||||
new PushNotificationExperimentSample<>(finalSampleAccountIdentifier,
|
||||
finalSampleDeviceId,
|
||||
finalSampleInExperimentGroup,
|
||||
new TestDeviceState(initialBounciness),
|
||||
new TestDeviceState(finalBounciness)));
|
||||
|
||||
assertEquals(expectedSamples,
|
||||
pushNotificationExperimentSamples.getSamples(experimentName, TestDeviceState.class, 1, Schedulers.immediate()).collect(Collectors.toSet()).block());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
package org.whispersystems.textsecuregcm.workers;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyByte;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.WhisperServerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.experiment.PushNotificationExperiment;
|
||||
import org.whispersystems.textsecuregcm.experiment.PushNotificationExperimentSample;
|
||||
import org.whispersystems.textsecuregcm.experiment.PushNotificationExperimentSamples;
|
||||
@@ -12,24 +26,8 @@ import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuples;
|
||||
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyByte;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class FinishPushNotificationExperimentCommandTest {
|
||||
|
||||
private CommandDependencies commandDependencies;
|
||||
@@ -39,6 +37,10 @@ class FinishPushNotificationExperimentCommandTest {
|
||||
|
||||
private static final String EXPERIMENT_NAME = "test";
|
||||
|
||||
private static final Namespace NAMESPACE = new Namespace(Map.of(
|
||||
FinishPushNotificationExperimentCommand.MAX_CONCURRENCY_ARGUMENT, 1,
|
||||
FinishPushNotificationExperimentCommand.SEGMENT_COUNT_ARGUMENT, 1));
|
||||
|
||||
private static class TestFinishPushNotificationExperimentCommand extends FinishPushNotificationExperimentCommand<String> {
|
||||
|
||||
public TestFinishPushNotificationExperimentCommand(final PushNotificationExperiment<String> experiment) {
|
||||
@@ -49,14 +51,20 @@ class FinishPushNotificationExperimentCommandTest {
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws JsonProcessingException {
|
||||
void setUp() {
|
||||
final AccountsManager accountsManager = mock(AccountsManager.class);
|
||||
|
||||
final PushNotificationExperimentSamples pushNotificationExperimentSamples =
|
||||
mock(PushNotificationExperimentSamples.class);
|
||||
|
||||
when(pushNotificationExperimentSamples.recordFinalState(any(), anyByte(), any(), any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(new PushNotificationExperimentSample<>(true, "test", "test")));
|
||||
.thenAnswer(invocation -> {
|
||||
final UUID accountIdentifier = invocation.getArgument(0);
|
||||
final byte deviceId = invocation.getArgument(1);
|
||||
|
||||
return CompletableFuture.completedFuture(
|
||||
new PushNotificationExperimentSample<>(accountIdentifier, deviceId, true, "test", "test"));
|
||||
});
|
||||
|
||||
commandDependencies = new CommandDependencies(accountsManager,
|
||||
null,
|
||||
@@ -78,6 +86,7 @@ class FinishPushNotificationExperimentCommandTest {
|
||||
experiment = mock(PushNotificationExperiment.class);
|
||||
when(experiment.getExperimentName()).thenReturn(EXPERIMENT_NAME);
|
||||
when(experiment.getState(any(), any())).thenReturn("test");
|
||||
when(experiment.getStateClass()).thenReturn(String.class);
|
||||
|
||||
doAnswer(invocation -> {
|
||||
final Flux<PushNotificationExperimentSample<String>> samples = invocation.getArgument(0);
|
||||
@@ -90,7 +99,7 @@ class FinishPushNotificationExperimentCommandTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void run() throws Exception {
|
||||
void run() {
|
||||
final UUID accountIdentifier = UUID.randomUUID();
|
||||
final byte deviceId = Device.PRIMARY_ID;
|
||||
|
||||
@@ -103,69 +112,54 @@ class FinishPushNotificationExperimentCommandTest {
|
||||
when(commandDependencies.accountsManager().getByAccountIdentifierAsync(accountIdentifier))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getDevicesPendingFinalState(EXPERIMENT_NAME))
|
||||
.thenReturn(Flux.just(Tuples.of(accountIdentifier, deviceId)));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null,
|
||||
new Namespace(Map.of(FinishPushNotificationExperimentCommand.MAX_CONCURRENCY_ARGUMENT, 1)),
|
||||
null,
|
||||
commandDependencies));
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getSamples(eq(EXPERIMENT_NAME), eq(String.class), anyInt(), any()))
|
||||
.thenReturn(Flux.just(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, true, "test", null)));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null, NAMESPACE, null, commandDependencies));
|
||||
verify(experiment).getState(account, device);
|
||||
|
||||
verify(commandDependencies.pushNotificationExperimentSamples())
|
||||
.recordFinalState(eq(accountIdentifier), eq(deviceId), eq(EXPERIMENT_NAME), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runMissingAccount() throws Exception {
|
||||
void runMissingAccount() {
|
||||
final UUID accountIdentifier = UUID.randomUUID();
|
||||
final byte deviceId = Device.PRIMARY_ID;
|
||||
|
||||
when(commandDependencies.accountsManager().getByAccountIdentifierAsync(accountIdentifier))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.empty()));
|
||||
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getDevicesPendingFinalState(EXPERIMENT_NAME))
|
||||
.thenReturn(Flux.just(Tuples.of(accountIdentifier, deviceId)));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null,
|
||||
new Namespace(Map.of(FinishPushNotificationExperimentCommand.MAX_CONCURRENCY_ARGUMENT, 1)),
|
||||
null,
|
||||
commandDependencies));
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getSamples(eq(EXPERIMENT_NAME), eq(String.class), anyInt(), any()))
|
||||
.thenReturn(Flux.just(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, true, "test", null)));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null, NAMESPACE, null, commandDependencies));
|
||||
verify(experiment).getState(null, null);
|
||||
|
||||
verify(commandDependencies.pushNotificationExperimentSamples())
|
||||
.recordFinalState(eq(accountIdentifier), eq(deviceId), eq(EXPERIMENT_NAME), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runMissingDevice() throws Exception {
|
||||
void runMissingDevice() {
|
||||
final UUID accountIdentifier = UUID.randomUUID();
|
||||
final byte deviceId = Device.PRIMARY_ID;
|
||||
|
||||
final Account account = mock(Account.class);
|
||||
when(account.getDevice(anyByte())).thenReturn(Optional.empty());
|
||||
when(account.getDevice(deviceId)).thenReturn(Optional.empty());
|
||||
|
||||
when(commandDependencies.accountsManager().getByAccountIdentifierAsync(accountIdentifier))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getDevicesPendingFinalState(EXPERIMENT_NAME))
|
||||
.thenReturn(Flux.just(Tuples.of(accountIdentifier, deviceId)));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null,
|
||||
new Namespace(Map.of(FinishPushNotificationExperimentCommand.MAX_CONCURRENCY_ARGUMENT, 1)),
|
||||
null,
|
||||
commandDependencies));
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getSamples(eq(EXPERIMENT_NAME), eq(String.class), anyInt(), any()))
|
||||
.thenReturn(Flux.just(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, true, "test", null)));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null, NAMESPACE, null, commandDependencies));
|
||||
verify(experiment).getState(account, null);
|
||||
|
||||
verify(commandDependencies.pushNotificationExperimentSamples())
|
||||
.recordFinalState(eq(accountIdentifier), eq(deviceId), eq(EXPERIMENT_NAME), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runAccountFetchRetry() throws Exception {
|
||||
void runAccountFetchRetry() {
|
||||
final UUID accountIdentifier = UUID.randomUUID();
|
||||
final byte deviceId = Device.PRIMARY_ID;
|
||||
|
||||
@@ -180,24 +174,19 @@ class FinishPushNotificationExperimentCommandTest {
|
||||
.thenReturn(CompletableFuture.failedFuture(new RuntimeException()))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getDevicesPendingFinalState(EXPERIMENT_NAME))
|
||||
.thenReturn(Flux.just(Tuples.of(accountIdentifier, deviceId)));
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getSamples(eq(EXPERIMENT_NAME), eq(String.class), anyInt(), any()))
|
||||
.thenReturn(Flux.just(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, true, "test", null)));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null,
|
||||
new Namespace(Map.of(FinishPushNotificationExperimentCommand.MAX_CONCURRENCY_ARGUMENT, 1)),
|
||||
null,
|
||||
commandDependencies));
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null, NAMESPACE, null, commandDependencies));
|
||||
verify(experiment).getState(account, device);
|
||||
verify(commandDependencies.pushNotificationExperimentSamples())
|
||||
.recordFinalState(eq(accountIdentifier), eq(deviceId), eq(EXPERIMENT_NAME), any());
|
||||
|
||||
verify(commandDependencies.accountsManager(), times(3)).getByAccountIdentifierAsync(accountIdentifier);
|
||||
|
||||
verify(experiment).getState(account, device);
|
||||
|
||||
verify(commandDependencies.pushNotificationExperimentSamples())
|
||||
.recordFinalState(eq(accountIdentifier), eq(deviceId), eq(EXPERIMENT_NAME), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runStoreSampleRetry() throws Exception {
|
||||
void runStoreSampleRetry() {
|
||||
final UUID accountIdentifier = UUID.randomUUID();
|
||||
final byte deviceId = Device.PRIMARY_ID;
|
||||
|
||||
@@ -210,27 +199,22 @@ class FinishPushNotificationExperimentCommandTest {
|
||||
when(commandDependencies.accountsManager().getByAccountIdentifierAsync(accountIdentifier))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getDevicesPendingFinalState(EXPERIMENT_NAME))
|
||||
.thenReturn(Flux.just(Tuples.of(accountIdentifier, deviceId)));
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getSamples(eq(EXPERIMENT_NAME), eq(String.class), anyInt(), any()))
|
||||
.thenReturn(Flux.just(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, true, "test", null)));
|
||||
|
||||
when(commandDependencies.pushNotificationExperimentSamples().recordFinalState(any(), anyByte(), any(), any()))
|
||||
.thenReturn(CompletableFuture.failedFuture(new RuntimeException()))
|
||||
.thenReturn(CompletableFuture.failedFuture(new RuntimeException()))
|
||||
.thenReturn(CompletableFuture.completedFuture(new PushNotificationExperimentSample<>(true, "test", "test")));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null,
|
||||
new Namespace(Map.of(FinishPushNotificationExperimentCommand.MAX_CONCURRENCY_ARGUMENT, 1)),
|
||||
null,
|
||||
commandDependencies));
|
||||
.thenReturn(CompletableFuture.completedFuture(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, true, "test", "test")));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null, NAMESPACE, null, commandDependencies));
|
||||
verify(experiment).getState(account, device);
|
||||
|
||||
verify(commandDependencies.pushNotificationExperimentSamples(), times(3))
|
||||
.recordFinalState(eq(accountIdentifier), eq(deviceId), eq(EXPERIMENT_NAME), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runMissingInitialSample() throws Exception {
|
||||
void runMissingInitialSample() {
|
||||
final UUID accountIdentifier = UUID.randomUUID();
|
||||
final byte deviceId = Device.PRIMARY_ID;
|
||||
|
||||
@@ -243,20 +227,27 @@ class FinishPushNotificationExperimentCommandTest {
|
||||
when(commandDependencies.accountsManager().getByAccountIdentifierAsync(accountIdentifier))
|
||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getDevicesPendingFinalState(EXPERIMENT_NAME))
|
||||
.thenReturn(Flux.just(Tuples.of(accountIdentifier, deviceId)));
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getSamples(eq(EXPERIMENT_NAME), eq(String.class), anyInt(), any()))
|
||||
.thenReturn(Flux.just(new PushNotificationExperimentSample<>(accountIdentifier, deviceId, true, "test", null)));
|
||||
|
||||
when(commandDependencies.pushNotificationExperimentSamples().recordFinalState(any(), anyByte(), any(), any()))
|
||||
.thenReturn(CompletableFuture.failedFuture(ConditionalCheckFailedException.builder().build()));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null,
|
||||
new Namespace(Map.of(FinishPushNotificationExperimentCommand.MAX_CONCURRENCY_ARGUMENT, 1)),
|
||||
null,
|
||||
commandDependencies));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null, NAMESPACE, null, commandDependencies));
|
||||
verify(experiment).getState(account, device);
|
||||
|
||||
verify(commandDependencies.pushNotificationExperimentSamples())
|
||||
.recordFinalState(eq(accountIdentifier), eq(deviceId), eq(EXPERIMENT_NAME), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runFinalSampleAlreadyRecorded() {
|
||||
when(commandDependencies.pushNotificationExperimentSamples().getSamples(eq(EXPERIMENT_NAME), eq(String.class), anyInt(), any()))
|
||||
.thenReturn(Flux.just(new PushNotificationExperimentSample<>(UUID.randomUUID(), Device.PRIMARY_ID, true, "test", "test")));
|
||||
|
||||
assertDoesNotThrow(() -> finishPushNotificationExperimentCommand.run(null, NAMESPACE, null, commandDependencies));
|
||||
verify(commandDependencies.accountsManager(), never()).getByAccountIdentifier(any());
|
||||
verify(experiment, never()).getState(any(), any());
|
||||
verify(commandDependencies.pushNotificationExperimentSamples(), never())
|
||||
.recordFinalState(any(), anyByte(), any(), any());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user