pekko/akka-docs/src/test/java/jdocs/stream/ReactiveStreamsDocTest.java
2021-01-08 17:55:38 +01:00

256 lines
6.9 KiB
Java

/*
* Copyright (C) 2015-2021 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.stream;
import akka.NotUsed;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.japi.function.Creator;
import akka.stream.javadsl.*;
import akka.testkit.TestProbe;
import jdocs.AbstractJavaTest;
import jdocs.stream.TwitterStreamQuickstartDocTest.Model.Author;
import jdocs.stream.TwitterStreamQuickstartDocTest.Model.Tweet;
import akka.testkit.javadsl.TestKit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
// #imports
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Processor;
// #imports
import org.reactivestreams.Subscription;
import java.lang.Exception;
import static jdocs.stream.ReactiveStreamsDocTest.Fixture.Data.authors;
import static jdocs.stream.TwitterStreamQuickstartDocTest.Model.AKKA;
public class ReactiveStreamsDocTest extends AbstractJavaTest {
static ActorSystem system;
static TestProbe storageProbe;
static TestProbe alertProbe;
@BeforeClass
public static void setup() {
system = ActorSystem.create("ReactiveStreamsDocTest");
storageProbe = new TestProbe(system);
alertProbe = new TestProbe(system);
}
@AfterClass
public static void tearDown() {
TestKit.shutdownActorSystem(system);
system = null;
storageProbe = null;
alertProbe = null;
}
static class Fixture {
// below class additionally helps with aligning code includes nicely
static class Data {
static // #authors
final Flow<Tweet, Author, NotUsed> authors =
Flow.of(Tweet.class).filter(t -> t.hashtags().contains(AKKA)).map(t -> t.author);
// #authors
}
static interface RS {
// #tweets-publisher
Publisher<Tweet> tweets();
// #tweets-publisher
// #author-storage-subscriber
Subscriber<Author> storage();
// #author-storage-subscriber
// #author-alert-subscriber
Subscriber<Author> alert();
// #author-alert-subscriber
}
}
final Fixture.RS rs =
new Fixture.RS() {
@Override
public Publisher<Tweet> tweets() {
return TwitterStreamQuickstartDocTest.Model.tweets.runWith(
Sink.asPublisher(AsPublisher.WITHOUT_FANOUT), system);
}
/**
* This is a minimal version of SubscriberProbe, which lives in akka-stream-testkit (test
* scope) and for now wanted to avoid setting up (test -> compile) dependency for Maven).
*
* <p>TODO: Once SubscriberProbe is easily used here replace this MPS with it.
*/
class MinimalProbeSubscriber<T> implements Subscriber<T> {
private final ActorRef ref;
public MinimalProbeSubscriber(ActorRef ref) {
this.ref = ref;
}
@Override
public void onSubscribe(Subscription s) {
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(T t) {
ref.tell(t, ActorRef.noSender());
}
@Override
public void onError(Throwable t) {
ref.tell(t, ActorRef.noSender());
}
@Override
public void onComplete() {
ref.tell("complete", ActorRef.noSender());
}
}
@Override
public Subscriber<Author> storage() {
return new MinimalProbeSubscriber<>(storageProbe.ref());
}
@Override
public Subscriber<Author> alert() {
return new MinimalProbeSubscriber<>(alertProbe.ref());
}
};
@Test
public void reactiveStreamsPublisherViaFlowToSubscriber() throws Exception {
new TestKit(system) {
final TestProbe probe = new TestProbe(system);
{
// #connect-all
Source.fromPublisher(rs.tweets()).via(authors).to(Sink.fromSubscriber(rs.storage()));
// #connect-all
}
};
}
@Test
public void flowAsPublisherAndSubscriber() throws Exception {
new TestKit(system) {
final TestProbe probe = new TestProbe(system);
{
// #flow-publisher-subscriber
final Processor<Tweet, Author> processor = authors.toProcessor().run(system);
rs.tweets().subscribe(processor);
processor.subscribe(rs.storage());
// #flow-publisher-subscriber
assertStorageResult();
}
};
}
@Test
public void sourceAsPublisher() throws Exception {
new TestKit(system) {
final TestProbe probe = new TestProbe(system);
{
// #source-publisher
final Publisher<Author> authorPublisher =
Source.fromPublisher(rs.tweets())
.via(authors)
.runWith(Sink.asPublisher(AsPublisher.WITHOUT_FANOUT), system);
authorPublisher.subscribe(rs.storage());
// #source-publisher
assertStorageResult();
}
};
}
@Test
public void sourceAsFanoutPublisher() throws Exception {
new TestKit(system) {
final TestProbe probe = new TestProbe(system);
{
// #source-fanoutPublisher
final Publisher<Author> authorPublisher =
Source.fromPublisher(rs.tweets())
.via(authors)
.runWith(Sink.asPublisher(AsPublisher.WITH_FANOUT), system);
authorPublisher.subscribe(rs.storage());
authorPublisher.subscribe(rs.alert());
// #source-fanoutPublisher
assertStorageResult();
}
};
}
@Test
public void sinkAsSubscriber() throws Exception {
new TestKit(system) {
final TestProbe probe = new TestProbe(system);
{
// #sink-subscriber
final Subscriber<Author> storage = rs.storage();
final Subscriber<Tweet> tweetSubscriber =
authors.to(Sink.fromSubscriber(storage)).runWith(Source.asSubscriber(), system);
rs.tweets().subscribe(tweetSubscriber);
// #sink-subscriber
assertStorageResult();
}
};
}
@Test
public void useProcessor() throws Exception {
new TestKit(system) {
{
// #use-processor
// An example Processor factory
final Creator<Processor<Integer, Integer>> factory =
new Creator<Processor<Integer, Integer>>() {
public Processor<Integer, Integer> create() {
return Flow.of(Integer.class).toProcessor().run(system);
}
};
final Flow<Integer, Integer, NotUsed> flow = Flow.fromProcessor(factory);
// #use-processor
}
};
}
void assertStorageResult() {
storageProbe.expectMsg(new Author("rolandkuhn"));
storageProbe.expectMsg(new Author("patriknw"));
storageProbe.expectMsg(new Author("bantonsson"));
storageProbe.expectMsg(new Author("drewhk"));
storageProbe.expectMsg(new Author("ktosopl"));
storageProbe.expectMsg(new Author("mmartynas"));
storageProbe.expectMsg(new Author("akkateam"));
storageProbe.expectMsg("complete");
}
}