IBM MQ test drafts
This commit is contained in:
parent
ef4562ab74
commit
ce54e336ba
@ -0,0 +1,516 @@
|
|||||||
|
package cz.moneta.test.harness.support.messaging;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionEndpoint;
|
||||||
|
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionQueue;
|
||||||
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
|
import cz.moneta.test.harness.messaging.MessageContentType;
|
||||||
|
import cz.moneta.test.harness.messaging.MqMessageFormat;
|
||||||
|
import cz.moneta.test.harness.messaging.ReceivedMessage;
|
||||||
|
import cz.moneta.test.harness.messaging.exception.MessagingTimeoutException;
|
||||||
|
import cz.moneta.test.harness.support.util.FileReader;
|
||||||
|
import cz.moneta.test.harness.support.util.Template;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fluent builder for IBM MQ requests.
|
||||||
|
* <p>
|
||||||
|
* Usage:
|
||||||
|
* <pre>{@code
|
||||||
|
* ImqRequest.toQueue(endpoint, queue)
|
||||||
|
* .asJson()
|
||||||
|
* .withPayload("{\"field\": \"value\"}")
|
||||||
|
* .send();
|
||||||
|
*
|
||||||
|
* ImqRequest.fromQueue(endpoint, queue)
|
||||||
|
* .receiveWhere(msg -> msg.extract("field").equals("value"))
|
||||||
|
* .withTimeout(10, TimeUnit.SECONDS)
|
||||||
|
* .andAssertFieldValue("result", "OK");
|
||||||
|
* }</pre>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public final class ImqRequest {
|
||||||
|
|
||||||
|
private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
private ImqRequest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start building a message to send to a queue.
|
||||||
|
*/
|
||||||
|
public static QueuePhase toQueue(ImqFirstVisionEndpoint endpoint, ImqFirstVisionQueue queue) {
|
||||||
|
return new QueueBuilder(endpoint, queue, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start building a message to receive from a queue.
|
||||||
|
*/
|
||||||
|
public static ReceivePhase fromQueue(ImqFirstVisionEndpoint endpoint, ImqFirstVisionQueue queue) {
|
||||||
|
return new QueueBuilder(endpoint, queue, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start building a message to send to a queue by physical name.
|
||||||
|
*/
|
||||||
|
public static QueuePhase toQueue(ImqFirstVisionEndpoint endpoint, String queueName) {
|
||||||
|
return new QueueBuilder(endpoint, null, queueName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start building a message to receive from a queue by physical name.
|
||||||
|
*/
|
||||||
|
public static ReceivePhase fromQueue(ImqFirstVisionEndpoint endpoint, String queueName) {
|
||||||
|
return new QueueBuilder(endpoint, null, queueName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phase after specifying queue - can send or receive.
|
||||||
|
*/
|
||||||
|
public interface QueuePhase extends PayloadPhase, ReceivePhase {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phase for building message payload.
|
||||||
|
*/
|
||||||
|
public interface PayloadPhase {
|
||||||
|
/**
|
||||||
|
* Set message format to JSON (default).
|
||||||
|
*/
|
||||||
|
PayloadPhase asJson();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set message format to XML.
|
||||||
|
*/
|
||||||
|
PayloadPhase asXml();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set message format to EBCDIC (IBM-870).
|
||||||
|
*/
|
||||||
|
PayloadPhase asEbcdic();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set message format to UTF-8 (CCSID 1208).
|
||||||
|
*/
|
||||||
|
PayloadPhase asUtf8();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set message payload as JSON string.
|
||||||
|
*/
|
||||||
|
PayloadPhase withPayload(String payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load payload from resource file.
|
||||||
|
*/
|
||||||
|
PayloadPhase withPayloadFromFile(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render payload from template.
|
||||||
|
*/
|
||||||
|
PayloadPhase withPayloadFromTemplate(Template template);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add field to JSON payload at root level.
|
||||||
|
*/
|
||||||
|
PayloadPhase addField(String fieldName, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add field to JSON payload at specified path.
|
||||||
|
*/
|
||||||
|
PayloadPhase addField(String path, String fieldName, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append value to JSON array at specified path.
|
||||||
|
*/
|
||||||
|
PayloadPhase appendToArray(String path, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the message.
|
||||||
|
*/
|
||||||
|
void send();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phase for receiving messages.
|
||||||
|
*/
|
||||||
|
public interface ReceivePhase {
|
||||||
|
/**
|
||||||
|
* Set JMS message selector.
|
||||||
|
*/
|
||||||
|
ReceivePhase withSelector(String selector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start receiving message matching predicate.
|
||||||
|
*/
|
||||||
|
AwaitingPhase receiveWhere(Predicate<ReceivedMessage> filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browse messages from queue (non-destructive).
|
||||||
|
*/
|
||||||
|
List<ReceivedMessage> browse(int maxMessages);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browse messages from queue with selector (non-destructive).
|
||||||
|
*/
|
||||||
|
List<ReceivedMessage> browse(String selector, int maxMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phase after specifying filter - await message with timeout.
|
||||||
|
*/
|
||||||
|
public interface AwaitingPhase {
|
||||||
|
/**
|
||||||
|
* Set timeout and get message response for assertions.
|
||||||
|
*/
|
||||||
|
MessageResponse withTimeout(long duration, java.util.concurrent.TimeUnit unit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set timeout duration and get message response for assertions.
|
||||||
|
*/
|
||||||
|
MessageResponse withTimeout(Duration timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response with fluent assertions.
|
||||||
|
*/
|
||||||
|
public interface MessageResponse extends cz.moneta.test.harness.support.messaging.MessageResponse {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for queue operations.
|
||||||
|
*/
|
||||||
|
private static class QueueBuilder implements QueuePhase, AwaitingPhase {
|
||||||
|
|
||||||
|
private final ImqFirstVisionEndpoint endpoint;
|
||||||
|
private final ImqFirstVisionQueue logicalQueue;
|
||||||
|
private final String physicalQueue;
|
||||||
|
private String selector;
|
||||||
|
private MqMessageFormat format = MqMessageFormat.JSON;
|
||||||
|
private String payload;
|
||||||
|
private Map<String, Object> fields = new HashMap<>();
|
||||||
|
private List<Map.Entry<String, Object>> arrayAppends = new ArrayList<>();
|
||||||
|
private Predicate<ReceivedMessage> filter;
|
||||||
|
private Duration timeout;
|
||||||
|
|
||||||
|
public QueueBuilder(ImqFirstVisionEndpoint endpoint, ImqFirstVisionQueue logicalQueue, String physicalQueue) {
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
this.logicalQueue = logicalQueue;
|
||||||
|
this.physicalQueue = physicalQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getQueueName() {
|
||||||
|
if (logicalQueue != null) {
|
||||||
|
return endpoint.resolveQueue(logicalQueue);
|
||||||
|
}
|
||||||
|
return physicalQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase asJson() {
|
||||||
|
this.format = MqMessageFormat.JSON;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase asXml() {
|
||||||
|
this.format = MqMessageFormat.XML;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase asEbcdic() {
|
||||||
|
this.format = MqMessageFormat.EBCDIC_870;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase asUtf8() {
|
||||||
|
this.format = MqMessageFormat.UTF8_1208;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase withPayload(String payload) {
|
||||||
|
this.payload = payload;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase withPayloadFromFile(String path) {
|
||||||
|
this.payload = FileReader.readFileFromResources(path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase withPayloadFromTemplate(Template template) {
|
||||||
|
this.payload = template.render();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase addField(String fieldName, Object value) {
|
||||||
|
return addField("", fieldName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase addField(String path, String fieldName, Object value) {
|
||||||
|
String key = StringUtils.isNotBlank(path) && StringUtils.isNotBlank(fieldName) ? path + "." + fieldName : fieldName;
|
||||||
|
this.fields.put(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayloadPhase appendToArray(String path, Object value) {
|
||||||
|
this.arrayAppends.add(Map.entry(path, value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send() {
|
||||||
|
String finalPayload = buildPayload();
|
||||||
|
Map<String, String> properties = new HashMap<>();
|
||||||
|
|
||||||
|
if (logicalQueue != null) {
|
||||||
|
properties.put("LogicalQueue", logicalQueue.name());
|
||||||
|
}
|
||||||
|
if (selector != null && !selector.isBlank()) {
|
||||||
|
properties.put("Selector", selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint.send(getQueueName(), finalPayload, format, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReceivePhase withSelector(String selector) {
|
||||||
|
this.selector = selector;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AwaitingPhase receiveWhere(Predicate<ReceivedMessage> filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ReceivedMessage> browse(int maxMessages) {
|
||||||
|
return browse(selector, maxMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ReceivedMessage> browse(String sel, int maxMessages) {
|
||||||
|
return endpoint.browse(getQueueName(), sel, format, maxMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageResponse withTimeout(long duration, java.util.concurrent.TimeUnit unit) {
|
||||||
|
return withTimeout(Duration.of(duration, java.time.temporal.ChronoUnit.MILLIS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageResponse withTimeout(Duration timeout) {
|
||||||
|
this.timeout = timeout;
|
||||||
|
|
||||||
|
if (filter == null) {
|
||||||
|
throw new IllegalStateException("Must specify receiveWhere filter before withTimeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
ReceivedMessage message = receiveMessage();
|
||||||
|
return new ResponseImpl(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReceivedMessage receiveMessage() {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
long timeoutMs = timeout.toMillis();
|
||||||
|
|
||||||
|
while (System.currentTimeMillis() - startTime < timeoutMs) {
|
||||||
|
try {
|
||||||
|
ReceivedMessage message = endpoint.receive(getQueueName(), selector, format, Duration.ofSeconds(1));
|
||||||
|
if (filter.test(message)) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
} catch (MessagingTimeoutException e) {
|
||||||
|
// Continue polling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MessagingTimeoutException(
|
||||||
|
"No message matching filter found on queue '" + getQueueName() +
|
||||||
|
"' within " + timeout.toMillis() + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildPayload() {
|
||||||
|
if (payload == null) {
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fields.isEmpty() && arrayAppends.isEmpty()) {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, Object> json = JSON_MAPPER.readValue(payload, Map.class);
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : fields.entrySet()) {
|
||||||
|
setField(json, entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : arrayAppends) {
|
||||||
|
appendToArray(json, entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON_MAPPER.writeValueAsString(json);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new HarnessException("Failed to build payload", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setField(Map<String, Object> json, String path, Object value) {
|
||||||
|
if (StringUtils.isBlank(path)) {
|
||||||
|
json.put(path, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = path.split("\\.");
|
||||||
|
Map<String, Object> current = json;
|
||||||
|
|
||||||
|
for (int i = 0; i < parts.length - 1; i++) {
|
||||||
|
String part = parts[i];
|
||||||
|
if (!current.containsKey(part)) {
|
||||||
|
current.put(part, new HashMap<String, Object>());
|
||||||
|
}
|
||||||
|
current = (Map<String, Object>) current.get(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
current.put(parts[parts.length - 1], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendToArray(Map<String, Object> json, String path, Object value) {
|
||||||
|
String[] parts = path.split("\\.");
|
||||||
|
Map<String, Object> current = json;
|
||||||
|
|
||||||
|
for (int i = 0; i < parts.length - 1; i++) {
|
||||||
|
String part = parts[i];
|
||||||
|
if (!current.containsKey(part)) {
|
||||||
|
current.put(part, new ArrayList<Map<String, Object>>());
|
||||||
|
}
|
||||||
|
current = (Map<String, Object>) current.get(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, Object>> array = (List<Map<String, Object>>) current.get(parts[parts.length - 1]);
|
||||||
|
if (array == null) {
|
||||||
|
array = new ArrayList<>();
|
||||||
|
current.put(parts[parts.length - 1], array);
|
||||||
|
}
|
||||||
|
array.add((Map<String, Object>) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response implementation with assertions.
|
||||||
|
*/
|
||||||
|
private static class ResponseImpl implements MessageResponse {
|
||||||
|
|
||||||
|
private final ReceivedMessage message;
|
||||||
|
|
||||||
|
public ResponseImpl(ReceivedMessage message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageResponse andAssertFieldValue(String path, String value) {
|
||||||
|
String actual = message.extract(path);
|
||||||
|
if (!Objects.equals(value, actual)) {
|
||||||
|
throw new AssertionError(String.format("Expected field '%s' to be '%s' but was '%s'", path, value, actual));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageResponse andAssertPresent(String path) {
|
||||||
|
JsonPathValue extracted = new JsonPathValue(message.extract(path));
|
||||||
|
if (extracted.asText() == null && !isPresentInJson(path)) {
|
||||||
|
throw new AssertionError(String.format("Expected field '%s' to be present", path));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageResponse andAssertNotPresent(String path) {
|
||||||
|
if (isPresentInJson(path)) {
|
||||||
|
throw new AssertionError(String.format("Expected field '%s' to be absent", path));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageResponse andAssertHeaderValue(String headerName, String value) {
|
||||||
|
String actual = message.getHeader(headerName);
|
||||||
|
if (!Objects.equals(value, actual)) {
|
||||||
|
throw new AssertionError(String.format("Expected header '%s' to be '%s' but was '%s'", headerName, value, actual));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageResponse andAssertBodyContains(String substring) {
|
||||||
|
if (!message.getBody().contains(substring)) {
|
||||||
|
throw new AssertionError(String.format("Body does not contain '%s'", substring));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonPathValue extract(String path) {
|
||||||
|
return new JsonPathValue(message.extract(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T mapTo(Class<T> type) {
|
||||||
|
return message.mapTo(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public cz.moneta.test.harness.support.messaging.ReceivedMessage getMessage() {
|
||||||
|
return cz.moneta.test.harness.support.messaging.ReceivedMessage.fromMessagingReceivedMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBody() {
|
||||||
|
return message.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String name) {
|
||||||
|
return message.getHeader(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPresentInJson(String path) {
|
||||||
|
try {
|
||||||
|
String body = message.getBody();
|
||||||
|
if (message.getContentType() != MessageContentType.JSON) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JsonNode node = JSON_MAPPER.readTree(body);
|
||||||
|
JsonNode target = extractNode(path, node);
|
||||||
|
return target != null && !target.isMissingNode();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode extractNode(String path, JsonNode node) {
|
||||||
|
return Arrays.stream(path.split("\\."))
|
||||||
|
.filter(StringUtils::isNotEmpty)
|
||||||
|
.reduce(node,
|
||||||
|
(n, p) -> n.isContainerNode() ? n.get(p) : n,
|
||||||
|
(n1, n2) -> n1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package cz.moneta.test.harness.support.messaging;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for extracted JSON path value.
|
||||||
|
*/
|
||||||
|
public class JsonPathValue {
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public JsonPathValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value as String.
|
||||||
|
*/
|
||||||
|
public String asText() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value as Boolean.
|
||||||
|
*/
|
||||||
|
public Boolean asBoolean() {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Boolean.parseBoolean(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value as Integer.
|
||||||
|
*/
|
||||||
|
public Integer asInteger() {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Cannot parse '" + value + "' as Integer", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value as Long.
|
||||||
|
*/
|
||||||
|
public Long asLong() {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Long.parseLong(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Cannot parse '" + value + "' as Long", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the underlying value.
|
||||||
|
*/
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package cz.moneta.test.harness.support.messaging;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content type of a received message.
|
||||||
|
*/
|
||||||
|
public enum MessageContentType {
|
||||||
|
/**
|
||||||
|
* JSON content - body is a JSON string, can use dot-path or bracket notation for extraction
|
||||||
|
*/
|
||||||
|
JSON,
|
||||||
|
/**
|
||||||
|
* XML content - body is an XML string, can use XPath for extraction
|
||||||
|
*/
|
||||||
|
XML,
|
||||||
|
/**
|
||||||
|
* Raw text content - body is plain text without structured format
|
||||||
|
*/
|
||||||
|
RAW_TEXT
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
package cz.moneta.test.harness.support.messaging;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response from a messaging operation (send/receive).
|
||||||
|
* Provides fluent assertion API for received messages.
|
||||||
|
*/
|
||||||
|
public interface MessageResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a field has the expected value.
|
||||||
|
*
|
||||||
|
* @param path the field path (JSON dot-path for JSON, XPath for XML)
|
||||||
|
* @param value the expected value
|
||||||
|
* @return this for method chaining
|
||||||
|
*/
|
||||||
|
MessageResponse andAssertFieldValue(String path, String value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a field is present in the message.
|
||||||
|
*
|
||||||
|
* @param path the field path
|
||||||
|
* @return this for method chaining
|
||||||
|
*/
|
||||||
|
MessageResponse andAssertPresent(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a field is NOT present in the message.
|
||||||
|
*
|
||||||
|
* @param path the field path
|
||||||
|
* @return this for method chaining
|
||||||
|
*/
|
||||||
|
MessageResponse andAssertNotPresent(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that a header has the expected value.
|
||||||
|
*
|
||||||
|
* @param headerName the header name
|
||||||
|
* @param value the expected value
|
||||||
|
* @return this for method chaining
|
||||||
|
*/
|
||||||
|
MessageResponse andAssertHeaderValue(String headerName, String value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the body contains the given substring.
|
||||||
|
* Useful for raw text or EBCDIC/UTF-8 encoded messages.
|
||||||
|
*
|
||||||
|
* @param substring the expected substring
|
||||||
|
* @return this for method chaining
|
||||||
|
*/
|
||||||
|
MessageResponse andAssertBodyContains(String substring);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a value from the message.
|
||||||
|
*
|
||||||
|
* @param path the path expression
|
||||||
|
* @return JsonPathValue for further assertion
|
||||||
|
*/
|
||||||
|
JsonPathValue extract(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the message body into a Java object.
|
||||||
|
*
|
||||||
|
* @param type the target class
|
||||||
|
* @param <T> the target type
|
||||||
|
* @return the deserialized object
|
||||||
|
*/
|
||||||
|
<T> T mapTo(Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the received message.
|
||||||
|
*
|
||||||
|
* @return the message
|
||||||
|
*/
|
||||||
|
ReceivedMessage getMessage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message body.
|
||||||
|
*
|
||||||
|
* @return the body
|
||||||
|
*/
|
||||||
|
String getBody();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a header value.
|
||||||
|
*
|
||||||
|
* @param name the header name
|
||||||
|
* @return the header value or null
|
||||||
|
*/
|
||||||
|
String getHeader(String name);
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package cz.moneta.test.harness.support.messaging;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message format for IBM MQ messages.
|
||||||
|
*/
|
||||||
|
public enum MqMessageFormat {
|
||||||
|
/**
|
||||||
|
* JSON format - JMS TextMessage with plain JSON string (UTF-8 default)
|
||||||
|
*/
|
||||||
|
JSON,
|
||||||
|
/**
|
||||||
|
* XML format - JMS TextMessage with XML string (UTF-8 default)
|
||||||
|
*/
|
||||||
|
XML,
|
||||||
|
/**
|
||||||
|
* EBCDIC format - JMS BytesMessage with EBCDIC IBM-870 encoding (CZ/SK mainframe)
|
||||||
|
*/
|
||||||
|
EBCDIC_870,
|
||||||
|
/**
|
||||||
|
* UTF-8 format - JMS BytesMessage with UTF-8 IBM CCSID 1208 encoding
|
||||||
|
*/
|
||||||
|
UTF8_1208
|
||||||
|
}
|
||||||
@ -0,0 +1,304 @@
|
|||||||
|
package cz.moneta.test.harness.support.messaging;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.xpath.XPath;
|
||||||
|
import javax.xml.xpath.XPathExpressionException;
|
||||||
|
import javax.xml.xpath.XPathFactory;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a received message from a messaging system (IBM MQ or Kafka).
|
||||||
|
* <p>
|
||||||
|
* Provides unified API for accessing message content regardless of source system.
|
||||||
|
* Body is always normalized to a String, with content type detection for proper extraction.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class ReceivedMessage {
|
||||||
|
|
||||||
|
private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
|
||||||
|
private static final XmlMapper XML_MAPPER = new XmlMapper();
|
||||||
|
|
||||||
|
private final String body;
|
||||||
|
private final MessageContentType contentType;
|
||||||
|
private final Map<String, String> headers;
|
||||||
|
private final long timestamp;
|
||||||
|
private final String source;
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
private ReceivedMessage(Builder builder) {
|
||||||
|
this.body = builder.body;
|
||||||
|
this.contentType = builder.contentType;
|
||||||
|
this.headers = builder.headers != null ? Collections.unmodifiableMap(new HashMap<>(builder.headers)) : Collections.emptyMap();
|
||||||
|
this.timestamp = builder.timestamp;
|
||||||
|
this.source = builder.source;
|
||||||
|
this.key = builder.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new builder for ReceivedMessage.
|
||||||
|
*/
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a value from the message body using JSON dot-path or bracket notation.
|
||||||
|
* <p>
|
||||||
|
* Supports paths like:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code "field"} - top-level field</li>
|
||||||
|
* <li>{@code "parent.child"} - nested field</li>
|
||||||
|
* <li>{@code "items[0]"} - array element</li>
|
||||||
|
* <li>{@code "items[0].name"} - nested field in array element</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param path the JSON path expression
|
||||||
|
* @return the extracted value as JsonNode
|
||||||
|
*/
|
||||||
|
public JsonNode extractJson(String path) {
|
||||||
|
if (contentType != MessageContentType.JSON) {
|
||||||
|
throw new IllegalStateException("JSON extraction is only supported for JSON content type, got: " + contentType);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
JsonNode rootNode = JSON_MAPPER.readTree(body);
|
||||||
|
return extractNode(path, rootNode);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to parse JSON body: " + body, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a value from XML message body using XPath expression.
|
||||||
|
*
|
||||||
|
* @param xpath the XPath expression
|
||||||
|
* @return the extracted value as String
|
||||||
|
*/
|
||||||
|
public String extractXml(String xpath) {
|
||||||
|
if (contentType != MessageContentType.XML) {
|
||||||
|
throw new IllegalStateException("XML extraction is only supported for XML content type, got: " + contentType);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
|
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||||
|
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||||
|
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||||
|
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||||
|
Document doc = builder.parse(new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
XPath xPath = XPathFactory.newInstance().newXPath();
|
||||||
|
return xPath.evaluate(xpath, doc);
|
||||||
|
} catch (XPathExpressionException e) {
|
||||||
|
throw new RuntimeException("Failed to evaluate XPath: " + xpath, e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to parse XML body: " + body, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a value from the message body. Auto-detects content type and uses appropriate extraction method.
|
||||||
|
*
|
||||||
|
* @param expression the path expression (JSON path for JSON, XPath for XML)
|
||||||
|
* @return the extracted value as String
|
||||||
|
*/
|
||||||
|
public String extract(String expression) {
|
||||||
|
return switch (contentType) {
|
||||||
|
case JSON -> extractJson(expression).asText();
|
||||||
|
case XML -> extractXml(expression);
|
||||||
|
case RAW_TEXT -> body;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message body as a String.
|
||||||
|
*
|
||||||
|
* @return the body content
|
||||||
|
*/
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message content type.
|
||||||
|
*
|
||||||
|
* @return the content type
|
||||||
|
*/
|
||||||
|
public MessageContentType getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a header value by name.
|
||||||
|
*
|
||||||
|
* @param name the header name
|
||||||
|
* @return the header value, or null if not present
|
||||||
|
*/
|
||||||
|
public String getHeader(String name) {
|
||||||
|
return headers.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all headers.
|
||||||
|
*
|
||||||
|
* @return unmodifiable map of headers
|
||||||
|
*/
|
||||||
|
public Map<String, String> getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message timestamp.
|
||||||
|
*
|
||||||
|
* @return timestamp in milliseconds
|
||||||
|
*/
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the source (topic name for Kafka, queue name for IBM MQ).
|
||||||
|
*
|
||||||
|
* @return the source name
|
||||||
|
*/
|
||||||
|
public String getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message key (Kafka only, null for IBM MQ).
|
||||||
|
*
|
||||||
|
* @return the message key or null
|
||||||
|
*/
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes the message body into a Java object.
|
||||||
|
* <p>
|
||||||
|
* For JSON content: uses Jackson ObjectMapper.readValue
|
||||||
|
* For XML content: uses Jackson XmlMapper
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param type the target class
|
||||||
|
* @param <T> the target type
|
||||||
|
* @return the deserialized object
|
||||||
|
*/
|
||||||
|
public <T> T mapTo(Class<T> type) {
|
||||||
|
try {
|
||||||
|
if (contentType == MessageContentType.XML) {
|
||||||
|
return XML_MAPPER.readValue(body, type);
|
||||||
|
} else {
|
||||||
|
return JSON_MAPPER.readValue(body, type);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to deserialize message body to " + type.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for ReceivedMessage.
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private String body;
|
||||||
|
private MessageContentType contentType = MessageContentType.JSON;
|
||||||
|
private Map<String, String> headers;
|
||||||
|
private long timestamp = System.currentTimeMillis();
|
||||||
|
private String source;
|
||||||
|
private String key;
|
||||||
|
|
||||||
|
public Builder body(String body) {
|
||||||
|
this.body = body;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder contentType(MessageContentType contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder headers(Map<String, String> headers) {
|
||||||
|
this.headers = headers;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder timestamp(long timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder source(String source) {
|
||||||
|
this.source = source;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder key(String key) {
|
||||||
|
this.key = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReceivedMessage build() {
|
||||||
|
return new ReceivedMessage(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a node from JSON using dot/bracket notation.
|
||||||
|
*/
|
||||||
|
private static JsonNode extractNode(String path, JsonNode rootNode) {
|
||||||
|
Pattern arrayPattern = Pattern.compile("(.*?)\\[([0-9]+)\\]");
|
||||||
|
return Arrays.stream(path.split("\\."))
|
||||||
|
.filter(StringUtils::isNotEmpty)
|
||||||
|
.reduce(rootNode,
|
||||||
|
(node, part) -> {
|
||||||
|
Matcher matcher = arrayPattern.matcher(part);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return node.path(matcher.group(1)).path(Integer.parseInt(matcher.group(2)));
|
||||||
|
} else {
|
||||||
|
return node.path(part);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(n1, n2) -> n1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a cz.moneta.test.harness.messaging.ReceivedMessage to this class.
|
||||||
|
*
|
||||||
|
* @param other the message to convert
|
||||||
|
* @return converted message
|
||||||
|
*/
|
||||||
|
public static ReceivedMessage fromMessagingReceivedMessage(
|
||||||
|
cz.moneta.test.harness.messaging.ReceivedMessage other) {
|
||||||
|
if (other == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
cz.moneta.test.harness.support.messaging.MessageContentType contentType =
|
||||||
|
switch (other.getContentType()) {
|
||||||
|
case JSON -> cz.moneta.test.harness.support.messaging.MessageContentType.JSON;
|
||||||
|
case XML -> cz.moneta.test.harness.support.messaging.MessageContentType.XML;
|
||||||
|
case RAW_TEXT -> cz.moneta.test.harness.support.messaging.MessageContentType.RAW_TEXT;
|
||||||
|
};
|
||||||
|
return builder()
|
||||||
|
.body(other.getBody())
|
||||||
|
.contentType(contentType)
|
||||||
|
.headers(other.getHeaders())
|
||||||
|
.timestamp(other.getTimestamp())
|
||||||
|
.source(other.getSource())
|
||||||
|
.key(other.getKey())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,7 +6,7 @@
|
|||||||
<artifactId>tests</artifactId>
|
<artifactId>tests</artifactId>
|
||||||
<version>2.29-SNAPSHOT</version>
|
<version>2.29-SNAPSHOT</version>
|
||||||
<properties>
|
<properties>
|
||||||
<harness.version>7.55.820</harness.version>
|
<harness.version>7.55-SNAPSHOT</harness.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<junit.platform.version>1.5.1</junit.platform.version>
|
<junit.platform.version>1.5.1</junit.platform.version>
|
||||||
</properties>
|
</properties>
|
||||||
@ -15,6 +15,7 @@
|
|||||||
<groupId>cz.moneta.test</groupId>
|
<groupId>cz.moneta.test</groupId>
|
||||||
<artifactId>harness</artifactId>
|
<artifactId>harness</artifactId>
|
||||||
<version>${harness.version}</version>
|
<version>${harness.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import cz.moneta.test.dsl.greenscreen.GreenScreen;
|
|||||||
import cz.moneta.test.dsl.hypos.Hypos;
|
import cz.moneta.test.dsl.hypos.Hypos;
|
||||||
import cz.moneta.test.dsl.ib.Ib;
|
import cz.moneta.test.dsl.ib.Ib;
|
||||||
import cz.moneta.test.dsl.ilods.Ilods;
|
import cz.moneta.test.dsl.ilods.Ilods;
|
||||||
|
import cz.moneta.test.dsl.imq.ImqFirstVision;
|
||||||
import cz.moneta.test.dsl.kasanova.Kasanova;
|
import cz.moneta.test.dsl.kasanova.Kasanova;
|
||||||
import cz.moneta.test.dsl.mobile.smartbanking.home.Sb;
|
import cz.moneta.test.dsl.mobile.smartbanking.home.Sb;
|
||||||
import cz.moneta.test.dsl.monetaapiportal.MonetaApiPortal;
|
import cz.moneta.test.dsl.monetaapiportal.MonetaApiPortal;
|
||||||
@ -282,6 +283,10 @@ public class Harness extends BaseStoreAccessor {
|
|||||||
return new Cashman(this);
|
return new Cashman(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImqFirstVision withImqFirstVision() {
|
||||||
|
return new ImqFirstVision(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void initGenerators() {
|
private void initGenerators() {
|
||||||
addGenerator(RC, new RcGenerator());
|
addGenerator(RC, new RcGenerator());
|
||||||
addGenerator(FIRST_NAME, new FirstNameGenerator());
|
addGenerator(FIRST_NAME, new FirstNameGenerator());
|
||||||
|
|||||||
187
tests/src/main/java/cz/moneta/test/dsl/imq/ImqFirstVision.java
Normal file
187
tests/src/main/java/cz/moneta/test/dsl/imq/ImqFirstVision.java
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package cz.moneta.test.dsl.imq;
|
||||||
|
|
||||||
|
import cz.moneta.test.dsl.Harness;
|
||||||
|
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionEndpoint;
|
||||||
|
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionQueue;
|
||||||
|
import cz.moneta.test.harness.messaging.ReceivedMessage;
|
||||||
|
import cz.moneta.test.harness.messaging.exception.MessagingTimeoutException;
|
||||||
|
import cz.moneta.test.harness.support.messaging.ImqRequest;
|
||||||
|
import cz.moneta.test.harness.messaging.MqMessageFormat;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IBM MQ First Vision DSL.
|
||||||
|
* <p>
|
||||||
|
* Usage example:
|
||||||
|
* <pre>{@code
|
||||||
|
* harness.withImqFirstVision()
|
||||||
|
* .toQueue(ImqFirstVisionQueue.PAYMENT_NOTIFICATIONS)
|
||||||
|
* .withPayload("{\"paymentId\": \"PAY-123\"}")
|
||||||
|
* .send();
|
||||||
|
*
|
||||||
|
* harness.withImqFirstVision()
|
||||||
|
* .fromQueue(ImqFirstVisionQueue.PAYMENT_NOTIFICATIONS)
|
||||||
|
* .receiveWhere(msg -> msg.extract("paymentId").equals("PAY-123"))
|
||||||
|
* .withTimeout(10, TimeUnit.SECONDS)
|
||||||
|
* .andAssertFieldValue("result", "OK");
|
||||||
|
* }</pre>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class ImqFirstVision {
|
||||||
|
|
||||||
|
private final Harness harness;
|
||||||
|
|
||||||
|
public ImqFirstVision(Harness harness) {
|
||||||
|
this.harness = harness;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start building a message to send to a queue.
|
||||||
|
*/
|
||||||
|
public ImqRequest.QueuePhase toQueue(ImqFirstVisionQueue queue) {
|
||||||
|
ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
|
||||||
|
return ImqRequest.toQueue(endpoint, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start building a message to receive from a queue.
|
||||||
|
*/
|
||||||
|
public ImqRequest.ReceivePhase fromQueue(ImqFirstVisionQueue queue) {
|
||||||
|
ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
|
||||||
|
return ImqRequest.fromQueue(endpoint, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a JSON message to a queue.
|
||||||
|
*/
|
||||||
|
public void sendToQueue(ImqFirstVisionQueue queue, String payload) {
|
||||||
|
sendToQueue(queue, payload, MqMessageFormat.JSON, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to a queue with specified format.
|
||||||
|
*/
|
||||||
|
public void sendToQueue(ImqFirstVisionQueue queue, String payload, MqMessageFormat format) {
|
||||||
|
sendToQueue(queue, payload, format, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to a queue with specified format and properties.
|
||||||
|
*/
|
||||||
|
public void sendToQueue(ImqFirstVisionQueue queue, String payload, MqMessageFormat format,
|
||||||
|
Map<String, String> properties) {
|
||||||
|
ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
|
||||||
|
endpoint.send(queue, payload, format, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a JSON message to a queue by physical name.
|
||||||
|
*/
|
||||||
|
public void sendToQueue(String queueName, String payload) {
|
||||||
|
sendToQueue(queueName, payload, MqMessageFormat.JSON, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to a queue by physical name with specified format.
|
||||||
|
*/
|
||||||
|
public void sendToQueue(String queueName, String payload, MqMessageFormat format) {
|
||||||
|
sendToQueue(queueName, payload, format, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to a queue by physical name with specified format and properties.
|
||||||
|
*/
|
||||||
|
public void sendToQueue(String queueName, String payload, MqMessageFormat format,
|
||||||
|
Map<String, String> properties) {
|
||||||
|
ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
|
||||||
|
endpoint.send(queueName, payload, format, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a message from a queue with timeout.
|
||||||
|
*/
|
||||||
|
public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, Duration timeout) {
|
||||||
|
return receiveFromQueue(queue, null, MqMessageFormat.JSON, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a message from a queue with selector and timeout.
|
||||||
|
*/
|
||||||
|
public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, String selector, Duration timeout) {
|
||||||
|
return receiveFromQueue(queue, selector, MqMessageFormat.JSON, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a message from a queue with format and timeout.
|
||||||
|
*/
|
||||||
|
public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, MqMessageFormat format, Duration timeout) {
|
||||||
|
return receiveFromQueue(queue, null, format, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a message from a queue with selector, format and timeout.
|
||||||
|
*/
|
||||||
|
public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, String selector,
|
||||||
|
MqMessageFormat format, Duration timeout) {
|
||||||
|
ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
|
||||||
|
return endpoint.receive(queue, selector, format, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a message matching predicate from a queue.
|
||||||
|
*/
|
||||||
|
public ReceivedMessage receiveFromQueue(ImqFirstVisionQueue queue, Predicate<ReceivedMessage> filter,
|
||||||
|
Duration timeout) {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
long timeoutMs = timeout.toMillis();
|
||||||
|
|
||||||
|
while (System.currentTimeMillis() - startTime < timeoutMs) {
|
||||||
|
try {
|
||||||
|
ReceivedMessage message = receiveFromQueue(queue, MqMessageFormat.JSON, Duration.ofSeconds(1));
|
||||||
|
if (filter.test(message)) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
} catch (MessagingTimeoutException e) {
|
||||||
|
// Continue polling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MessagingTimeoutException(
|
||||||
|
"No message matching filter found on queue '" + queue.getConfigKey() +
|
||||||
|
"' within " + timeout.toMillis() + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browse messages from a queue (non-destructive).
|
||||||
|
*/
|
||||||
|
public List<ReceivedMessage> browseQueue(ImqFirstVisionQueue queue, int maxMessages) {
|
||||||
|
return browseQueue(queue, null, MqMessageFormat.JSON, maxMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browse messages from a queue with selector (non-destructive).
|
||||||
|
*/
|
||||||
|
public List<ReceivedMessage> browseQueue(ImqFirstVisionQueue queue, String selector, int maxMessages) {
|
||||||
|
return browseQueue(queue, selector, MqMessageFormat.JSON, maxMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browse messages from a queue with format and max count (non-destructive).
|
||||||
|
*/
|
||||||
|
public List<ReceivedMessage> browseQueue(ImqFirstVisionQueue queue, MqMessageFormat format, int maxMessages) {
|
||||||
|
return browseQueue(queue, null, format, maxMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browse messages from a queue with selector, format and max count (non-destructive).
|
||||||
|
*/
|
||||||
|
public List<ReceivedMessage> browseQueue(ImqFirstVisionQueue queue, String selector,
|
||||||
|
MqMessageFormat format, int maxMessages) {
|
||||||
|
ImqFirstVisionEndpoint endpoint = harness.getEndpoint(ImqFirstVisionEndpoint.class);
|
||||||
|
return endpoint.browse(queue, selector, format, maxMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -76,4 +76,21 @@ endpoints.szr-mock-api.url=https://api-szr.ppe.moneta-containers.net
|
|||||||
endpoints.forte.url=https://forteppe.ux.mbid.cz/portal/home/
|
endpoints.forte.url=https://forteppe.ux.mbid.cz/portal/home/
|
||||||
|
|
||||||
#Exevido
|
#Exevido
|
||||||
endpoints.exevido.url=https://exevido.ppe.moneta-containers.net/#/auth/login
|
endpoints.exevido.url=https://exevido.ppe.moneta-containers.net/#/auth/login
|
||||||
|
|
||||||
|
#IBM MQ First Vision
|
||||||
|
endpoints.imq-first-vision.connection-name-list=mq9multipe5x(1414),mq9multipe6x(1414)
|
||||||
|
endpoints.imq-first-vision.channel=CLIENT.CHANNEL
|
||||||
|
endpoints.imq-first-vision.queue-manager=MVSW2PPE
|
||||||
|
endpoints.imq-first-vision.ssl-cipher-suite=TLS_RSA_WITH_AES_256_CBC_SHA256
|
||||||
|
|
||||||
|
#IBM MQ queues
|
||||||
|
endpoints.imq-first-vision.payment-notifications.queue=MVSW2PPE.DELIVERY.NOTIFICATION
|
||||||
|
endpoints.imq-first-vision.payment-request.queue=MVSW2PPE.DELIVERY.REQUEST
|
||||||
|
endpoints.imq-first-vision.mf-requests.queue=MVSW2PPE.MF.REQUESTS
|
||||||
|
endpoints.imq-first-vision.mf-responses.queue=MVSW2PPE.MF.RESPONSES
|
||||||
|
endpoints.imq-first-vision.mf-ebcdic.queue=MVSW2PPE.MF.EBCDIC
|
||||||
|
endpoints.imq-first-vision.mf-utf8.queue=MVSW2PPE.MF.UTF8
|
||||||
|
|
||||||
|
#Vault path for IBM MQ credentials
|
||||||
|
vault.imq-first-vision.secrets.path=/kv/autotesty/ppe/imq-first-vision
|
||||||
@ -97,4 +97,21 @@ endpoints.szr-mock-api.url=https://api-szr.tst.moneta-containers.net
|
|||||||
endpoints.exevido.url=https://exevido.tst.moneta-containers.net/#/auth/login
|
endpoints.exevido.url=https://exevido.tst.moneta-containers.net/#/auth/login
|
||||||
|
|
||||||
#Cashman
|
#Cashman
|
||||||
endpoints.cashman.url=https://cashmantst.mbid.cz/
|
endpoints.cashman.url=https://cashmantst.mbid.cz/
|
||||||
|
|
||||||
|
#IBM MQ First Vision
|
||||||
|
endpoints.imq-first-vision.connection-name-list=mq9multitst5x(1414),mq9multitst6x(1414)
|
||||||
|
endpoints.imq-first-vision.channel=CLIENT.CHANNEL
|
||||||
|
endpoints.imq-first-vision.queue-manager=MVSW2TST3
|
||||||
|
endpoints.imq-first-vision.ssl-cipher-suite=TLS_RSA_WITH_AES_256_CBC_SHA256
|
||||||
|
|
||||||
|
#IBM MQ queues
|
||||||
|
endpoints.imq-first-vision.payment-notifications.queue=MVSW2TST3.DELIVERY.NOTIFICATION
|
||||||
|
endpoints.imq-first-vision.payment-request.queue=MVSW2TST3.DELIVERY.REQUEST
|
||||||
|
endpoints.imq-first-vision.mf-requests.queue=MVSW2TST3.MF.REQUESTS
|
||||||
|
endpoints.imq-first-vision.mf-responses.queue=MVSW2TST3.MF.RESPONSES
|
||||||
|
endpoints.imq-first-vision.mf-ebcdic.queue=MVSW2TST3.MF.EBCDIC
|
||||||
|
endpoints.imq-first-vision.mf-utf8.queue=MVSW2TST3.MF.UTF8
|
||||||
|
|
||||||
|
#Vault path for IBM MQ credentials
|
||||||
|
vault.imq-first-vision.secrets.path=/kv/autotesty/tst1/imq-first-vision
|
||||||
Loading…
x
Reference in New Issue
Block a user