0

I am experiencing a problem doing an integration test on a responsive endpoint using spring and Flux. Specifically, it is a simple chat mechanism. I have one endpoint that exposes a Flux object of Messages and a second endpoint that, via post, publishes the message on the flux object. Below are the service class and controller

package com.giane.reactiveangularchat.core.usecases;

import com.giane.reactiveangularchat.core.entities.Message;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;

@Service
public class MessageService {
    private final Sinks.Many<Message> sink;

    public MessageService() {
        sink = Sinks.many().multicast().directAllOrNothing();
    }


    public void save(Message message) {
        sink.emitNext(message, Sinks.EmitFailureHandler.FAIL_FAST);
        sink.tryEmitNext(message);
    }

    /**
     * This method return a Flux of ServerSentEvent. The ServerSentEvent is a reactive stream filtered by user.
     * @param user the user to filter the stream
     * @return a Flux of ServerSentEvent
     */
    public Flux<ServerSentEvent<Message>> getMessage(String project) {
        return sink.asFlux()
            .filter(message -> message.getTo().equals(project))
            .map(message -> ServerSentEvent.builder(message).build());
    }
}
package com.giane.reactiveangularchat.entrypoints.rest;

import com.giane.reactiveangularchat.core.entities.Message;
import com.giane.reactiveangularchat.core.usecases.MessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.net.URI;

@RestController
@RequestMapping("/api/messages")
@CrossOrigin(originPatterns = "*", allowedHeaders = "*")
@Slf4j
public class MessageController {
    private final MessageService messageService;

    public MessageController(MessageService messageService) {
        this.messageService = messageService;
    }

    @PostMapping
    public ResponseEntity<Message> postMessage(@RequestBody Message message) {
        log.info("Received message {}", message);
        messageService.save(message);
        return ResponseEntity.created(URI.create("")).body(message);
    }

    @GetMapping(path = "/stream/{user}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<Message>> getMessages(@PathVariable String user) {
        log.info("Received request for user {}", user);
        return messageService.getMessage(user);
    }
}

My test should involve the following flow.

  1. Connection to the flux endpoint.
  2. Sending the message
  3. Checking the received message on the stream

The following is the test method.

@Test
    void contextLoads3() throws InterruptedException {

        FluxExchangeResult<Message> fluxFluxExchangeResult = webTestClient.get()
            .uri("/api/messages/stream/user1").exchange().expectStatus().is2xxSuccessful().returnResult(Message.class);
        Flux<Message> messages = fluxFluxExchangeResult.getResponseBody();
        
        webTestClient.post().uri("/api/messages").bodyValue(new Message("message", "user1")).exchange().expectStatus().is2xxSuccessful();



        StepVerifier
            .create(messages)
            .expectSubscription()
            .assertNext(it->{
                assertThat(it).isNotNull();
                assertThat(it.getMessageContent()).isEqualTo("message");
                assertThat(it.getTo()).isEqualTo("user1");
            })
            .thenCancel()
            .verify(Duration.ofSeconds(10));
    }

My problem now is that webtestclient's exchange method is blocked until the first event is received. But I cannot send the first event until the exchange method unlocks.

Can someone explain how I can test this use case?

Giane
  • 85
  • 1
  • 9

0 Answers0