Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 54a821e

Browse files
authored
Add test case after fixing the assertions. (#184)
1 parent 3581b87 commit 54a821e

File tree

1 file changed

+310
-0
lines changed

1 file changed

+310
-0
lines changed
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
package com.dke.data.agrirouter.test.usecase;
2+
3+
import agrirouter.feed.response.FeedResponse;
4+
import agrirouter.request.Request;
5+
import agrirouter.response.Response;
6+
import com.dke.data.agrirouter.api.cancellation.DefaultCancellationToken;
7+
import com.dke.data.agrirouter.api.dto.encoding.DecodeMessageResponse;
8+
import com.dke.data.agrirouter.api.dto.messaging.FetchMessageResponse;
9+
import com.dke.data.agrirouter.api.dto.onboard.OnboardingResponse;
10+
import com.dke.data.agrirouter.api.enums.ContentMessageType;
11+
import com.dke.data.agrirouter.api.enums.SystemMessageType;
12+
import com.dke.data.agrirouter.api.env.QA;
13+
import com.dke.data.agrirouter.api.service.messaging.encoding.DecodeMessageService;
14+
import com.dke.data.agrirouter.api.service.messaging.encoding.EncodeMessageService;
15+
import com.dke.data.agrirouter.api.service.messaging.http.FetchMessageService;
16+
import com.dke.data.agrirouter.api.service.parameters.*;
17+
import com.dke.data.agrirouter.impl.common.MessageIdService;
18+
import com.dke.data.agrirouter.impl.common.UtcTimeService;
19+
import com.dke.data.agrirouter.impl.messaging.SequenceNumberService;
20+
import com.dke.data.agrirouter.impl.messaging.encoding.DecodeMessageServiceImpl;
21+
import com.dke.data.agrirouter.impl.messaging.encoding.EncodeMessageServiceImpl;
22+
import com.dke.data.agrirouter.impl.messaging.rest.*;
23+
import com.dke.data.agrirouter.test.AbstractIntegrationTest;
24+
import com.dke.data.agrirouter.test.Assertions;
25+
import com.dke.data.agrirouter.test.OnboardingResponseRepository;
26+
import com.dke.data.agrirouter.test.helper.ContentReader;
27+
import com.google.protobuf.ByteString;
28+
import java.io.IOException;
29+
import java.util.*;
30+
import java.util.concurrent.atomic.AtomicReference;
31+
import java.util.stream.Collectors;
32+
import java.util.stream.Stream;
33+
import org.apache.http.HttpStatus;
34+
import org.jetbrains.annotations.NotNull;
35+
import org.junit.jupiter.api.AfterEach;
36+
import org.junit.jupiter.api.BeforeEach;
37+
import org.junit.jupiter.params.ParameterizedTest;
38+
import org.junit.jupiter.params.provider.Arguments;
39+
import org.junit.jupiter.params.provider.MethodSource;
40+
41+
/** Test case to show the behavior for chunked message sending. */
42+
class SendAndReceiveChunkedMessagesTest extends AbstractIntegrationTest {
43+
44+
private static final int MAX_CHUNK_SIZE = 1024000;
45+
46+
@ParameterizedTest
47+
@MethodSource
48+
void givenRealMessageContentWhenSendingMessagesTheContentShouldMatchAfterReceivingAndMergingIt(
49+
ByteString messageContent, int expectedNrOfChunks) throws Throwable {
50+
actionsForSender(messageContent, expectedNrOfChunks);
51+
actionsForTheRecipient(messageContent, expectedNrOfChunks);
52+
}
53+
54+
/**
55+
* These are the actions for the recipient. The recipient is already set up and declared the
56+
* capabilities. The actions for the recipient are documented inline.
57+
*
58+
* @param messageContent -
59+
* @param expectedNrOfChunks -
60+
*/
61+
private void actionsForTheRecipient(ByteString messageContent, int expectedNrOfChunks)
62+
throws Throwable {
63+
// [1] Fetch all the messages within the feed. The number of headers should match the number of
64+
// chunks sent.
65+
final MessageQueryServiceImpl messageQueryService = new MessageQueryServiceImpl(new QA() {});
66+
final MessageQueryParameters messageQueryParameters = new MessageQueryParameters();
67+
final OnboardingResponse recipient =
68+
OnboardingResponseRepository.read(
69+
OnboardingResponseRepository.Identifier.COMMUNICATION_UNIT);
70+
messageQueryParameters.setOnboardingResponse(recipient);
71+
messageQueryParameters.setSentToInSeconds(UtcTimeService.inTheFuture(5).toEpochSecond());
72+
messageQueryParameters.setSentFromInSeconds(
73+
UtcTimeService.inThePast(UtcTimeService.FOUR_WEEKS_AGO).toEpochSecond());
74+
messageQueryService.send(messageQueryParameters);
75+
76+
// [2] Wait for the agrirouter to process the message.
77+
waitForTheAgrirouterToProcessSingleMessage();
78+
79+
// [3] Fetch the chunks from the outbox. Since we have the same restrictions while receiving,
80+
// this has to be the same number of messages as it is chunks.
81+
FetchMessageService fetchMessageService = new FetchMessageServiceImpl();
82+
Optional<List<FetchMessageResponse>> fetchMessageResponses =
83+
fetchMessageService.fetch(
84+
recipient, new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL));
85+
Assertions.assertTrue(fetchMessageResponses.isPresent());
86+
Assertions.assertEquals(expectedNrOfChunks, fetchMessageResponses.get().size());
87+
88+
// [4] Check if the response from the AR only contains valid results.
89+
final DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl();
90+
fetchMessageResponses.get().stream()
91+
.map(
92+
fetchMessageResponse ->
93+
decodeMessageService.decode(fetchMessageResponse.getCommand().getMessage()))
94+
.forEach(
95+
decodeMessageResponse ->
96+
Assertions.assertEquals(
97+
Response.ResponseEnvelope.ResponseBodyType.ACK_FOR_FEED_MESSAGE,
98+
decodeMessageResponse.getResponseEnvelope().getType()));
99+
100+
// [5] Map the results from the query to 'real' messages within the feed and perform some
101+
// assertions.
102+
final List<FeedResponse.MessageQueryResponse.FeedMessage> feedMessages =
103+
fetchMessageResponses.get().stream()
104+
.map(
105+
fetchMessageResponse ->
106+
decodeMessageService.decode(fetchMessageResponse.getCommand().getMessage()))
107+
.map(
108+
decodeMessageResponse ->
109+
messageQueryService.decode(
110+
decodeMessageResponse.getResponsePayloadWrapper().getDetails().getValue()))
111+
.map(messageQueryResponse -> messageQueryResponse.getMessages(0))
112+
.collect(Collectors.toList());
113+
Assertions.assertEquals(expectedNrOfChunks, feedMessages.size());
114+
feedMessages.forEach(
115+
feedMessage -> Assertions.assertNotNull(feedMessage.getHeader().getChunkContext()));
116+
Assertions.assertEquals(
117+
feedMessages.get(0).getHeader().getChunkContext().getTotal(), expectedNrOfChunks);
118+
final Set<String> chunkContextIds =
119+
feedMessages.stream()
120+
.map(feedMessage -> feedMessage.getHeader().getChunkContext().getContextId())
121+
.collect(Collectors.toSet());
122+
Assertions.assertEquals(
123+
1, chunkContextIds.size(), "There should be only one chunk context ID.");
124+
125+
// [6] Confirm the chunks to remove them from the feed.
126+
final List<String> messageIdsToConfirm =
127+
feedMessages.stream()
128+
.map(feedMessage -> feedMessage.getHeader().getMessageId())
129+
.collect(Collectors.toList());
130+
final MessageConfirmationServiceImpl messageConfirmationService =
131+
new MessageConfirmationServiceImpl(new QA() {});
132+
final MessageConfirmationParameters messageConfirmationParameters =
133+
new MessageConfirmationParameters();
134+
messageConfirmationParameters.setOnboardingResponse(recipient);
135+
messageConfirmationParameters.setMessageIds(messageIdsToConfirm);
136+
messageConfirmationService.send(messageConfirmationParameters);
137+
138+
// [7] Fetch the response from the agrirouter after confirming the messages.
139+
waitForTheAgrirouterToProcessSingleMessage();
140+
fetchMessageResponses =
141+
fetchMessageService.fetch(
142+
recipient, new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL));
143+
Assertions.assertTrue(fetchMessageResponses.isPresent());
144+
Assertions.assertEquals(
145+
1, fetchMessageResponses.get().size(), "This should be a single response.");
146+
}
147+
148+
/**
149+
* These are the actions for the sender. The sender is already set up and declared the
150+
* capabilities. The actions for the sender are documented inline.
151+
*
152+
* @param messageContent -
153+
* @param expectedNrOfChunks -
154+
* @throws IOException -
155+
* @throws InterruptedException -
156+
*/
157+
private void actionsForSender(ByteString messageContent, int expectedNrOfChunks)
158+
throws IOException, InterruptedException {
159+
final EncodeMessageService encodeMessageService = new EncodeMessageServiceImpl();
160+
final SendMessageServiceImpl sendMessageService = new SendMessageServiceImpl();
161+
final OnboardingResponse onboardingResponse =
162+
OnboardingResponseRepository.read(OnboardingResponseRepository.Identifier.FARMING_SOFTWARE);
163+
164+
// [1] Define the raw message, in this case this is the Base64 encoded message content, no
165+
// chunking needed.
166+
MessageHeaderParameters messageHeaderParameters = new MessageHeaderParameters();
167+
messageHeaderParameters.setTechnicalMessageType(ContentMessageType.ISO_11783_TASKDATA_ZIP);
168+
messageHeaderParameters.setApplicationMessageId(MessageIdService.generateMessageId());
169+
messageHeaderParameters.setApplicationMessageSeqNo(
170+
SequenceNumberService.generateSequenceNumberForEndpoint(onboardingResponse));
171+
messageHeaderParameters.setMode(Request.RequestEnvelope.Mode.DIRECT);
172+
messageHeaderParameters.setRecipients(
173+
Collections.singletonList(
174+
OnboardingResponseRepository.read(
175+
OnboardingResponseRepository.Identifier.COMMUNICATION_UNIT)
176+
.getSensorAlternateId()));
177+
178+
PayloadParameters payloadParameters = new PayloadParameters();
179+
payloadParameters.setValue(messageContent);
180+
payloadParameters.setTypeUrl(SystemMessageType.EMPTY.getKey());
181+
182+
// [2] Chunk the message content using the SDK specific methods ('chunkAndEncode').
183+
List<MessageParameterTuple> tuples =
184+
encodeMessageService.chunkAndBase64EncodeEachChunk(
185+
messageHeaderParameters, payloadParameters, onboardingResponse);
186+
187+
tuples.forEach(
188+
messageParameterTuple ->
189+
Assertions.assertTrue(
190+
Objects.requireNonNull(messageParameterTuple.getPayloadParameters().getValue())
191+
.toStringUtf8()
192+
.length()
193+
<= MAX_CHUNK_SIZE));
194+
195+
List<String> encodedMessages = encodeMessageService.encode(tuples);
196+
197+
// [3] Send the chunks to the agrirouter.
198+
SendMessageParameters sendMessageParameters = new SendMessageParameters();
199+
sendMessageParameters.setEncodedMessages(encodedMessages);
200+
sendMessageParameters.setOnboardingResponse(onboardingResponse);
201+
sendMessageService.send(sendMessageParameters);
202+
203+
// [4] Wait for the AR to process the chunks.
204+
waitForTheAgrirouterToProcessMultipleMessages();
205+
206+
// [5] Check if the chunks were processed successfully.
207+
FetchMessageService fetchMessageService = new FetchMessageServiceImpl();
208+
Optional<List<FetchMessageResponse>> fetchMessageResponses =
209+
fetchMessageService.fetch(
210+
onboardingResponse,
211+
new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL));
212+
213+
Assertions.assertTrue(fetchMessageResponses.isPresent());
214+
Assertions.assertEquals(expectedNrOfChunks, fetchMessageResponses.get().size());
215+
216+
DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl();
217+
AtomicReference<DecodeMessageResponse> decodeMessageResponse = new AtomicReference<>();
218+
fetchMessageResponses.get().stream()
219+
.map(FetchMessageResponse::getCommand)
220+
.forEach(
221+
message -> {
222+
Assertions.assertNotNull(message);
223+
decodeMessageResponse.set(decodeMessageService.decode(message.getMessage()));
224+
225+
Assertions.assertMatchesAny(
226+
Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_CREATED, HttpStatus.SC_NO_CONTENT),
227+
decodeMessageResponse.get().getResponseEnvelope().getResponseCode());
228+
});
229+
}
230+
231+
/**
232+
* Delivers fake message content for multiple test cases.
233+
*
234+
* @return -
235+
*/
236+
@SuppressWarnings("unused")
237+
private static @NotNull Stream<Arguments> givenRealMessageContentWhenSendingMessagesTheContentShouldMatchAfterReceivingAndMergingIt() throws Throwable {
238+
return Stream.of(
239+
Arguments.of(
240+
ByteString.copyFrom(
241+
ContentReader.readRawData(ContentReader.Identifier.BIG_TASK_DATA)),
242+
3));
243+
}
244+
245+
/**
246+
* Cleanup before and after each test case. These actions are necessary because it could be the
247+
* case, that there are dangling messages from former tests.
248+
*/
249+
@BeforeEach
250+
@AfterEach
251+
public void prepareTestEnvironment() throws Throwable {
252+
FetchMessageService fetchMessageService = new FetchMessageServiceImpl();
253+
final OnboardingResponse recipient =
254+
OnboardingResponseRepository.read(
255+
OnboardingResponseRepository.Identifier.COMMUNICATION_UNIT);
256+
final MessageHeaderQueryServiceImpl messageHeaderQueryService =
257+
new MessageHeaderQueryServiceImpl(new QA() {});
258+
259+
// [1] Clean the outbox of the endpoint.
260+
fetchMessageService.fetch(
261+
recipient, new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL));
262+
263+
// [2] Fetch all message headers for the last 4 weeks (maximum retention time within the
264+
// agrirouter).
265+
final MessageQueryParameters messageQueryParameters = new MessageQueryParameters();
266+
messageQueryParameters.setOnboardingResponse(recipient);
267+
messageQueryParameters.setSentToInSeconds(UtcTimeService.inTheFuture(5).toEpochSecond());
268+
messageQueryParameters.setSentFromInSeconds(
269+
UtcTimeService.inThePast(UtcTimeService.FOUR_WEEKS_AGO).toEpochSecond());
270+
messageHeaderQueryService.send(messageQueryParameters);
271+
waitForTheAgrirouterToProcessSingleMessage();
272+
Optional<List<FetchMessageResponse>> fetchMessageResponses =
273+
fetchMessageService.fetch(
274+
recipient, new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL));
275+
Assertions.assertTrue(fetchMessageResponses.isPresent());
276+
Assertions.assertEquals(
277+
1, fetchMessageResponses.get().size(), "This should be a single response.");
278+
final DecodeMessageService decodeMessageService = new DecodeMessageServiceImpl();
279+
final DecodeMessageResponse decodeMessageResponse =
280+
decodeMessageService.decode(fetchMessageResponses.get().get(0).getCommand().getMessage());
281+
Assertions.assertEquals(
282+
Response.ResponseEnvelope.ResponseBodyType.ACK_FOR_FEED_HEADER_LIST,
283+
decodeMessageResponse.getResponseEnvelope().getType());
284+
final FeedResponse.HeaderQueryResponse headerQueryResponse =
285+
messageHeaderQueryService.decode(
286+
decodeMessageResponse.getResponsePayloadWrapper().getDetails().getValue());
287+
288+
// [3] Delete the dangling messages from the feed of the endpoint if necessary.
289+
if (headerQueryResponse.getQueryMetrics().getTotalMessagesInQuery() > 0) {
290+
final DeleteMessageServiceImpl deleteMessageService = new DeleteMessageServiceImpl();
291+
final DeleteMessageParameters deleteMessageParameters = new DeleteMessageParameters();
292+
deleteMessageParameters.setOnboardingResponse(recipient);
293+
final List<String> messageIds =
294+
headerQueryResponse.getFeedList().stream()
295+
.map(FeedResponse.HeaderQueryResponse.Feed::getHeadersList)
296+
.flatMap(Collection::stream)
297+
.map(FeedResponse.HeaderQueryResponse.Header::getMessageId)
298+
.collect(Collectors.toList());
299+
deleteMessageParameters.setMessageIds(messageIds);
300+
deleteMessageService.send(deleteMessageParameters);
301+
waitForTheAgrirouterToProcessSingleMessage();
302+
fetchMessageService.fetch(
303+
recipient, new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL));
304+
}
305+
306+
// [4] Clean the outbox of the endpoint.
307+
fetchMessageService.fetch(
308+
recipient, new DefaultCancellationToken(MAX_TRIES_BEFORE_FAILURE, DEFAULT_INTERVAL));
309+
}
310+
}

0 commit comments

Comments
 (0)