/* * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cloudfoundry.reactor.util; import com.fasterxml.jackson.databind.ObjectMapper; import org.cloudfoundry.UnknownCloudFoundryException; import org.cloudfoundry.client.v2.ClientV2Exception; import org.cloudfoundry.client.v3.ClientV3Exception; import org.cloudfoundry.uaa.UaaException; import org.junit.Test; import org.springframework.core.io.ClassPathResource; import reactor.core.publisher.Mono; import reactor.ipc.netty.ByteBufFlux; import reactor.ipc.netty.http.client.HttpClientResponse; import reactor.test.StepVerifier; import java.io.IOException; import java.time.Duration; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.RETURNS_SMART_NULLS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public final class ErrorPayloadMapperTest { private final ObjectMapper objectMapper = new ObjectMapper(); private final HttpClientResponse response = mock(HttpClientResponse.class, RETURNS_SMART_NULLS); @Test public void clientV2BadPayload() throws IOException { when(this.response.status()).thenReturn(BAD_REQUEST); when(this.response.receive()).thenReturn(ByteBufFlux.fromPath(new ClassPathResource("fixtures/invalid_error_response.json").getFile().toPath())); Mono.just(this.response) .transform(ErrorPayloadMapper.clientV2(this.objectMapper)) .as(StepVerifier::create) .consumeErrorWith(t -> assertThat(t) .isInstanceOf(UnknownCloudFoundryException.class) .hasMessage("Unknown Cloud Foundry Exception") .extracting("statusCode", "payload") .containsExactly(BAD_REQUEST.code(), "Invalid Error Response")) .verify(Duration.ofSeconds(1)); } @Test public void clientV2ClientError() throws IOException { when(this.response.status()).thenReturn(BAD_REQUEST); when(this.response.receive()).thenReturn(ByteBufFlux.fromPath(new ClassPathResource("fixtures/client/v2/error_response.json").getFile().toPath())); Mono.just(this.response) .transform(ErrorPayloadMapper.clientV2(this.objectMapper)) .as(StepVerifier::create) .consumeErrorWith(t -> assertThat(t) .isInstanceOf(ClientV2Exception.class) .hasMessage("CF-UnprocessableEntity(10008): The request is semantically invalid: space_guid and name unique") .extracting("statusCode", "code", "description", "errorCode") .containsExactly(BAD_REQUEST.code(), 10008, "The request is semantically invalid: space_guid and name unique", "CF-UnprocessableEntity")) .verify(Duration.ofSeconds(1)); } @Test public void clientV2NoError() { when(this.response.status()).thenReturn(OK); Mono.just(this.response) .transform(ErrorPayloadMapper.clientV2(this.objectMapper)) .as(StepVerifier::create) .expectNext(this.response) .expectComplete() .verify(Duration.ofSeconds(1)); } @Test public void clientV2ServerError() throws IOException { when(this.response.status()).thenReturn(INTERNAL_SERVER_ERROR); when(this.response.receive()).thenReturn(ByteBufFlux.fromPath(new ClassPathResource("fixtures/client/v2/error_response.json").getFile().toPath())); Mono.just(this.response) .transform(ErrorPayloadMapper.clientV2(this.objectMapper)) .as(StepVerifier::create) .consumeErrorWith(t -> assertThat(t) .isInstanceOf(ClientV2Exception.class) .hasMessage("CF-UnprocessableEntity(10008): The request is semantically invalid: space_guid and name unique") .extracting("statusCode", "code", "description", "errorCode") .containsExactly(INTERNAL_SERVER_ERROR.code(), 10008, "The request is semantically invalid: space_guid and name unique", "CF-UnprocessableEntity")) .verify(Duration.ofSeconds(1)); } @Test public void clientV3BadPayload() throws IOException { when(this.response.status()).thenReturn(BAD_REQUEST); when(this.response.receive()).thenReturn(ByteBufFlux.fromPath(new ClassPathResource("fixtures/invalid_error_response.json").getFile().toPath())); Mono.just(this.response) .transform(ErrorPayloadMapper.clientV3(this.objectMapper)) .as(StepVerifier::create) .consumeErrorWith(t -> assertThat(t) .isInstanceOf(UnknownCloudFoundryException.class) .hasMessage("Unknown Cloud Foundry Exception") .extracting("statusCode", "payload") .containsExactly(BAD_REQUEST.code(), "Invalid Error Response")) .verify(Duration.ofSeconds(1)); } @Test public void clientV3ClientError() throws IOException { when(this.response.status()).thenReturn(BAD_REQUEST); when(this.response.receive()).thenReturn(ByteBufFlux.fromPath(new ClassPathResource("fixtures/client/v3/error_response.json").getFile().toPath())); Mono.just(this.response) .transform(ErrorPayloadMapper.clientV3(this.objectMapper)) .as(StepVerifier::create) .consumeErrorWith(t -> { assertThat(t) .isInstanceOf(ClientV3Exception.class) .hasMessage("CF-UnprocessableEntity(10008): The request is semantically invalid: something went wrong") .extracting("statusCode") .containsExactly(BAD_REQUEST.code()); assertThat(((ClientV3Exception) t).getErrors()) .flatExtracting(ClientV3Exception.Error::getCode, ClientV3Exception.Error::getDetail, ClientV3Exception.Error::getTitle) .containsExactly(10008, "The request is semantically invalid: something went wrong", "CF-UnprocessableEntity"); }) .verify(Duration.ofSeconds(1)); } @Test public void clientV3NoError() { when(this.response.status()).thenReturn(OK); Mono.just(this.response) .transform(ErrorPayloadMapper.clientV3(this.objectMapper)) .as(StepVerifier::create) .expectNext(this.response) .expectComplete() .verify(Duration.ofSeconds(1)); } @Test public void clientV3ServerError() throws IOException { when(this.response.status()).thenReturn(INTERNAL_SERVER_ERROR); when(this.response.receive()).thenReturn(ByteBufFlux.fromPath(new ClassPathResource("fixtures/client/v3/error_response.json").getFile().toPath())); Mono.just(this.response) .transform(ErrorPayloadMapper.clientV3(this.objectMapper)) .as(StepVerifier::create) .consumeErrorWith(t -> { assertThat(t) .isInstanceOf(ClientV3Exception.class) .hasMessage("CF-UnprocessableEntity(10008): The request is semantically invalid: something went wrong") .extracting("statusCode") .containsExactly(INTERNAL_SERVER_ERROR.code()); assertThat(((ClientV3Exception) t).getErrors()) .flatExtracting(ClientV3Exception.Error::getCode, ClientV3Exception.Error::getDetail, ClientV3Exception.Error::getTitle) .containsExactly(10008, "The request is semantically invalid: something went wrong", "CF-UnprocessableEntity"); }) .verify(Duration.ofSeconds(1)); } @Test public void uaaBadPayload() throws IOException { when(this.response.status()).thenReturn(BAD_REQUEST); when(this.response.receive()).thenReturn(ByteBufFlux.fromPath(new ClassPathResource("fixtures/invalid_error_response.json").getFile().toPath())); Mono.just(this.response) .transform(ErrorPayloadMapper.uaa(this.objectMapper)) .as(StepVerifier::create) .consumeErrorWith(t -> assertThat(t) .isInstanceOf(UnknownCloudFoundryException.class) .hasMessage("Unknown Cloud Foundry Exception") .extracting("statusCode", "payload") .containsExactly(BAD_REQUEST.code(), "Invalid Error Response")) .verify(Duration.ofSeconds(1)); } @Test public void uaaClientError() throws IOException { when(this.response.status()).thenReturn(BAD_REQUEST); when(this.response.receive()).thenReturn(ByteBufFlux.fromPath(new ClassPathResource("fixtures/uaa/error_response.json").getFile().toPath())); Mono.just(this.response) .transform(ErrorPayloadMapper.uaa(this.objectMapper)) .as(StepVerifier::create) .consumeErrorWith(t -> assertThat(t) .isInstanceOf(UaaException.class) .hasMessage("unauthorized: Bad credentials") .extracting("statusCode", "error", "errorDescription") .containsExactly(BAD_REQUEST.code(), "unauthorized", "Bad credentials")) .verify(Duration.ofSeconds(1)); } @Test public void uaaNoError() { when(this.response.status()).thenReturn(OK); Mono.just(this.response) .transform(ErrorPayloadMapper.uaa(this.objectMapper)) .as(StepVerifier::create) .expectNext(this.response) .expectComplete() .verify(Duration.ofSeconds(1)); } @Test public void uaaServerError() throws IOException { when(this.response.status()).thenReturn(INTERNAL_SERVER_ERROR); when(this.response.receive()).thenReturn(ByteBufFlux.fromPath(new ClassPathResource("fixtures/uaa/error_response.json").getFile().toPath())); Mono.just(this.response) .transform(ErrorPayloadMapper.uaa(this.objectMapper)) .as(StepVerifier::create) .consumeErrorWith(t -> assertThat(t) .isInstanceOf(UaaException.class) .hasMessage("unauthorized: Bad credentials") .extracting("statusCode", "error", "errorDescription") .containsExactly(INTERNAL_SERVER_ERROR.code(), "unauthorized", "Bad credentials")) .verify(Duration.ofSeconds(1)); } }