Skip to content

Commit 25aae1b

Browse files
timonbackPascal Dal Farra
authored andcommitted
Chore/improve amqp test coverage (springwolf#948)
* tests: refactoring + add yaml endpoint testing * tests: additional RabbitListener tests * chore(amqp): fix queue configuration * chore(amqp): simplify local testing * chore: update asyncapi.yaml gradle script * feat(amqp): scan all queues In addition to the routingKeys * test(amqp): update asyncapi artifacts Part of springwolfGH-366 * test(ui): use valid mock data * chore(ui): fix formatting * test(amqp): update asyncapi artifacts * feat(ui): show channel bindings * test: add WaitStrategy for ApiSystemTest * chore(amqp): use non-exclusive queue in example * chore(amqp): use non-exclusive queue in example * test(amqp): persist patched asyncapi.yaml * chore(amqp): add spring-messaging dependency in example * test(amqp): wait for ready amqp server * test(amqp): update e2e * test(amqp): cleanup --------- Co-authored-by: Pascal Dal Farra <[email protected]>
1 parent 0a0184a commit 25aae1b

File tree

34 files changed

+1007
-41
lines changed

34 files changed

+1007
-41
lines changed

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ allprojects {
162162
project
163163
.file('springwolf-examples/' + it + '-example/src/test/resources/asyncapi.actual.json')
164164
.renameTo('springwolf-examples/' + it + '-example/src/test/resources/asyncapi.json')
165+
project
166+
.file('springwolf-examples/' + it + '-example/src/test/resources/asyncapi.actual.yaml')
167+
.renameTo('springwolf-examples/' + it + '-example/src/test/resources/asyncapi.yaml')
165168
}
166169
}
167170
}

springwolf-core/src/main/java/io/github/springwolf/core/SpringwolfInitApplicationListener.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ public class SpringwolfInitApplicationListener implements ApplicationListener<Ap
2424
@Override
2525
public void onApplicationEvent(ApplicationReadyEvent event) {
2626
if (configProperties.getInitMode() == InitMode.BACKGROUND) {
27-
log.debug("triggering background asyncapi creation..");
27+
log.debug("Triggering background asyncapi creation");
2828
new Thread(asyncApiService::getAsyncAPI).start();
2929
} else {
30-
log.debug("triggering asyncapi creation..");
30+
log.debug("Triggering asyncapi creation");
3131
this.asyncApiService.getAsyncAPI();
3232
}
3333
}

springwolf-examples/e2e/tests/publishing.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ function testPublishingEveryChannelItem() {
7474
payload === "MonetaryAmount" || // Issue with either MonetaryAmount of ModelConverters
7575
payload === "Message" || // Unable to instantiate ExamplePayloadProtobufDto$Message class
7676
payload === "VehicleBase" || // Unable to publish abstract class for discriminator demo
77+
payload === "GenericPayloadDto" || // Unable to publish generic payload (amqp)
78+
channelName === "#" || // Publishing through amqp exchange is not supported, see GH-366
7779
channelName === "example-topic-routing-key" // Publishing through amqp exchange is not supported, see GH-366
7880
) {
7981
return; // skip

springwolf-examples/springwolf-amqp-example/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dependencies {
3131
implementation "org.springframework.boot:spring-boot-autoconfigure"
3232
implementation "org.springframework.boot:spring-boot"
3333
implementation "org.springframework:spring-context"
34+
implementation "org.springframework:spring-messaging"
3435

3536
testRuntimeOnly "org.junit.jupiter:junit-jupiter:${junitJupiterVersion}"
3637

springwolf-examples/springwolf-amqp-example/docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
services:
22
app:
33
image: stavshamir/springwolf-amqp-example:${SPRINGWOLF_VERSION}
4+
environment:
5+
AMQP_HOST: amqp
46
links:
57
- amqp
68
ports:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
package io.github.springwolf.examples.amqp;
3+
4+
import lombok.AccessLevel;
5+
import lombok.NoArgsConstructor;
6+
7+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
8+
public final class AmqpConstants {
9+
10+
// Exchanges
11+
public static final String EXCHANGE_EXAMPLE_TOPIC_EXCHANGE = "example-topic-exchange";
12+
public static final String EXCHANGE_CRUD_TOPIC_EXCHANGE_1 = "CRUD-topic-exchange-1";
13+
public static final String EXCHANGE_CRUD_TOPIC_EXCHANGE_2 = "CRUD-topic-exchange-2";
14+
/**
15+
* Direct Exchange - Routing is based on 'Routing key'.
16+
* Messages are 'routed' towards queue which name is equals to 'routing key' <BR/>
17+
* Note:<BR/>
18+
* The 'Default exchange' is a 'Direct exchange' with an empty name ( name= "")
19+
*/
20+
public static final String EXCHANGE_DEFAULT_EXCHANGE = "";
21+
22+
// Routing keys
23+
/**
24+
* When a queue is bound with "#" (hash) binding key,
25+
* it will receive all the messages, regardless of the routing key - like in fanout exchange.
26+
*/
27+
public static final String ROUTING_KEY_ALL_MESSAGES = "#";
28+
29+
public static final String ROUTING_KEY_EXAMPLE_TOPIC_ROUTING_KEY = "example-topic-routing-key";
30+
31+
// Queues
32+
public static final String QUEUE_EXAMPLE_QUEUE = "example-queue";
33+
public static final String QUEUE_ANOTHER_QUEUE = "another-queue";
34+
public static final String QUEUE_MULTI_PAYLOAD_QUEUE = "multi-payload-queue";
35+
public static final String QUEUE_EXAMPLE_BINDINGS_QUEUE = "example-bindings-queue";
36+
37+
public static final String QUEUE_CREATE = "queue-create";
38+
public static final String QUEUE_READ = "queue-read";
39+
public static final String QUEUE_DELETE = "queue-delete";
40+
public static final String QUEUE_UPDATE = "queue-update";
41+
}

springwolf-examples/springwolf-amqp-example/src/main/java/io/github/springwolf/examples/amqp/configuration/RabbitConfiguration.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22
package io.github.springwolf.examples.amqp.configuration;
33

4+
import io.github.springwolf.examples.amqp.AmqpConstants;
45
import org.springframework.amqp.core.Binding;
56
import org.springframework.amqp.core.BindingBuilder;
67
import org.springframework.amqp.core.Exchange;
@@ -23,34 +24,39 @@ public Jackson2JsonMessageConverter converter() {
2324

2425
@Bean
2526
public Queue exampleQueue() {
26-
return new Queue("example-queue", false);
27+
return new Queue(AmqpConstants.QUEUE_EXAMPLE_QUEUE, false);
2728
}
2829

2930
@Bean
3031
public Queue anotherQueue() {
31-
return new Queue("another-queue", false);
32+
return new Queue(AmqpConstants.QUEUE_ANOTHER_QUEUE, false);
3233
}
3334

3435
@Bean
3536
public Queue exampleBindingsQueue() {
36-
return new Queue("example-bindings-queue", false, true, true);
37+
return new Queue(AmqpConstants.QUEUE_EXAMPLE_BINDINGS_QUEUE, false, false, true);
38+
}
39+
40+
@Bean
41+
public Queue queueRead() {
42+
return new Queue(AmqpConstants.QUEUE_READ, false);
3743
}
3844

3945
@Bean
4046
public Exchange exampleTopicExchange() {
41-
return new TopicExchange("example-topic-exchange");
47+
return new TopicExchange(AmqpConstants.EXCHANGE_EXAMPLE_TOPIC_EXCHANGE);
4248
}
4349

4450
@Bean
4551
public Queue multiPayloadQueue() {
46-
return new Queue("multi-payload-queue");
52+
return new Queue(AmqpConstants.QUEUE_MULTI_PAYLOAD_QUEUE);
4753
}
4854

4955
@Bean
5056
public Binding exampleTopicBinding(Queue exampleBindingsQueue, Exchange exampleTopicExchange) {
5157
return BindingBuilder.bind(exampleBindingsQueue)
5258
.to(exampleTopicExchange)
53-
.with("example-topic-routing-key")
59+
.with(AmqpConstants.ROUTING_KEY_EXAMPLE_TOPIC_ROUTING_KEY)
5460
.noargs();
5561
}
5662
}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
// SPDX-License-Identifier: Apache-2.0
22
package io.github.springwolf.examples.amqp.consumers;
33

4+
import io.github.springwolf.examples.amqp.AmqpConstants;
45
import io.github.springwolf.examples.amqp.dtos.AnotherPayloadDto;
56
import io.github.springwolf.examples.amqp.dtos.ExamplePayloadDto;
7+
import io.github.springwolf.examples.amqp.dtos.GenericPayloadDto;
68
import io.github.springwolf.examples.amqp.producers.AnotherProducer;
79
import lombok.RequiredArgsConstructor;
810
import lombok.extern.slf4j.Slf4j;
911
import org.springframework.amqp.core.ExchangeTypes;
12+
import org.springframework.amqp.core.Message;
1013
import org.springframework.amqp.rabbit.annotation.Exchange;
1114
import org.springframework.amqp.rabbit.annotation.Queue;
1215
import org.springframework.amqp.rabbit.annotation.QueueBinding;
1316
import org.springframework.amqp.rabbit.annotation.RabbitListener;
17+
import org.springframework.messaging.handler.annotation.Payload;
1418
import org.springframework.stereotype.Component;
1519

1620
@Component
@@ -20,9 +24,9 @@ public class ExampleConsumer {
2024

2125
private final AnotherProducer anotherProducer;
2226

23-
@RabbitListener(queues = "example-queue")
27+
@RabbitListener(queues = AmqpConstants.QUEUE_EXAMPLE_QUEUE)
2428
public void receiveExamplePayload(ExamplePayloadDto payload) {
25-
log.info("Received new message in example-queue: {}", payload.toString());
29+
log.info("Received new message in {}: {}", AmqpConstants.QUEUE_EXAMPLE_QUEUE, payload.toString());
2630

2731
AnotherPayloadDto example = new AnotherPayloadDto();
2832
example.setExample(payload);
@@ -31,37 +35,102 @@ public void receiveExamplePayload(ExamplePayloadDto payload) {
3135
anotherProducer.sendMessage(example);
3236
}
3337

34-
@RabbitListener(queues = "another-queue")
38+
@RabbitListener(queues = AmqpConstants.QUEUE_ANOTHER_QUEUE)
3539
public void receiveAnotherPayload(AnotherPayloadDto payload) {
36-
log.info("Received new message in another-queue: {}", payload.toString());
40+
log.info("Received new message in {}: {}", AmqpConstants.QUEUE_ANOTHER_QUEUE, payload.toString());
3741
}
3842

3943
@RabbitListener(
4044
bindings = {
4145
@QueueBinding(
42-
exchange = @Exchange(name = "example-topic-exchange", type = ExchangeTypes.TOPIC),
46+
exchange =
47+
@Exchange(
48+
name = AmqpConstants.EXCHANGE_EXAMPLE_TOPIC_EXCHANGE,
49+
type = ExchangeTypes.TOPIC),
4350
value =
4451
@Queue(
45-
name = "example-bindings-queue",
52+
name = AmqpConstants.QUEUE_EXAMPLE_BINDINGS_QUEUE,
4653
durable = "false",
47-
exclusive = "true",
54+
exclusive = "false",
4855
autoDelete = "true"),
49-
key = "example-topic-routing-key")
56+
key = AmqpConstants.ROUTING_KEY_EXAMPLE_TOPIC_ROUTING_KEY)
5057
})
5158
public void bindingsExample(AnotherPayloadDto payload) {
5259
log.info(
53-
"Received new message in example-bindings-queue"
54-
+ " through exchange example-topic-exchange using routing key example-topic-routing-key: {}",
60+
"Received new message in {}" + " through exchange {}" + " using routing key {}: {}",
61+
AmqpConstants.QUEUE_EXAMPLE_BINDINGS_QUEUE,
62+
AmqpConstants.EXCHANGE_EXAMPLE_TOPIC_EXCHANGE,
63+
AmqpConstants.ROUTING_KEY_EXAMPLE_TOPIC_ROUTING_KEY,
5564
payload.toString());
5665
}
5766

58-
@RabbitListener(queues = "multi-payload-queue")
67+
@RabbitListener(queues = AmqpConstants.QUEUE_MULTI_PAYLOAD_QUEUE)
5968
public void bindingsBeanExample(AnotherPayloadDto payload) {
60-
log.info("Received new message in multi-payload-queue (AnotherPayloadDto): {}", payload.toString());
69+
log.info(
70+
"Received new message in {} (AnotherPayloadDto): {}",
71+
AmqpConstants.QUEUE_MULTI_PAYLOAD_QUEUE,
72+
payload.toString());
6173
}
6274

63-
@RabbitListener(queues = "multi-payload-queue")
75+
@RabbitListener(queues = AmqpConstants.QUEUE_MULTI_PAYLOAD_QUEUE)
6476
public void bindingsBeanExample(ExamplePayloadDto payload) {
65-
log.info("Received new message in multi-payload-queue (ExamplePayloadDto): {}", payload.toString());
77+
log.info(
78+
"Received new message in {} (ExamplePayloadDto): {}",
79+
AmqpConstants.QUEUE_MULTI_PAYLOAD_QUEUE,
80+
payload.toString());
81+
}
82+
83+
@RabbitListener(queuesToDeclare = @Queue(name = AmqpConstants.QUEUE_CREATE, autoDelete = "false", durable = "true"))
84+
public void queuesToDeclareCreate(Message message, @Payload GenericPayloadDto<String> payload) {
85+
log.info(
86+
"Received new message {} in {} (GenericPayloadDto<String>): {}",
87+
message,
88+
AmqpConstants.QUEUE_CREATE,
89+
payload.toString());
90+
}
91+
92+
@RabbitListener(queuesToDeclare = @Queue(name = AmqpConstants.QUEUE_DELETE, autoDelete = "false", durable = "true"))
93+
public void queuesToDeclareDelete(Message message, @Payload GenericPayloadDto<Long> payload) {
94+
log.info(
95+
"Received new message {} in {} (GenericPayloadDto<Long>): {}",
96+
message,
97+
AmqpConstants.QUEUE_DELETE,
98+
payload.toString());
99+
}
100+
101+
@RabbitListener(
102+
autoStartup = "false",
103+
bindings =
104+
@QueueBinding(
105+
exchange =
106+
@Exchange(
107+
name = AmqpConstants.EXCHANGE_CRUD_TOPIC_EXCHANGE_1,
108+
type = ExchangeTypes.TOPIC),
109+
key = AmqpConstants.ROUTING_KEY_ALL_MESSAGES,
110+
value = @Queue(name = AmqpConstants.QUEUE_UPDATE, durable = "true", autoDelete = "false")))
111+
public void bindingsUpdate(Message message, @Payload GenericPayloadDto<ExamplePayloadDto> payload) {
112+
log.info(
113+
"Received new message {} in {} (GenericPayloadDto<ExamplePayloadDto>): {}",
114+
message,
115+
AmqpConstants.QUEUE_UPDATE,
116+
payload.toString());
117+
}
118+
119+
@RabbitListener(
120+
autoStartup = "false",
121+
bindings =
122+
@QueueBinding(
123+
exchange =
124+
@Exchange(
125+
name = AmqpConstants.EXCHANGE_CRUD_TOPIC_EXCHANGE_2,
126+
type = ExchangeTypes.TOPIC),
127+
key = AmqpConstants.ROUTING_KEY_ALL_MESSAGES,
128+
value = @Queue(name = AmqpConstants.QUEUE_READ, durable = "false", autoDelete = "false")))
129+
public void bindingsRead(Message message, @Payload ExamplePayloadDto payload) {
130+
log.info(
131+
"Received new message {} in {} (ExamplePayloadDto): {}",
132+
message,
133+
AmqpConstants.QUEUE_UPDATE,
134+
payload.toString());
66135
}
67136
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
package io.github.springwolf.examples.amqp.dtos;
3+
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
10+
11+
@Schema(description = "Generic payload model")
12+
@Data
13+
@AllArgsConstructor
14+
@NoArgsConstructor
15+
public class GenericPayloadDto<T> {
16+
17+
@Schema(description = "Generic Payload field", requiredMode = REQUIRED)
18+
private T genericValue;
19+
}

springwolf-examples/springwolf-amqp-example/src/main/java/io/github/springwolf/examples/amqp/producers/AnotherProducer.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.github.springwolf.bindings.amqp.annotations.AmqpAsyncOperationBinding;
55
import io.github.springwolf.core.asyncapi.annotations.AsyncOperation;
66
import io.github.springwolf.core.asyncapi.annotations.AsyncPublisher;
7+
import io.github.springwolf.examples.amqp.AmqpConstants;
78
import io.github.springwolf.examples.amqp.dtos.AnotherPayloadDto;
89
import lombok.RequiredArgsConstructor;
910
import org.springframework.amqp.rabbit.core.RabbitTemplate;
@@ -17,11 +18,14 @@ public class AnotherProducer {
1718
@AsyncPublisher(
1819
operation =
1920
@AsyncOperation(
20-
channelName = "example-topic-exchange",
21+
channelName = AmqpConstants.EXCHANGE_EXAMPLE_TOPIC_EXCHANGE,
2122
description = "Custom, optional description defined in the AsyncPublisher annotation"))
2223
@AmqpAsyncOperationBinding()
2324
public void sendMessage(AnotherPayloadDto msg) {
2425
// send
25-
rabbitTemplate.convertAndSend("example-topic-exchange", "example-topic-routing-key", msg);
26+
rabbitTemplate.convertAndSend(
27+
AmqpConstants.EXCHANGE_EXAMPLE_TOPIC_EXCHANGE,
28+
AmqpConstants.ROUTING_KEY_EXAMPLE_TOPIC_ROUTING_KEY,
29+
msg);
2630
}
2731
}

springwolf-examples/springwolf-amqp-example/src/main/resources/application.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ spring.application.name=Springwolf example project - AMQP
55

66
#########
77
# Spring amqp configuration
8-
spring.rabbitmq.host=amqp
8+
spring.rabbitmq.host=${AMQP_HOST:localhost}
99
spring.rabbitmq.port=5672
1010
spring.rabbitmq.username=guest
1111
spring.rabbitmq.password=guest

0 commit comments

Comments
 (0)