/*
* Copyright (c) 2011-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.ext.stomp.impl;
import com.jayway.awaitility.Awaitility;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.ext.stomp.*;
import io.vertx.ext.stomp.utils.Headers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test the {@code RECEIPT} frame.
*
* @author <a href="http://escoffier.me">Clement Escoffier</a>
*/
public class ReceiptTest {
private Vertx vertx;
private StompServer server;
private List<StompClient> clients = new ArrayList<>();
@Before
public void setUp() {
vertx = Vertx.vertx();
AsyncLock<StompServer> lock = new AsyncLock<>();
vertx = Vertx.vertx();
server = StompServer.create(vertx)
.handler(StompServerHandler.create(vertx))
.listen(lock.handler());
lock.waitForSuccess();
}
@After
public void tearDown() {
clients.forEach(StompClient::close);
clients.clear();
AsyncLock<Void> lock = new AsyncLock<>();
server.close(lock.handler());
lock.waitForSuccess();
lock = new AsyncLock<>();
vertx.close(lock.handler());
lock.waitForSuccess();
}
@Test
public void testReceiptsOnSend() {
List<Frame> frames = new CopyOnWriteArrayList<>();
List<Frame> receipts = new CopyOnWriteArrayList<>();
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.subscribe("/queue", frames::add, receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> Helper.hasDestination(server.stompHandler().getDestinations(), "/queue"));
assertThat(receipts).hasSize(1);
assertThat(receipts.get(0).toString()).contains("SUBSCRIBE", "/queue");
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.send("/queue", Buffer.buffer("Hello"), receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> !frames.isEmpty());
assertThat(receipts).hasSize(2);
}
@Test
public void testReceiptsOnSubscribeAndUnsubscribe() {
List<Frame> frames = new CopyOnWriteArrayList<>();
List<Frame> receipts = new CopyOnWriteArrayList<>();
AtomicReference<StompClientConnection> client = new AtomicReference<>();
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.subscribe("/queue", frames::add, receipts::add);
}));
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
client.set(connection);
connection.subscribe("/queue", frames::add, receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> Helper.hasDestination(server.stompHandler().getDestinations(), "/queue"));
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.send("/queue", Buffer.buffer("Hello"), receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> frames.size() >= 2);
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> receipts.size() == 3);
client.get().unsubscribe("/queue", receipts::add);
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> receipts.size() == 4);
}
@Test
public void testReceiptsWithAck() {
List<Frame> receipts = new CopyOnWriteArrayList<>();
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.subscribe("/queue", Headers.create(Frame.ACK, "client"),
frame -> connection.ack(frame.getAck()),
receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> Helper.hasDestination(server.stompHandler().getDestinations(), "/queue"));
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.send("/queue", Buffer.buffer("Hello"), receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> receipts.size() == 2);
}
@Test
public void testReceiptsWithNack() {
List<Frame> receipts = new CopyOnWriteArrayList<>();
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.subscribe("/queue", Headers.create(Frame.ACK, "client"),
frame -> connection.nack(frame.getAck()),
receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> Helper.hasDestination(server.stompHandler().getDestinations(), "/queue"));
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.send("/queue", Buffer.buffer("Hello"), receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> receipts.size() == 2);
}
@Test
public void testReceiptsInCommittedTransactions() {
List<Frame> receipts = new CopyOnWriteArrayList<>();
List<Frame> frames = new CopyOnWriteArrayList<>();
List<Frame> errors = new CopyOnWriteArrayList<>();
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.subscribe("/queue", frames::add, receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> receipts.size() == 1);
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.errorHandler(errors::add);
connection.beginTX("my-tx", receipts::add);
connection.send(new Frame().setCommand(Frame.Command.SEND).setDestination("/queue").setTransaction("my-tx")
.setBody(Buffer.buffer("Hello")), receipts::add);
connection.send(new Frame().setCommand(Frame.Command.SEND).setDestination("/queue").setTransaction("my-tx").setBody(
Buffer.buffer("World")), receipts::add);
connection.send(new Frame().setCommand(Frame.Command.SEND).setDestination("/queue").setTransaction("my-tx")
.setBody(Buffer.buffer("!!!")), receipts::add);
connection.commit("my-tx", receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> frames.size() == 3 && errors.isEmpty()
&& receipts.size() == 6);
}
@Test
public void testReceiptsInAbortedTransactions() throws InterruptedException {
List<Frame> frames = new CopyOnWriteArrayList<>();
List<Frame> errors = new CopyOnWriteArrayList<>();
List<Frame> receipts = new CopyOnWriteArrayList<>();
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.subscribe("/queue", frames::add, receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> receipts.size() == 1);
clients.add(StompClient.create(vertx).connect(ar -> {
final StompClientConnection connection = ar.result();
connection.errorHandler(errors::add);
connection.beginTX("my-tx", receipts::add);
connection.send(new Frame().setCommand(Frame.Command.SEND).setDestination("/queue").setTransaction("my-tx")
.setBody(Buffer.buffer("Hello")), receipts::add);
connection.send(new Frame().setCommand(Frame.Command.SEND).setDestination("/queue").setTransaction("my-tx").setBody(
Buffer.buffer("World")), receipts::add);
connection.send(new Frame().setCommand(Frame.Command.SEND).setDestination("/queue").setTransaction("my-tx")
.setBody(Buffer.buffer("!!!")), receipts::add);
connection.abort("my-tx", receipts::add);
}));
Awaitility.waitAtMost(10, TimeUnit.SECONDS).until(() -> frames.size() == 0 && errors.isEmpty()
&& receipts.size() == 6);
}
}