From 5ac480199a833184904241cdce01f705e74c5cd3 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 25 Mar 2020 12:52:05 +0100 Subject: [PATCH] improvements of the PersistenceTestKit doc samples * and actually run the tests --- .../main/paradox/typed/persistence-testing.md | 6 +- .../testkit/PersistenceTestKitSampleTest.java | 125 +++++++++++ .../persistence/testkit/TestKitExamples.java | 90 -------- .../persistence/testkit/TestKitExamples.scala | 200 ++++++++++-------- build.sbt | 1 + 5 files changed, 235 insertions(+), 187 deletions(-) create mode 100644 akka-docs/src/test/java/jdocs/persistence/testkit/PersistenceTestKitSampleTest.java diff --git a/akka-docs/src/main/paradox/typed/persistence-testing.md b/akka-docs/src/main/paradox/typed/persistence-testing.md index f7aa50ee5a..7d3c80e5fd 100644 --- a/akka-docs/src/main/paradox/typed/persistence-testing.md +++ b/akka-docs/src/main/paradox/typed/persistence-testing.md @@ -107,10 +107,10 @@ Java A typical scenario is to create a persistent actor, send commands to it and check that it persists events as it is expected: Scala -: @@snip [TestKitExamples.scala](/akka-docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala) { #testkit-typed-usecase } +: @@snip [TestKitExamples.scala](/akka-docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala) { #test } Java -: @@snip [TestKitExamples.java](/akka-docs/src/test/java/jdocs/persistence/testkit/TestKitExamples.java) { #testkit-typed-usecase } +: @@snip [TestKitExamples.java](/akka-docs/src/test/java/jdocs/persistence/testkit/PersistenceTestKitSampleTest.java) { #test } You can safely use persistence testkit in combination with main akka testkit. @@ -188,7 +188,7 @@ For tests that involve more than one Cluster node you have to use another journa While it's possible to use the @ref:[Persistence Plugin Proxy](../persistence-plugins.md#persistence-plugin-proxy) it's often better and more realistic to use a real database. -See [akka-samples issue #128](https://github.com/akka/akka-samples/issues/128). +The @ref:[CQRS example](../project/examples.md#cqrs) includes tests that are using Akka Persistence Cassandra. ### Plugin initialization diff --git a/akka-docs/src/test/java/jdocs/persistence/testkit/PersistenceTestKitSampleTest.java b/akka-docs/src/test/java/jdocs/persistence/testkit/PersistenceTestKitSampleTest.java new file mode 100644 index 0000000000..ec6c01994b --- /dev/null +++ b/akka-docs/src/test/java/jdocs/persistence/testkit/PersistenceTestKitSampleTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2009-2020 Lightbend Inc. + */ + +package jdocs.persistence.testkit; + +import akka.actor.testkit.typed.javadsl.TestKitJunitResource; +import akka.actor.typed.ActorRef; +import akka.actor.typed.Behavior; +import akka.actor.typed.javadsl.Behaviors; +import akka.persistence.testkit.PersistenceTestKitPlugin; +import akka.persistence.testkit.javadsl.PersistenceTestKit; +import akka.persistence.typed.PersistenceId; +import akka.persistence.typed.javadsl.CommandHandler; +import akka.persistence.typed.javadsl.EventHandler; +import akka.persistence.typed.javadsl.EventSourcedBehavior; +import akka.serialization.jackson.CborSerializable; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.typesafe.config.ConfigFactory; +import jdocs.AbstractJavaTest; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; + +// #test +public class PersistenceTestKitSampleTest extends AbstractJavaTest { + + @ClassRule + public static final TestKitJunitResource testKit = + new TestKitJunitResource( + PersistenceTestKitPlugin.getInstance() + .config() + .withFallback(ConfigFactory.defaultApplication())); + + PersistenceTestKit persistenceTestKit = PersistenceTestKit.create(testKit.system()); + + @Before + public void beforeEach() { + persistenceTestKit.clearAll(); + } + + @Test + public void test() { + PersistenceId persistenceId = PersistenceId.ofUniqueId("some-id"); + ActorRef ref = + testKit.spawn(YourPersistentBehavior.create(persistenceId)); + + YourPersistentBehavior.Cmd cmd = new YourPersistentBehavior.Cmd("data"); + ref.tell(cmd); + YourPersistentBehavior.Evt expectedEventPersisted = new YourPersistentBehavior.Evt(cmd.data); + + persistenceTestKit.expectNextPersisted(persistenceId.id(), expectedEventPersisted); + } +} + +class YourPersistentBehavior + extends EventSourcedBehavior< + YourPersistentBehavior.Cmd, YourPersistentBehavior.Evt, YourPersistentBehavior.State> { + + static final class Cmd implements CborSerializable { + + public final String data; + + @JsonCreator + public Cmd(String data) { + this.data = data; + } + } + + static final class Evt implements CborSerializable { + + public final String data; + + @JsonCreator + public Evt(String data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Evt evt = (Evt) o; + + return data.equals(evt.data); + } + + @Override + public int hashCode() { + return data.hashCode(); + } + } + + static final class State implements CborSerializable {} + + static Behavior create(PersistenceId persistenceId) { + return Behaviors.setup(context -> new YourPersistentBehavior(persistenceId)); + } + + private YourPersistentBehavior(PersistenceId persistenceId) { + super(persistenceId); + } + + @Override + public State emptyState() { + // some state + return new State(); + } + + @Override + public CommandHandler commandHandler() { + return newCommandHandlerBuilder() + .forAnyState() + .onCommand(Cmd.class, command -> Effect().persist(new Evt(command.data))) + .build(); + } + + @Override + public EventHandler eventHandler() { + // TODO handle events + return newEventHandlerBuilder().forAnyState().onEvent(Evt.class, (state, evt) -> state).build(); + } +} +// #test diff --git a/akka-docs/src/test/java/jdocs/persistence/testkit/TestKitExamples.java b/akka-docs/src/test/java/jdocs/persistence/testkit/TestKitExamples.java index 8a78ba0968..dcc6c19b9e 100644 --- a/akka-docs/src/test/java/jdocs/persistence/testkit/TestKitExamples.java +++ b/akka-docs/src/test/java/jdocs/persistence/testkit/TestKitExamples.java @@ -4,13 +4,10 @@ package jdocs.persistence.testkit; -import akka.actor.testkit.typed.javadsl.TestKitJunitResource; -import akka.actor.typed.ActorRef; import akka.persistence.testkit.DeleteEvents; import akka.persistence.testkit.DeleteSnapshotByMeta; import akka.persistence.testkit.DeleteSnapshotsByCriteria; import akka.persistence.testkit.JournalOperation; -import akka.persistence.testkit.PersistenceTestKitPlugin; import akka.persistence.testkit.ProcessingPolicy; import akka.persistence.testkit.ProcessingResult; import akka.persistence.testkit.ProcessingSuccess; @@ -22,15 +19,6 @@ import akka.persistence.testkit.SnapshotOperation; import akka.persistence.testkit.StorageFailure; import akka.persistence.testkit.WriteEvents; import akka.persistence.testkit.WriteSnapshot; -import akka.persistence.testkit.javadsl.PersistenceTestKit; -import akka.persistence.typed.PersistenceId; -import akka.persistence.typed.javadsl.CommandHandler; -import akka.persistence.typed.javadsl.EventHandler; -import akka.persistence.typed.javadsl.EventSourcedBehavior; -import com.typesafe.config.ConfigFactory; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; public class TestKitExamples { @@ -105,81 +93,3 @@ public class TestKitExamples { // #set-snapshot-storage-policy } - -// #testkit-typed-usecase -class SampleTest { - - @ClassRule - public static final TestKitJunitResource testKit = - new TestKitJunitResource( - PersistenceTestKitPlugin.getInstance() - .config() - .withFallback(ConfigFactory.defaultApplication())); - - PersistenceTestKit persistenceTestKit = PersistenceTestKit.create(testKit.system()); - - @Before - void beforeAll() { - persistenceTestKit.clearAll(); - } - - @Test - void test() { - ActorRef ref = - testKit.spawn(new YourPersistentBehavior(PersistenceId.ofUniqueId("some-id"))); - - Cmd cmd = new Cmd("data"); - ref.tell(cmd); - Evt expectedEventPersisted = new Evt(cmd.data); - - persistenceTestKit.expectNextPersisted("your-persistence-id", expectedEventPersisted); - } -} - -final class Cmd { - - public final String data; - - public Cmd(String data) { - this.data = data; - } -} - -final class Evt { - - public final String data; - - public Evt(String data) { - this.data = data; - } -} - -final class State {} - -class YourPersistentBehavior extends EventSourcedBehavior { - - public YourPersistentBehavior(PersistenceId persistenceId) { - super(persistenceId); - } - - @Override - public State emptyState() { - // some state - return new State(); - } - - @Override - public CommandHandler commandHandler() { - return newCommandHandlerBuilder() - .forAnyState() - .onCommand(Cmd.class, command -> Effect().persist(new Evt(command.data))) - .build(); - } - - @Override - public EventHandler eventHandler() { - // TODO handle events - return newEventHandlerBuilder().build(); - } -} -// #testkit-typed-usecase diff --git a/akka-docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala b/akka-docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala index 8d443e757e..1f58594e17 100644 --- a/akka-docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala +++ b/akka-docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala @@ -4,104 +4,116 @@ package docs.persistence.testkit -import akka.actor.typed.ActorSystem -import akka.persistence.testkit._ -import akka.persistence.testkit.scaladsl.PersistenceTestKit -import akka.persistence.typed.scaladsl.{ Effect, EventSourcedBehavior } +import akka.persistence.typed.PersistenceId +import akka.persistence.typed.scaladsl.Effect +import akka.persistence.typed.scaladsl.EventSourcedBehavior +import akka.serialization.jackson.CborSerializable import com.typesafe.config.ConfigFactory -import org.scalatest.BeforeAndAfterAll +import docs.persistence.testkit.PersistenceTestKitSampleSpec._ +import org.scalatest.BeforeAndAfterEach import org.scalatest.wordspec.AnyWordSpecLike -class TestKitExamples { - - //#testkit-typed-usecase - class TypedSampleSpec extends AnyWordSpecLike with BeforeAndAfterAll { - - val system: ActorSystem[Cmd] = ActorSystem( - EventSourcedBehavior[Cmd, Evt, State]( - persistenceId = ???, - eventHandler = ???, - commandHandler = (_, cmd) => Effect.persist(Evt(cmd.data)), - emptyState = ???), - "name", - PersistenceTestKitPlugin.config.withFallback(ConfigFactory.defaultApplication())) - val persistenceTestKit = PersistenceTestKit(system) - - override def beforeAll(): Unit = - persistenceTestKit.clearAll() - - "Persistent actor" should { - - "persist all events" in { - - val persistentActor = system - val cmd = Cmd("data") - - persistentActor ! cmd - - val expectedPersistedEvent = Evt(cmd.data) - persistenceTestKit.expectNextPersisted("your-persistence-id", expectedPersistedEvent) - } - - } +object PersistenceTestKitSampleSpec { + final case class Cmd(data: String) extends CborSerializable + final case class Evt(data: String) extends CborSerializable + object State { + val empty: State = new State } - //#testkit-typed-usecase - - //#set-event-storage-policy - class SampleEventStoragePolicy extends EventStorage.JournalPolicies.PolicyType { - - //you can use internal state, it does not need to be thread safe - var count = 1 - - override def tryProcess(persistenceId: String, processingUnit: JournalOperation): ProcessingResult = - if (count < 10) { - count += 1 - //check the type of operation and react with success or with reject or with failure. - //if you return ProcessingSuccess the operation will be performed, otherwise not. - processingUnit match { - case ReadEvents(batch) if batch.nonEmpty => ProcessingSuccess - case WriteEvents(batch) if batch.size > 1 => - ProcessingSuccess - case ReadSeqNum => StorageFailure() - case DeleteEvents(_) => Reject() - case _ => StorageFailure() - } - } else { - ProcessingSuccess - } - + final class State extends CborSerializable { + def updated(event: Evt): State = this } - //#set-event-storage-policy - - //#set-snapshot-storage-policy - class SampleSnapshotStoragePolicy extends SnapshotStorage.SnapshotPolicies.PolicyType { - - //you can use internal state, it does not need to be thread safe - var count = 1 - - override def tryProcess(persistenceId: String, processingUnit: SnapshotOperation): ProcessingResult = - if (count < 10) { - count += 1 - //check the type of operation and react with success or with reject or with failure. - //if you return ProcessingSuccess the operation will be performed, otherwise not. - processingUnit match { - case ReadSnapshot(_, payload) if payload.nonEmpty => - ProcessingSuccess - case WriteSnapshot(meta, payload) if meta.sequenceNr > 10 => - ProcessingSuccess - case DeleteSnapshotsByCriteria(_) => StorageFailure() - case DeleteSnapshotByMeta(meta) if meta.sequenceNr < 10 => - ProcessingSuccess - case _ => StorageFailure() - } - } else { - ProcessingSuccess - } - } - //#set-snapshot-storage-policy - } -case class Cmd(data: String) -case class Evt(data: String) -trait State +//#test +import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.persistence.testkit.PersistenceTestKitPlugin +import akka.persistence.testkit.scaladsl.PersistenceTestKit + +class PersistenceTestKitSampleSpec + extends ScalaTestWithActorTestKit(PersistenceTestKitPlugin.config.withFallback(ConfigFactory.defaultApplication())) + with AnyWordSpecLike + with BeforeAndAfterEach { + + val persistenceTestKit = PersistenceTestKit(system) + + override def beforeEach(): Unit = { + persistenceTestKit.clearAll() + } + + "Persistent actor" should { + + "persist all events" in { + + val persistenceId = PersistenceId.ofUniqueId("your-persistence-id") + val persistentActor = spawn( + EventSourcedBehavior[Cmd, Evt, State]( + persistenceId, + emptyState = State.empty, + commandHandler = (_, cmd) => Effect.persist(Evt(cmd.data)), + eventHandler = (state, evt) => state.updated(evt))) + val cmd = Cmd("data") + + persistentActor ! cmd + + val expectedPersistedEvent = Evt(cmd.data) + persistenceTestKit.expectNextPersisted(persistenceId.id, expectedPersistedEvent) + } + + } +} +//#test + +//#set-event-storage-policy +import akka.persistence.testkit._ + +class SampleEventStoragePolicy extends EventStorage.JournalPolicies.PolicyType { + + //you can use internal state, it does not need to be thread safe + var count = 1 + + override def tryProcess(persistenceId: String, processingUnit: JournalOperation): ProcessingResult = + if (count < 10) { + count += 1 + //check the type of operation and react with success or with reject or with failure. + //if you return ProcessingSuccess the operation will be performed, otherwise not. + processingUnit match { + case ReadEvents(batch) if batch.nonEmpty => ProcessingSuccess + case WriteEvents(batch) if batch.size > 1 => + ProcessingSuccess + case ReadSeqNum => StorageFailure() + case DeleteEvents(_) => Reject() + case _ => StorageFailure() + } + } else { + ProcessingSuccess + } + +} +//#set-event-storage-policy + +//#set-snapshot-storage-policy +class SampleSnapshotStoragePolicy extends SnapshotStorage.SnapshotPolicies.PolicyType { + + //you can use internal state, it does not need to be thread safe + var count = 1 + + override def tryProcess(persistenceId: String, processingUnit: SnapshotOperation): ProcessingResult = + if (count < 10) { + count += 1 + //check the type of operation and react with success or with reject or with failure. + //if you return ProcessingSuccess the operation will be performed, otherwise not. + processingUnit match { + case ReadSnapshot(_, payload) if payload.nonEmpty => + ProcessingSuccess + case WriteSnapshot(meta, payload) if meta.sequenceNr > 10 => + ProcessingSuccess + case DeleteSnapshotsByCriteria(_) => StorageFailure() + case DeleteSnapshotByMeta(meta) if meta.sequenceNr < 10 => + ProcessingSuccess + case _ => StorageFailure() + } + } else { + ProcessingSuccess + } +} +//#set-snapshot-storage-policy diff --git a/build.sbt b/build.sbt index 4e3d7be495..e2733c5b2d 100644 --- a/build.sbt +++ b/build.sbt @@ -201,6 +201,7 @@ lazy val docs = akkaModule("akka-docs") .settings(Dependencies.docs) .settings(Paradox.settings) .settings(ParadoxSupport.paradoxWithCustomDirectives) + .settings(javacOptions += "-parameters") // for Jackson .enablePlugins( AkkaParadoxPlugin, DeployRsync,