| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.sling.jaxrs.json.problem; |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertNotNull; |
| import static org.junit.jupiter.api.Assertions.assertNull; |
| import static org.junit.jupiter.api.Assertions.assertThrows; |
| |
| import java.io.IOException; |
| import java.net.URI; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.stream.Stream; |
| |
| import javax.jcr.AccessDeniedException; |
| import javax.jcr.InvalidItemStateException; |
| import javax.jcr.InvalidLifecycleTransitionException; |
| import javax.jcr.InvalidSerializedDataException; |
| import javax.jcr.ItemExistsException; |
| import javax.jcr.ItemNotFoundException; |
| import javax.jcr.LoginException; |
| import javax.jcr.MergeException; |
| import javax.jcr.NoSuchWorkspaceException; |
| import javax.jcr.PathNotFoundException; |
| import javax.jcr.ReferentialIntegrityException; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.UnsupportedRepositoryOperationException; |
| import javax.jcr.ValueFormatException; |
| import javax.jcr.lock.LockException; |
| import javax.jcr.nodetype.ConstraintViolationException; |
| import javax.jcr.nodetype.InvalidNodeTypeDefinitionException; |
| import javax.jcr.nodetype.NoSuchNodeTypeException; |
| import javax.jcr.nodetype.NodeTypeExistsException; |
| import javax.jcr.query.InvalidQueryException; |
| import javax.jcr.query.Query; |
| import javax.jcr.security.AccessControlException; |
| import javax.jcr.version.VersionException; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.sling.api.SlingException; |
| import org.apache.sling.api.resource.QuerySyntaxException; |
| import org.apache.sling.api.resource.ResourceNotFoundException; |
| import org.junit.jupiter.api.Test; |
| import org.junit.jupiter.params.ParameterizedTest; |
| import org.junit.jupiter.params.provider.Arguments; |
| import org.junit.jupiter.params.provider.MethodSource; |
| |
| import com.fasterxml.jackson.core.JsonProcessingException; |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| |
| class ProblemBuilderTest { |
| |
| private ObjectMapper objectMapper = new ObjectMapper(); |
| |
| @Test |
| void noParams() throws JsonProcessingException { |
| Problem built = ProblemBuilder.get().build(); |
| assertEquals(Collections.emptyMap(), built.getCustom()); |
| assertNull(built.getDetail()); |
| assertNull(built.getInstance()); |
| assertEquals(500, built.getStatus()); |
| assertEquals("Internal Server Error", built.getTitle()); |
| assertNull(built.getType()); |
| |
| assertEquals("{\"title\":\"Internal Server Error\",\"status\":500}", objectMapper.writeValueAsString(built)); |
| } |
| |
| @Test |
| void supportsStatusOnly() throws JsonProcessingException { |
| Problem built = ProblemBuilder.get().withStatus(HttpServletResponse.SC_GONE).build(); |
| assertEquals(Collections.emptyMap(), built.getCustom()); |
| assertNull(built.getDetail()); |
| assertNull(built.getInstance()); |
| assertEquals(410, built.getStatus()); |
| assertEquals("Gone", built.getTitle()); |
| assertNull(built.getType()); |
| assertEquals("{\"title\":\"Gone\",\"status\":410}", objectMapper.writeValueAsString(built)); |
| } |
| |
| @Test |
| void supportsAllProps() { |
| Problem built = ProblemBuilder.get().withStatus(HttpServletResponse.SC_GONE).withDetail("DETAIL") |
| .withInstance(URI.create("https://www.apache.org/")).withTitle( |
| "TITLE") |
| .withType(URI.create("https://sling.apache.org/")).build(); |
| assertEquals(Collections.emptyMap(), built.getCustom()); |
| assertEquals("DETAIL", built.getDetail()); |
| assertEquals(URI.create("https://www.apache.org/"), built.getInstance()); |
| assertEquals(410, built.getStatus()); |
| assertEquals("TITLE", built.getTitle()); |
| assertEquals(URI.create("https://sling.apache.org/"), built.getType()); |
| } |
| |
| @Test |
| void supportsCustom() throws JsonProcessingException { |
| Problem built = ProblemBuilder.get().with("test", "value").build(); |
| assertEquals("value", built.getCustom().get("test")); |
| assertEquals("{\"title\":\"Internal Server Error\",\"status\":500,\"test\":\"value\"}", |
| objectMapper.writeValueAsString(built)); |
| } |
| |
| @Test |
| void protectsReservedProperties() throws JsonProcessingException { |
| assertThrows(IllegalArgumentException.class, () -> ProblemBuilder.get().with("type", "test")); |
| } |
| |
| @Test |
| void assertStatusCodesMaptoStrings() { |
| for (int i = 100; i < 600; i++) { |
| Problem problem = ProblemBuilder.get().withStatus(i).build(); |
| assertNotNull(problem.getTitle()); |
| } |
| } |
| |
| @Test |
| void canGetThrowableProblem() throws JsonProcessingException { |
| ProblemBuilder builder = ProblemBuilder.get().with("test", "value"); |
| Problem built = builder.build(); |
| assertEquals(objectMapper.writeValueAsString(built), |
| objectMapper.writeValueAsString(builder.buildThrowable().getProblem())); |
| } |
| |
| @ParameterizedTest |
| @MethodSource("provideExceptions") |
| void supportsWithException(Exception exception, int status, String title, String detail) { |
| Problem built = ProblemBuilder.get().fromException(exception).build(); |
| assertEquals(detail, built.getDetail()); |
| assertEquals(status, built.getStatus()); |
| assertEquals(title, built.getTitle()); |
| } |
| |
| static Arguments createArg(Exception ex, int status, String title) throws Exception { |
| return Arguments.of(ex, status, title, ex.toString()); |
| } |
| |
| static Stream<Arguments> provideExceptions() throws Exception { |
| |
| List<Arguments> testContent = List.of( |
| |
| // 400's |
| createArg(new InvalidQueryException("Bad"), 400, "Bad Request"), |
| createArg(new NoSuchNodeTypeException("Bad"), 400, "Bad Request"), |
| createArg(new ConstraintViolationException("Bad"), 400, "Bad Request"), |
| createArg(new InvalidLifecycleTransitionException("Bad"), 400, "Bad Request"), |
| createArg(new InvalidNodeTypeDefinitionException("Bad"), 400, "Bad Request"), |
| createArg(new InvalidSerializedDataException("Bad"), 400, "Bad Request"), |
| createArg(new ReferentialIntegrityException("Bad"), 400, "Bad Request"), |
| createArg(new UnsupportedRepositoryOperationException("Bad"), 400, "Bad Request"), |
| createArg(new ValueFormatException("Bad"), 400, "Bad Request"), |
| createArg(new VersionException("Bad"), 400, "Bad Request"), |
| createArg(new QuerySyntaxException("Bad", "SELECT * FROM [nt:file]", Query.JCR_SQL2), 400, |
| "Bad Request"), |
| |
| // 401's |
| createArg(new LoginException("Bad"), 401, "Unauthorized"), |
| createArg(new org.apache.sling.api.resource.LoginException("Bad"), 401, "Unauthorized"), |
| |
| // 403's |
| createArg(new AccessDeniedException("Bad"), 403, "Forbidden"), |
| createArg(new AccessControlException("Bad"), 403, "Forbidden"), |
| |
| // 404's |
| createArg(new ResourceNotFoundException("/content", "Bad"), 404, "Not Found"), |
| createArg(new ItemNotFoundException("Bad"), 404, "Not Found"), |
| createArg(new PathNotFoundException("Bad"), 404, "Not Found"), |
| createArg(new NoSuchWorkspaceException("Bad"), 404, "Not Found"), |
| |
| // 409's |
| createArg(new ItemExistsException("Bad"), 409, "Conflict"), |
| createArg(new InvalidItemStateException("Bad"), 409, "Conflict"), |
| createArg(new LockException("Bad"), 409, "Conflict"), |
| createArg(new MergeException("Bad"), 409, "Conflict"), |
| createArg(new NodeTypeExistsException("Bad"), 409, "Conflict"), |
| |
| // 500's |
| createArg(new RepositoryException("Bad"), 500, "Internal Server Error"), |
| createArg(new SlingException("Bad", new Exception()), 500, "Internal Server Error"), |
| createArg(new IOException("Bad"), 500, "Internal Server Error")); |
| |
| return Stream.concat(testContent.stream(), testContent.stream().map(a -> { |
| Object[] args = a.get(); |
| return Arguments.of(new Exception("Wrapped", (Exception) args[0]), args[1], args[2], |
| "java.lang.Exception: Wrapped\nCause: " + args[3]); |
| })); |
| } |
| } |