package combo;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.web.client.HttpClientErrorException;
import java.net.URI;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import static com.googlecode.catchexception.CatchException.caughtException;
import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.then;
import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.when;
import static combo.ComboFactory.httpCombo;
import static combo.ComboServerRule.ComboServerResponse.*;
import static combo.HttpComboTest.ConsumedFact.consumedFact;
import static combo.HttpComboTest.ConsumedFact.randomConsumedFact;
import static combo.HttpComboTest.PublishedFact.randomPublishedFact;
import static org.assertj.core.api.Assertions.assertThat;
import static uk.org.fyodor.generators.characters.CharacterSetFilter.LettersOnly;
public final class HttpComboTest {
@Rule
public final ComboServerRule comboServer = new ComboServerRule(8080);
private final Combo combo = httpCombo(URI.create("http://localhost:8080"));
@Test public void publishesFact() {
//Given
final String topicName = RDG.topicName().next();
comboServer.whenFactIsPublished(topicName).thenRespondWith(ok());
//When
combo.publishFact(topicName, PublishedFact.randomPublishedFact());
//Then
comboServer.verifyFactWasPublished(topicName);
}
@Test public void throwsExceptionWhenResponseToPublishedFactIsBadRequest() {
//Given
final String topicName = RDG.topicName().next();
comboServer.whenFactIsPublished(topicName).thenRespondWith(badRequest());
//When
when(combo).publishFact(topicName, randomPublishedFact());
//Then
then(caughtException())
.isInstanceOf(HttpClientErrorException.class)
.hasMessage("400 Bad Request");
}
@Test public void consumesFirstFactFromTopic() {
//Given
final String topicName = RDG.topicName().next();
final String subscriptionId = RDG.subscriptionId().next();
comboServer.whenTopicIsSubscribedTo(topicName)
.thenRespondWith(subscriptionWithId(subscriptionId));
final ConsumedFact consumedFact = randomConsumedFact();
comboServer.whenNextFactIsRequested(topicName, subscriptionId)
.thenRespondWith(fact(consumedFact));
//When
final List<ConsumedFact> facts = consumeFacts(topicName, ConsumedFact.class);
//Then
assertThat(facts).containsExactly(consumedFact);
}
@Test public void consumesFirstTwoFactsFromTopic() {
//Given
final String topicName = RDG.topicName().next();
final String subscriptionId = RDG.subscriptionId().next();
comboServer.whenTopicIsSubscribedTo(topicName)
.thenRespondWith(subscriptionWithId(subscriptionId));
final ConsumedFact firstFact = randomConsumedFact();
final ConsumedFact secondFact = randomConsumedFact();
comboServer.whenNextFactIsRequested(topicName, subscriptionId).thenRespondWith(
fact(firstFact),
fact(secondFact));
//When
final List<ConsumedFact> facts = consumeFacts(topicName, ConsumedFact.class);
//Then
assertThat(facts)
.containsExactly(firstFact, secondFact);
}
@Test public void consumesZeroFactsWhenThereIsNoContent() {
//Given
final String topicName = RDG.topicName().next();
final String subscriptionId = RDG.subscriptionId().next();
comboServer.whenTopicIsSubscribedTo(topicName)
.thenRespondWith(subscriptionWithId(subscriptionId));
comboServer.whenNextFactIsRequested(topicName, subscriptionId)
.thenRespondWith(noContent());
//When
final List<ConsumedFact> facts = consumeFacts(topicName, ConsumedFact.class);
//Then
assertThat(facts).isEmpty();
}
@Test public void onlyPresentFactsAreConsumed() {
//Given
final String topicName = RDG.topicName().next();
final String subscriptionId = RDG.subscriptionId().next();
comboServer.whenTopicIsSubscribedTo(topicName)
.thenRespondWith(subscriptionWithId(subscriptionId));
final ConsumedFact fact1 = randomConsumedFact();
final ConsumedFact fact2 = randomConsumedFact();
final ConsumedFact fact3 = randomConsumedFact();
final ConsumedFact fact4 = randomConsumedFact();
comboServer.whenNextFactIsRequested(topicName, subscriptionId)
.thenRespondWith(noContent(), fact(fact1), fact(fact2), noContent(), fact(fact3), noContent(), fact(fact4), noContent());
//When
final List<ConsumedFact> facts = consumeFacts(topicName, ConsumedFact.class);
//Then
assertThat(facts)
.containsExactly(fact1, fact2, fact3, fact4);
}
@Test public void consumedFactsCanBeFiltered() {
//Given
final String topicName = RDG.topicName().next();
final String subscriptionId = RDG.subscriptionId().next();
comboServer.whenTopicIsSubscribedTo(topicName)
.thenRespondWith(subscriptionWithId(subscriptionId));
final ConsumedFact fact1 = consumedFact(1L);
final ConsumedFact fact2 = consumedFact(2L);
final ConsumedFact fact3 = consumedFact(3L);
comboServer.whenNextFactIsRequested(topicName, subscriptionId)
.thenRespondWith(fact(fact1), fact(fact2), fact(fact3));
//When
final List<ConsumedFact> facts = consumeFactsAndFilter(topicName, ConsumedFact.class, fact -> fact.getSomeField() == 2L);
//Then
assertThat(facts).containsExactly(fact2);
}
@Test public void consumedFactsCanBeTransformed() {
//Given
final String topicName = RDG.topicName().next();
final String subscriptionId = RDG.subscriptionId().next();
comboServer.whenTopicIsSubscribedTo(topicName)
.thenRespondWith(subscriptionWithId(subscriptionId));
final ConsumedFact fact1 = consumedFact(1L);
final ConsumedFact fact2 = consumedFact(2L);
final ConsumedFact fact3 = consumedFact(3L);
comboServer.whenNextFactIsRequested(topicName, subscriptionId)
.thenRespondWith(fact(fact1), fact(fact2), fact(fact3));
//When
final List<Long> facts = consumeFactsAndTransform(topicName, ConsumedFact.class, fact -> fact.getSomeField());
//Then
assertThat(facts).containsExactly(1L, 2L, 3L);
}
private <T> List<T> consumeFacts(final String topicName, final Class<? extends T> classOfT) {
return consumeFactsFilterAndTransform(topicName, classOfT, f -> true, f -> f);
}
private <T> List<T> consumeFactsAndFilter(final String topicName, final Class<? extends T> classOfT, final Predicate<T> predicate) {
return consumeFactsFilterAndTransform(topicName, classOfT, predicate, f -> f);
}
private <T, R> List<R> consumeFactsAndTransform(final String topicName, final Class<? extends T> classOfT, final Function<T, R> function) {
return consumeFactsFilterAndTransform(topicName, classOfT, f -> true, function);
}
private <T, R> List<R> consumeFactsFilterAndTransform(final String topicName,
final Class<? extends T> classOfT,
final Predicate<T> predicate,
final Function<T, R> function) {
final FactCollector<R> factCollector = new FactCollector<R>();
try {
combo.facts(topicName, classOfT)
.filter(predicate)
.map(function)
.forEach(factCollector);
} catch (final HttpClientErrorException e) {
//Swallow exception, we're using it to terminate the fact request loop
}
return factCollector.facts();
}
static final class ConsumedFact {
private final long someField;
ConsumedFact(final long someField) {
this.someField = someField;
}
@SuppressWarnings("UnusedDeclaration") long getSomeField() {
return someField;
}
static ConsumedFact randomConsumedFact() {
return new ConsumedFact(RDG.longVal().next());
}
static ConsumedFact consumedFact(final long someField) {
return new ConsumedFact(someField);
}
@Override public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ConsumedFact that = (ConsumedFact) o;
return someField == that.someField;
}
@Override public int hashCode() {
return (int) (someField ^ (someField >>> 32));
}
}
static final class PublishedFact {
private final String someField;
PublishedFact(final String someField) {
this.someField = someField;
}
@SuppressWarnings("UnusedDeclaration") String getSomeField() {
return someField;
}
static PublishedFact randomPublishedFact() {
return new PublishedFact(RDG.string(30, LettersOnly).next());
}
@Override public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final PublishedFact that = (PublishedFact) o;
return !(someField != null ? !someField.equals(that.someField) : that.someField != null);
}
@Override public int hashCode() {
return someField != null ? someField.hashCode() : 0;
}
}
}