org.zalando.problem.ProblemMixInTest.java Source code

Java tutorial

Introduction

Here is the source code for org.zalando.problem.ProblemMixInTest.java

Source

package org.zalando.problem;

/*
 * ?
 * Jackson-datatype-Problem
 * ??
 * Copyright (C) 2015 Zalando SE
 * ??
 * 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.
 * ?
 */

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import org.junit.Test;

import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.Response.Status.Family;
import javax.ws.rs.core.Response.StatusType;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.net.URL;
import java.util.List;
import java.util.Optional;
import java.util.Objects;

import static com.jayway.jsonassert.JsonAssert.with;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.hasToString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.hobsoft.hamcrest.compose.ComposeMatchers.hasFeature;
import static org.junit.Assert.assertThat;
import static org.zalando.problem.MoreStatus.UNPROCESSABLE_ENTITY;

public final class ProblemMixInTest {

    private final ObjectMapper mapper = new ObjectMapper().registerModule(new Jdk8Module())
            .registerModule(new ProblemModule());

    public ProblemMixInTest() {
        mapper.registerSubtypes(InsufficientFundsProblem.class);
    }

    @Test
    public void shouldSerializeDefaultProblem() throws JsonProcessingException {
        final Problem problem = Problem.valueOf(Status.NOT_FOUND);
        final String json = mapper.writeValueAsString(problem);

        with(json).assertThat("$.*", hasSize(2)).assertThat("$.title", is("Not Found")).assertThat("$.status",
                is(404));
    }

    @Test
    public void shouldSerializeCustomProperties() throws JsonProcessingException {
        final Problem problem = Problem.builder().withType(URI.create("https://example.org/out-of-stock"))
                .withTitle("Out of Stock").withStatus(UNPROCESSABLE_ENTITY)
                .withDetail("Item B00027Y5QG is no longer available").with("product", "B00027Y5QG").build();

        final String json = mapper.writeValueAsString(problem);

        with(json).assertThat("$.*", hasSize(5)).assertThat("$.product", is("B00027Y5QG"));
    }

    @Test
    public void shouldSerializeProblemCause() throws JsonProcessingException {
        final Problem problem = Problem.builder()
                .withType(URI.create("https://example.org/preauthorization-failed"))
                .withTitle("Preauthorization Failed").withStatus(UNPROCESSABLE_ENTITY)
                .withCause(Problem.builder().withType(URI.create("https://example.org/expired-credit-card"))
                        .withTitle("Expired Credit Card").withStatus(UNPROCESSABLE_ENTITY)
                        .withDetail("Credit card is expired as of 2015-09-16T00:00:00Z")
                        .with("since", "2015-09-16T00:00:00Z").build())
                .build();

        final String json = mapper.writeValueAsString(problem);

        with(json).assertThat("$.cause.type", is("https://example.org/expired-credit-card"))
                .assertThat("$.cause.title", is("Expired Credit Card")).assertThat("$.cause.status", is(422))
                .assertThat("$.cause.detail", is("Credit card is expired as of 2015-09-16T00:00:00Z"))
                .assertThat("$.cause.since", is("2015-09-16T00:00:00Z"));
    }

    @Test
    public void shouldNotSerializeStacktraceByDefault() throws JsonProcessingException {
        final Problem problem = Problem.builder().withType(URI.create("about:blank")).withTitle("Foo")
                .withStatus(Status.BAD_REQUEST).withCause(Problem.builder().withType(URI.create("about:blank"))
                        .withTitle("Bar").withStatus(Status.BAD_REQUEST).build())
                .build();

        final String json = mapper.writeValueAsString(problem);

        with(json).assertNotDefined("$.stacktrace");
    }

    @Test
    public void shouldSerializeStacktrace() throws JsonProcessingException {
        final Problem problem = Problem.builder().withType(URI.create("about:blank")).withTitle("Foo")
                .withStatus(Status.BAD_REQUEST).withCause(Problem.builder().withType(URI.create("about:blank"))
                        .withTitle("Bar").withStatus(Status.BAD_REQUEST).build())
                .build();

        final ObjectMapper mapper = new ObjectMapper().registerModule(new Jdk8Module())
                .registerModule(new ProblemModule().withStacktraces());

        final String json = mapper.writeValueAsString(problem);

        with(json).assertThat("$.stacktrace", is(instanceOf(List.class))).assertThat("$.stacktrace[0]",
                is(instanceOf(String.class)));
    }

    @Test
    public void shouldDeserializeDefaultProblem() throws IOException {
        final URL resource = getResource("out-of-stock.json");
        final Problem raw = mapper.readValue(resource, Problem.class);

        assertThat(raw, instanceOf(DefaultProblem.class));
        final DefaultProblem problem = (DefaultProblem) raw;

        assertThat(problem, hasFeature("type", Problem::getType, hasToString("https://example.org/out-of-stock")));
        assertThat(problem, hasFeature("title", Problem::getTitle, equalTo("Out of Stock")));
        assertThat(problem, hasFeature("status", Problem::getStatus, equalTo(UNPROCESSABLE_ENTITY)));
        assertThat(problem, hasFeature("detail", Problem::getDetail,
                equalTo(Optional.of("Item B00027Y5QG is no longer available"))));
        assertThat(problem,
                hasFeature("parameters", DefaultProblem::getParameters, hasEntry("product", "B00027Y5QG")));
    }

    @Test
    public void shouldDeserializeExceptional() throws IOException {
        final URL resource = getResource("out-of-stock.json");
        final Exceptional exceptional = mapper.readValue(resource, Exceptional.class);

        assertThat(exceptional, instanceOf(DefaultProblem.class));
        final DefaultProblem problem = (DefaultProblem) exceptional;

        assertThat(problem, hasFeature("type", Problem::getType, hasToString("https://example.org/out-of-stock")));
        assertThat(problem, hasFeature("title", Problem::getTitle, equalTo("Out of Stock")));
        assertThat(problem, hasFeature("status", Problem::getStatus, equalTo(UNPROCESSABLE_ENTITY)));
        assertThat(problem, hasFeature("detail", Problem::getDetail,
                equalTo(Optional.of("Item B00027Y5QG is no longer available"))));
        assertThat(problem,
                hasFeature("parameters", DefaultProblem::getParameters, hasEntry("product", "B00027Y5QG")));
    }

    @Test
    public void shouldDeserializeSpecificProblem() throws IOException {
        final URL resource = getResource("insufficient-funds.json");
        final Problem problem = mapper.readValue(resource, Problem.class);

        assertThat(problem, instanceOf(InsufficientFundsProblem.class));

        final InsufficientFundsProblem insufficientFundsProblem = InsufficientFundsProblem.class.cast(problem);
        assertThat(insufficientFundsProblem,
                hasFeature("balance", InsufficientFundsProblem::getBalance, equalTo(10)));
        assertThat(insufficientFundsProblem, hasFeature("debit", InsufficientFundsProblem::getDebit, equalTo(-20)));
    }

    @Test
    public void shouldDeserializeUnknownStatus() throws IOException {
        final URL resource = getResource("unknown.json");
        final Problem problem = mapper.readValue(resource, Problem.class);

        final StatusType status = problem.getStatus();

        assertThat(status, hasFeature("status code", StatusType::getStatusCode, equalTo(666)));
        assertThat(status, hasFeature("family", StatusType::getFamily, equalTo(Family.OTHER)));
        assertThat(status, hasFeature("reason phrase", StatusType::getReasonPhrase, equalTo("Unknown")));
    }

    @Test
    public void shouldDeserializeCause() throws IOException {
        final URL resource = getResource("cause.json");
        final ThrowableProblem problem = mapper.readValue(resource, ThrowableProblem.class);

        assertThat(problem, hasFeature("cause", Throwable::getCause, is(notNullValue())));
        final ThrowableProblem cause = problem.getCause();

        assertThat(cause, is(notNullValue()));
        assertThat(cause, instanceOf(DefaultProblem.class));

        final DefaultProblem c = (DefaultProblem) cause;

        assertThat(cause,
                hasFeature("type", Problem::getType, hasToString("https://example.org/expired-credit-card")));
        assertThat(cause, hasFeature("title", Problem::getTitle, equalTo("Expired Credit Card")));
        assertThat(cause, hasFeature("status", Problem::getStatus, equalTo(UNPROCESSABLE_ENTITY)));
        assertThat(cause, hasFeature("detail", Problem::getDetail,
                equalTo(Optional.of("Credit card is expired as of 2015-09-16T00:00:00Z"))));
        assertThat(c,
                hasFeature("parameters", DefaultProblem::getParameters, hasEntry("since", "2015-09-16T00:00:00Z")));
    }

    @Test
    public void shouldDeserializeWithProcessedStackTrace() throws IOException {
        final URL resource = getResource("cause.json");
        final ThrowableProblem problem = mapper.readValue(resource, ThrowableProblem.class);

        final String stackTrace = getStackTrace(problem);
        final String[] stackTraceElements = stackTrace.split("\n");

        assertThat(stackTraceElements[1], startsWith("\tat org.zalando.problem.ProblemMixInTest"));
    }

    private String getStackTrace(final Throwable throwable) {
        final StringWriter writer = new StringWriter();
        throwable.printStackTrace(new PrintWriter(writer));
        return writer.toString();
    }

    private static URL getResource(final String name) {
        final ClassLoader loader = Thread.currentThread().getContextClassLoader();
        return Objects.requireNonNull(loader.getResource(name), () -> "resource " + name + " not found.");
    }

}