.expectNext(bigMessage) .verifyComplete());
@Test public void writeServerSentEvent() { ServerSentEvent<?> event = ServerSentEvent.builder().data("bar").id("c42").event("foo") .comment("bla\nbla bla\nbla bla bla").retry(Duration.ofMillis(123L)).build(); Mono<ServerSentEvent> source = Mono.just(event); testWrite(source, outputMessage, ServerSentEvent.class); StepVerifier.create(outputMessage.getBody()) .consumeNextWith(stringConsumer("id:c42\nevent:foo\nretry:123\n:bla\n:bla bla\n:bla bla bla\ndata:")) .consumeNextWith(stringConsumer("bar\n")) .consumeNextWith(stringConsumer("\n")) .expectComplete() .verify(); }
/** * Test a {@link Encoder#encode encode} scenario where the input stream is canceled. * This test method will feed the first element of the {@code input} stream to the decoder, * followed by a cancel signal. * The result is expected to contain one "normal" element. * * @param input the input to be provided to the encoder * @param inputType the input type * @param mimeType the mime type to use for decoding. May be {@code null}. * @param hints the hints used for decoding. May be {@code null}. */ protected void testEncodeCancel(Publisher<?> input, ResolvableType inputType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { Flux<DataBuffer> result = encoder().encode(input, this.bufferFactory, inputType, mimeType, hints); StepVerifier.create(result) .consumeNextWith(DataBufferUtils::release) .thenCancel() .verify(); }
@Test public void decodeSplitChunks() { Flux<DataBuffer> input = Flux.just(this.testMsg1, this.testMsg2) .flatMap(msg -> Mono.defer(() -> { DataBuffer buffer = this.bufferFactory.allocateBuffer(); try { msg.writeDelimitedTo(buffer.asOutputStream()); return Mono.just(buffer); } catch (IOException e) { release(buffer); return Mono.error(e); } })) .flatMap(buffer -> { int len = buffer.readableByteCount() / 2; Flux<DataBuffer> result = Flux.just( DataBufferUtils.retain(buffer.slice(0, len)), DataBufferUtils .retain(buffer.slice(len, buffer.readableByteCount() - len)) ); release(buffer); return result; }); testDecode(input, Msg.class, step -> step .expectNext(this.testMsg1) .expectNext(this.testMsg2) .verifyComplete()); }
@Test public void decodeNewLine() { Flux<DataBuffer> input = Flux.just( stringBuffer("\r\nabc\n"), stringBuffer("def"), stringBuffer("ghi\r\n\n"), stringBuffer("jkl"), stringBuffer("mno\npqr\n"), stringBuffer("stu"), stringBuffer("vw"), stringBuffer("xyz") ); testDecode(input, String.class, step -> step .expectNext("") .expectNext("abc") .expectNext("defghi") .expectNext("") .expectNext("jklmno") .expectNext("pqr") .expectNext("stuvwxyz") .expectComplete() .verify()); }
@Test public void decodeNewLineIncludeDelimiters() { this.decoder = StringDecoder.allMimeTypes(StringDecoder.DEFAULT_DELIMITERS, false); Flux<DataBuffer> input = Flux.just( stringBuffer("\r\nabc\n"), stringBuffer("def"), stringBuffer("ghi\r\n\n"), stringBuffer("jkl"), stringBuffer("mno\npqr\n"), stringBuffer("stu"), stringBuffer("vw"), stringBuffer("xyz") ); testDecode(input, String.class, step -> step .expectNext("\r\n") .expectNext("abc\n") .expectNext("defghi\r\n") .expectNext("\n") .expectNext("jklmno\n") .expectNext("pqr\n") .expectNext("stuvwxyz") .expectComplete() .verify()); }
@Override @Test public void decode() { Flux<DataBuffer> input = Flux.just(this.testMsg1, this.testMsg2) .flatMap(msg -> Mono.defer(() -> { DataBuffer buffer = this.bufferFactory.allocateBuffer(); try { msg.writeDelimitedTo(buffer.asOutputStream()); return Mono.just(buffer); } catch (IOException e) { release(buffer); return Mono.error(e); } })); testDecodeAll(input, Msg.class, step -> step .expectNext(this.testMsg1) .expectNext(this.testMsg2) .verifyComplete()); }
/** * Test a {@link Decoder#decode decode} scenario where the input stream is canceled. * This test method will feed the first element of the {@code input} stream to the decoder, * followed by a cancel signal. * The result is expected to contain one "normal" element. * * @param input the input to be provided to the decoder * @param outputType the desired output type * @param mimeType the mime type to use for decoding. May be {@code null}. * @param hints the hints used for decoding. May be {@code null}. */ protected void testDecodeCancel(Publisher<DataBuffer> input, ResolvableType outputType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { Flux<?> result = this.decoder.decode(input, outputType, mimeType, hints); StepVerifier.create(result) .expectNextCount(1) .thenCancel() .verify(); }
@Test public void nonExisting() { Resource resource = new ClassPathResource("ResourceRegionEncoderTests.txt", getClass()); Resource nonExisting = new ClassPathResource("does not exist", getClass()); Flux<ResourceRegion> regions = Flux.just( new ResourceRegion(resource, 0, 6), new ResourceRegion(nonExisting, 0, 6)); String boundary = MimeTypeUtils.generateMultipartBoundaryString(); Flux<DataBuffer> result = this.encoder.encode(regions, this.bufferFactory, ResolvableType.forClass(ResourceRegion.class), MimeType.valueOf("text/plain"), Collections.singletonMap(ResourceRegionEncoder.BOUNDARY_STRING_HINT, boundary)); StepVerifier.create(result) .consumeNextWith(stringConsumer("\r\n--" + boundary + "\r\n")) .consumeNextWith(stringConsumer("Content-Type: text/plain\r\n")) .consumeNextWith(stringConsumer("Content-Range: bytes 0-5/39\r\n\r\n")) .consumeNextWith(stringConsumer("Spring")) .expectError(EncodingException.class) .verify(); }
private void verifyPersonEvents(Flux<ServerSentEvent<Person>> result) { StepVerifier.create(result) .consumeNextWith( event -> { assertEquals("0", event.id()); assertEquals(new Person("foo 0"), event.data()); assertEquals("bar 0", event.comment()); assertNull(event.event()); assertNull(event.retry()); }) .consumeNextWith( event -> { assertEquals("1", event.id()); assertEquals(new Person("foo 1"), event.data()); assertEquals("bar 1", event.comment()); assertNull(event.event()); assertNull(event.retry()); }) .thenCancel() .verify(Duration.ofSeconds(5L)); }
@Test public void writeAsynchronousFileChannelErrorInFlux() throws Exception { DataBuffer foo = stringBuffer("foo"); DataBuffer bar = stringBuffer("bar"); Flux<DataBuffer> flux = Flux.just(foo, bar).concatWith(Mono.error(new RuntimeException())); AsynchronousFileChannel channel = AsynchronousFileChannel.open(tempFile, StandardOpenOption.WRITE); Flux<DataBuffer> writeResult = DataBufferUtils.write(flux, channel); StepVerifier.create(writeResult) .consumeNextWith(stringConsumer("foo")) .consumeNextWith(stringConsumer("bar")) .expectError(RuntimeException.class) .verify(); String result = String.join("", Files.readAllLines(tempFile)); assertEquals("foobar", result); channel.close(); }
@Test public void readByteChannelError() throws Exception { ReadableByteChannel channel = mock(ReadableByteChannel.class); when(channel.read(any())) .thenAnswer(invocation -> { ByteBuffer buffer = invocation.getArgument(0); buffer.put("foo".getBytes(StandardCharsets.UTF_8)); buffer.flip(); return 3; }) .thenThrow(new IOException()); Flux<DataBuffer> result = DataBufferUtils.readByteChannel(() -> channel, this.bufferFactory, 3); StepVerifier.create(result) .consumeNextWith(stringConsumer("foo")) .expectError(IOException.class) .verify(Duration.ofSeconds(3)); }
@Test public void writeAsynchronousFileChannelCanceled() throws Exception { DataBuffer foo = stringBuffer("foo"); DataBuffer bar = stringBuffer("bar"); Flux<DataBuffer> flux = Flux.just(foo, bar); AsynchronousFileChannel channel = AsynchronousFileChannel.open(tempFile, StandardOpenOption.WRITE); Flux<DataBuffer> writeResult = DataBufferUtils.write(flux, channel); StepVerifier.create(writeResult, 1) .consumeNextWith(stringConsumer("foo")) .thenCancel() .verify(); String result = String.join("", Files.readAllLines(tempFile)); assertEquals("foo", result); channel.close(); flux.subscribe(DataBufferUtils::release); }
@Test public void values() { Flux<Msg> result = this.webClient.get() .uri("/messages") .exchange() .doOnNext(response -> { Assert.assertEquals("true", response.headers().contentType().get().getParameters().get("delimited")); Assert.assertEquals("sample.proto", response.headers().header("X-Protobuf-Schema").get(0)); Assert.assertEquals("Msg", response.headers().header("X-Protobuf-Message").get(0)); }) .flatMapMany(response -> response.bodyToFlux(Msg.class)); StepVerifier.create(result) .expectNext(TEST_MSG) .expectNext(TEST_MSG) .expectNext(TEST_MSG) .verifyComplete(); }
@Test // SPR-16494 @Ignore // https://github.com/reactor/reactor-netty/issues/283 public void serverDetectsClientDisconnect() { assumeTrue(this.server instanceof ReactorHttpServer); Flux<String> result = this.webClient.get() .uri("/infinite") .accept(TEXT_EVENT_STREAM) .retrieve() .bodyToFlux(String.class); StepVerifier.create(result) .expectNext("foo 0") .expectNext("foo 1") .thenCancel() .verify(Duration.ofSeconds(5L)); SseController controller = this.wac.getBean(SseController.class); controller.cancellation.block(Duration.ofSeconds(5)); }
@Test public void streaming() { Flux<Msg> result = this.webClient.get() .uri("/message-stream") .exchange() .doOnNext(response -> { Assert.assertEquals("true", response.headers().contentType().get().getParameters().get("delimited")); Assert.assertEquals("sample.proto", response.headers().header("X-Protobuf-Schema").get(0)); Assert.assertEquals("Msg", response.headers().header("X-Protobuf-Message").get(0)); }) .flatMapMany(response -> response.bodyToFlux(Msg.class)); StepVerifier.create(result) .expectNext(Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(0).build()).build()) .expectNext(Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(1).build()).build()) .thenCancel() .verify(); }
@Test public void writeWritableByteChannelCancel() throws Exception { DataBuffer foo = stringBuffer("foo"); DataBuffer bar = stringBuffer("bar"); Flux<DataBuffer> flux = Flux.just(foo, bar); WritableByteChannel channel = Files.newByteChannel(tempFile, StandardOpenOption.WRITE); Flux<DataBuffer> writeResult = DataBufferUtils.write(flux, channel); StepVerifier.create(writeResult, 1) .consumeNextWith(stringConsumer("foo")) .thenCancel() .verify(Duration.ofSeconds(5)); String result = String.join("", Files.readAllLines(tempFile)); assertEquals("foo", result); channel.close(); flux.subscribe(DataBufferUtils::release); }
@Test public void writeWritableByteChannelErrorInFlux() throws Exception { DataBuffer foo = stringBuffer("foo"); DataBuffer bar = stringBuffer("bar"); Flux<DataBuffer> flux = Flux.just(foo, bar).concatWith(Flux.error(new RuntimeException())); WritableByteChannel channel = Files.newByteChannel(tempFile, StandardOpenOption.WRITE); Flux<DataBuffer> writeResult = DataBufferUtils.write(flux, channel); StepVerifier.create(writeResult) .consumeNextWith(stringConsumer("foo")) .consumeNextWith(stringConsumer("bar")) .expectError() .verify(Duration.ofSeconds(5)); String result = String.join("", Files.readAllLines(tempFile)); assertEquals("foobar", result); channel.close(); }
@Test public void emptyBodyWithObservable() throws Exception { MethodParameter param = this.testMethod.annot(requestBody()).arg(Observable.class, String.class); Observable<String> observable = resolveValueWithEmptyBody(param); StepVerifier.create(RxReactiveStreams.toPublisher(observable)) .expectNextCount(0) .expectError(ServerWebInputException.class) .verify(); param = this.testMethod.annot(requestBody().notRequired()).arg(Observable.class, String.class); observable = resolveValueWithEmptyBody(param); StepVerifier.create(RxReactiveStreams.toPublisher(observable)) .expectNextCount(0) .expectComplete() .verify(); }
@Test public void emptyBodyWithMaybe() throws Exception { MethodParameter param = this.testMethod.annot(requestBody()).arg(Maybe.class, String.class); Maybe<String> maybe = resolveValueWithEmptyBody(param); StepVerifier.create(maybe.toFlowable()) .expectNextCount(0) .expectError(ServerWebInputException.class) .verify(); param = this.testMethod.annot(requestBody().notRequired()).arg(Maybe.class, String.class); maybe = resolveValueWithEmptyBody(param); StepVerifier.create(maybe.toFlowable()) .expectNextCount(0) .expectComplete() .verify(); }