Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import org.mockito.Mockito;
import reactor.core.publisher.Mono;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -435,7 +437,60 @@ public void invokeActorMethodWithoutDataWithVoidReturnType() {
Assertions.assertNull(emptyResponse);
}

@Test()
public void invokeActorMethodWithRenamedMethodViaReflection() throws NoSuchMethodException {
final ActorClient daprClient = mock(ActorClient.class);
when(daprClient.invoke(anyString(), anyString(), Mockito.eq("actualName"), Mockito.isNull()))
.thenReturn(Mono.just("\"ok\"".getBytes()));

final ActorProxyImpl actorProxy = new ActorProxyImpl(
"myActorType",
new ActorId("100"),
new DefaultObjectSerializer(),
daprClient);

String res = (String) actorProxy.invoke(actorProxy, Actor.class.getMethod("renamedMethod"), null);
Assertions.assertEquals("ok", res);
Mockito.verify(daprClient).invoke(
Mockito.eq("myActorType"), Mockito.eq("100"), Mockito.eq("actualName"), Mockito.isNull());
}

@Test()
public void invokeActorMethodSerializationFails() throws IOException {
final ActorClient daprClient = mock(ActorClient.class);
final DaprObjectSerializer serializer = mock(DaprObjectSerializer.class);
when(serializer.serialize(Mockito.any())).thenThrow(new IOException("cannot serialize"));

final ActorProxy actorProxy = new ActorProxyImpl(
"myActorType",
new ActorId("100"),
serializer,
daprClient);

assertThrows(DaprException.class, () -> actorProxy.invokeMethod("mymethod", "hello").block());
}

@Test()
public void invokeActorMethodDeserializationFails() throws IOException {
final ActorClient daprClient = mock(ActorClient.class);
when(daprClient.invoke(anyString(), anyString(), anyString(), Mockito.isNull()))
.thenReturn(Mono.just("\"ok\"".getBytes()));
final DaprObjectSerializer serializer = mock(DaprObjectSerializer.class);
when(serializer.deserialize(Mockito.any(), Mockito.any())).thenThrow(new IOException("cannot deserialize"));

final ActorProxy actorProxy = new ActorProxyImpl(
"myActorType",
new ActorId("100"),
serializer,
daprClient);

assertThrows(DaprException.class, () -> actorProxy.invokeMethod("mymethod", String.class).block());
}

interface Actor {
@ActorMethod(name = "actualName")
String renamedMethod();

MyData getData();

String echo(String message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,29 @@
import io.dapr.actors.ActorId;
import io.dapr.actors.ActorType;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.utils.TypeRef;
import io.grpc.ManagedChannel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.time.Duration;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.jupiter.api.Assertions.assertThrows;
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;

public class ActorRuntimeTest {

Expand Down Expand Up @@ -93,6 +103,40 @@ public int count() {
}
}

private static final String REMINDABLE_ACTOR_NAME = "MyRemindableActor";

private static final AtomicReference<String> lastReminder = new AtomicReference<>();

private static final AtomicReference<String> lastTimerData = new AtomicReference<>();

public interface MyRemindableActor {
void receiveTimer(String data);
}

@ActorType(name = REMINDABLE_ACTOR_NAME)
public static class MyRemindableActorImpl extends AbstractActor
implements MyRemindableActor, Remindable<String> {

public MyRemindableActorImpl(ActorRuntimeContext runtimeContext, ActorId id) {
super(runtimeContext, id);
}

@Override
public TypeRef<String> getStateType() {
return TypeRef.STRING;
}

@Override
public Mono<Void> receiveReminder(String reminderName, String state, Duration dueTime, Duration period) {
return Mono.fromRunnable(() -> lastReminder.set(reminderName + ":" + state));
}

@Override
public void receiveTimer(String data) {
lastTimerData.set(data);
}
}

private static final ActorObjectSerializer ACTOR_STATE_SERIALIZER = new ActorObjectSerializer();

private static Constructor<ActorRuntime> constructor;
Expand Down Expand Up @@ -260,6 +304,101 @@ public void lazyDeactivate() throws Exception {
.doOnSuccess(s -> Assertions.fail()).onErrorReturn("".getBytes()).block();
}

@Test
public void invokeReminder() throws Exception {
lastReminder.set(null);
String actorId = UUID.randomUUID().toString();
this.runtime.registerActor(MyRemindableActorImpl.class);

this.runtime.invokeReminder(
REMINDABLE_ACTOR_NAME, actorId, "myReminder", createReminderParams("hello")).block();

Assertions.assertEquals("myReminder:hello", lastReminder.get());
}

@Test
public void invokeReminderUnknownActorType() throws Exception {
this.runtime.registerActor(MyRemindableActorImpl.class);

assertThrows(IllegalArgumentException.class, () -> this.runtime.invokeReminder(
"UnknownActor", UUID.randomUUID().toString(), "myReminder", createReminderParams("hello")).block());
}

@Test
public void invokeTimer() throws Exception {
lastTimerData.set(null);
String actorId = UUID.randomUUID().toString();
this.runtime.registerActor(MyRemindableActorImpl.class);

this.runtime.invokeTimer(
REMINDABLE_ACTOR_NAME, actorId, "myTimer", createTimerParams("receiveTimer", "hello")).block();

Assertions.assertEquals("hello", lastTimerData.get());
}

@Test
public void invokeTimerUnknownActorType() throws Exception {
this.runtime.registerActor(MyRemindableActorImpl.class);

assertThrows(IllegalArgumentException.class, () -> this.runtime.invokeTimer(
"UnknownActor", UUID.randomUUID().toString(), "myTimer", createTimerParams("receiveTimer", "hello")).block());
}

@Test
public void deactivateUnknownActorType() {
this.runtime.registerActor(MyActorImpl.class);

assertThrows(IllegalArgumentException.class,
() -> this.runtime.deactivate("UnknownActor", UUID.randomUUID().toString()).block());
}

@Test
public void registerActorTwiceRegistersSingleEntity() throws Exception {
this.runtime.registerActor(MyActorImpl.class);
this.runtime.registerActor(MyActorImpl.class);

Assertions.assertEquals("{\"entities\":[\"" + ACTOR_NAME + "\"]}",
new String(this.runtime.serializeConfig()));
}

@Test
public void constructorShouldOnlyBeCalledOnce() throws Exception {
Field instanceField = ActorRuntime.class.getDeclaredField("instance");
instanceField.setAccessible(true);
instanceField.set(null, this.runtime);
try {
InvocationTargetException exception = assertThrows(InvocationTargetException.class,
() -> constructor.newInstance(null, this.mockDaprClient));
Assertions.assertTrue(exception.getCause() instanceof IllegalStateException);
} finally {
instanceField.set(null, null);
}
}

@Test
public void closeShutsDownChannel() throws Exception {
ManagedChannel channel = mock(ManagedChannel.class);
when(channel.isShutdown()).thenReturn(false);

try (ActorRuntime runtimeWithChannel = constructor.newInstance(channel, this.mockDaprClient)) {
// Try-with-resources closes the runtime.
}

verify(channel, times(1)).shutdown();
}

@Test
public void closeSkipsShutdownWhenChannelAlreadyShutdown() throws Exception {
ManagedChannel channel = mock(ManagedChannel.class);
when(channel.isShutdown()).thenReturn(true);

try (ActorRuntime runtimeWithChannel = constructor.newInstance(channel, this.mockDaprClient)) {
// Try-with-resources closes the runtime.
}

verify(channel, never()).shutdown();
}

@Test
public void lazyInvoke() throws Exception {
String actorId = UUID.randomUUID().toString();
Expand All @@ -278,4 +417,18 @@ public void lazyInvoke() throws Exception {
Assertions.assertEquals(1, count);
}

private static byte[] createReminderParams(String data) throws IOException {
byte[] serializedData = new DefaultObjectSerializer().serialize(data);
ActorReminderParams params =
new ActorReminderParams(serializedData, Duration.ofSeconds(1), Duration.ofSeconds(1));
return ACTOR_STATE_SERIALIZER.serialize(params);
}

private static byte[] createTimerParams(String callback, String data) throws IOException {
byte[] serializedData = new DefaultObjectSerializer().serialize(data);
ActorTimerParams params =
new ActorTimerParams(callback, serializedData, Duration.ofSeconds(1), Duration.ofSeconds(1));
return ACTOR_STATE_SERIALIZER.serialize(params);
}

}
Loading