/* * Copyright 2012 Guido Steinacker * * 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 de.otto.jsonhome.generator; import de.otto.jsonhome.model.*; import org.testng.annotations.Test; import javax.ws.rs.core.MediaType; import java.net.URI; import java.util.*; import static de.otto.jsonhome.fixtures.ResourceFixtures.*; import static de.otto.jsonhome.model.Allow.*; import static de.otto.jsonhome.model.Authentication.authReq; import static de.otto.jsonhome.model.DirectLink.directLink; import static de.otto.jsonhome.model.HrefVar.hrefVar; import static de.otto.jsonhome.model.Precondition.ETAG; import static java.net.URI.create; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static java.util.EnumSet.of; import static org.testng.Assert.*; public class JerseyJsonHomeGeneratorTest { public static final URI ROOT_URI = create("http://example.org"); @Test public void resourceWithoutResourceMappingShouldNotHaveResourceLinks() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")).with(ResourceWithoutResource.class).generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertTrue(resourceLinks.isEmpty()); } @Test public void resourceWithoutMethodsShouldNotHaveResourceLinks() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")).with(ResourceWithoutMethods.class).generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertTrue(resourceLinks.isEmpty()); } @Test public void resourceWithResourceWithoutLinkRelationTypeShouldNotHaveResourceLink() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithResourceWithoutLinkRelationType.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 0); } @Test public void shouldCombineRepresentationsFromMultipleMethods() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithDifferentRepresentations.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 1); final List<String> expected = asList("application/foo", "text/html", "text/plain", "application/json", "application/bar"); assertTrue(firstFrom(resourceLinks).getHints().getRepresentations().containsAll(expected)); } @Test public void separateMethodsShouldBeCombinedInAllowsSpec() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithAllowsSpecAcrossMultipleMethods.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 1); assertTrue(firstFrom(resourceLinks).getHints().getAllows().containsAll(of(GET, PUT, POST, DELETE))); } @Test public void defaultAcceptPostShouldBeFormUrlEncoded() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithGetAndPostMethodWithDefaultAllowsSpec.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 1); final Hints hints = firstFrom(resourceLinks).getHints(); assertEquals(hints.getAllows().size(), 2); assertTrue(hints.getAllows().containsAll(asList(GET, POST))); assertEquals(hints.getRepresentations(), asList("text/html")); assertEquals(hints.getAcceptPost().size(), 1); assertEquals(hints.getAcceptPost().get(0), "application/x-www-form-urlencoded"); } @Test public void shouldHaveCorrectAllowsSpec() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithDifferentAllowsSpecifications.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 1); assertTrue(firstFrom(resourceLinks).getHints().getAllows().containsAll(of(GET, PUT, POST, DELETE, HEAD))); } @Test public void shouldHaveCorrectHref() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithDifferentResourceDefinitions.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 3); final Iterator<ResourceLink> iterator = resourceLinks.iterator(); final List<String> uris = asList( ((DirectLink) iterator.next()).getHref().toString(), ((DirectLink) iterator.next()).getHref().toString(), ((DirectLink) iterator.next()).getHref().toString()); assertTrue(uris.containsAll(asList("http://example.org/foo", "http://example.org/foo/bar", "http://example.org/foo/foobar"))); } @Test public void shouldKeepUseBaseUriAsRelationTypePrefix() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithRelativeLinkRelationType.class) .generate(); // when final Set<URI> resourceLinks = jsonHome.getResources().keySet(); // then assertEquals(resourceLinks.size(), 1); assertEquals(resourceLinks.iterator().next().toString(), "http://example.org/rel/fooType"); } @Test public void shouldKeepUseRootRelUriAsRelationTypePrefix() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org"), create("http://otto.de")) .with(ResourceWithRelativeLinkRelationType.class) .generate(); // when final Set<URI> resourceLinks = jsonHome.getResources().keySet(); // then assertEquals(resourceLinks.size(), 1); assertEquals(resourceLinks.iterator().next().toString(), "http://otto.de/rel/fooType"); } @Test(expectedExceptions = IllegalArgumentException.class) public void shouldFailIfMultipleHrefsAreSupported() { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithDifferentUrisForSameRelationType.class) .generate(); // when jsonHome.getResources(); // then an exception is thrown } @Test public void resourceWithResourceAndDirectLinkRelationTypeShouldHaveDirectResourceLink() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithResourceAndLinkRelationType.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 1); final ResourceLink link = firstFrom(resourceLinks); assertTrue(link.isDirectLink()); assertEquals(link.getLinkRelationType(), create("http://example.org/rel/fooBarType")); } @Test public void methodShouldDefineLinkRelationTypeIfResourceHasNoAnnotation() { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithRequestMappingAndLinkRelationTypeAtMethodLevel.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 1); assertEquals(firstFrom(resourceLinks).getLinkRelationType(), create("http://example.org/rel/foo")); } @Test public void methodShouldInheritLinkRelationTypeFromResource() { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithRequestMappingAndLinkRelationTypeAtClassLevel.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 1); assertEquals(firstFrom(resourceLinks).getLinkRelationType(), create("http://example.org/rel/foo")); } @Test public void twoMethodsWithDifferentLinkRelationTypesShouldBeDifferentResources() { // given final JsonHome jsonHome = jsonHomeFor(create("http://example.org")) .with(ResourceWithMultipleLinkRelationTypes.class) .generate(); // when final Collection<ResourceLink> resourceLinks = jsonHome.getResources().values(); // then assertEquals(resourceLinks.size(), 2); assertTrue(resourceLinks.containsAll(asList( directLink(create("http://example.org/rel/foo"), create("http://example.org/foo"), Hints.hints(of(GET), singletonList("text/html"))), directLink(create("http://example.org/rel/bar"), create("http://example.org/foo"), Hints.hints(of(GET), singletonList("text/html"))) ))); } @Test public void mustNotHaveMultipleResourceLinksIfResourceIsImplementedInDifferentResources() { // given @SuppressWarnings("unchecked") final List<Class<?>> resource = Arrays.asList( ResourceWithRequestMappingAndLinkRelationTypeAtClassLevel.class, AnotherResourceWithRequestMappingAndLinkRelationTypeAtClassLevel.class ); final JsonHome foo = jsonHomeFor(create("http://example.org")).with(resource).generate(); // when final Collection<ResourceLink> resourceLinks = foo.getResources().values(); // then assertEquals(resourceLinks.size(), 1); assertTrue(firstFrom(resourceLinks).getHints().getAllows().containsAll(of(GET, PUT))); } @Test public void shouldHaveAcceptPostHintIfPostSupportsDifferentMediaTypeThangenerate() { // given final JsonHome foo = jsonHomeFor(ROOT_URI).with(ResourceWithAcceptPutAndAcceptPost.class).generate(); // when final Collection<ResourceLink> resourceLinks = foo.getResources().values(); // then assertEquals(resourceLinks.size(), 1); final ResourceLink resourceLink = firstFrom(resourceLinks); assertTrue(resourceLink.getHints().getAllows().containsAll(of(GET, PUT, POST))); assertEquals(resourceLink.getHints().getRepresentations(), asList("text/plain")); assertEquals(resourceLink.getHints().getAcceptPost(), asList("foo/bar")); assertEquals(resourceLink.getHints().getAcceptPut(), asList("bar/foo")); } @Test public void resourceWithRequiredPreconditionShouldHaveHintWithPreconditionReq() { // final JsonHome jsonHome = jsonHomeFor(ROOT_URI).with(ResourceWithRequiredPrecondition.class).generate(); // when final Hints hints = jsonHome.getResourceFor(create("http://example.org/rel/foo")).getHints(); // then assertFalse(hints.getPreconditionReq().isEmpty()); assertEquals(hints.getPreconditionReq(), asList(ETAG)); } @Test public void resourceWithRequiredAuthShouldHaveHintWithPreconditionAuth() { // given final JsonHome jsonHome = jsonHomeFor(ROOT_URI).with(ResourceWithHints.class).generate(); // when final Hints hints = jsonHome.getResourceFor(create("http://example.org/rel/foobar")).getHints(); // then assertFalse(hints.getAuthReq().isEmpty()); assertEquals(hints.getAuthReq(), asList(authReq("Basic", asList("foo")), authReq("Digest", asList("bar")))); } @Test public void templatedResourceLinkShouldHavePathAndQueryVar() { // given final JsonHome jsonHome = jsonHomeFor(ROOT_URI).with(ResourceWithTemplatedResourceLink.class).generate(); // when final TemplatedLink templatedLink = jsonHome.getResourceFor(create(ROOT_URI + "/rel/foo")).asTemplatedLink(); // then assertEquals(templatedLink.getHrefTemplate(), ROOT_URI + "/{bar}{?query}"); assertEquals(templatedLink.getHrefVars().size(), 2); assertEquals(templatedLink.getHrefVars().get(0), hrefVar( "bar", create(ROOT_URI + "/rel/foo#bar")) ); assertEquals(templatedLink.getHrefVars().get(1), hrefVar("query", create(ROOT_URI + "/rel/foo#query")) ); } @Test public void templatedResourceLinkShouldHavePathAndQueryVars() { // given final JsonHome jsonHome = jsonHomeFor(ROOT_URI).with(ResourceWithTemplatedResourceLink.class).generate(); // when final TemplatedLink templatedLink = jsonHome.getResourceFor(create(ROOT_URI + "/rel/foobar")).asTemplatedLink(); // then assertEquals(templatedLink.getHrefTemplate(), ROOT_URI + "/{bar}/{foobar}{?query,page}"); assertEquals(templatedLink.getHrefVars().size(), 4); assertEquals(templatedLink.getHrefVars().get(0), hrefVar("bar", create(ROOT_URI + "/rel/foobar#bar")) ); assertEquals(templatedLink.getHrefVars().get(1), hrefVar("foobar", create(ROOT_URI + "/rel/foobar#foobar")) ); assertEquals(templatedLink.getHrefVars().get(2), hrefVar("query", create(ROOT_URI + "/rel/foobar#query")) ); assertEquals(templatedLink.getHrefVars().get(3), hrefVar("page", create(ROOT_URI + "/rel/foobar#page")) ); } @Test public void shouldFindDocumentationAndOverrideDocFromResource() { // given final JsonHome jsonHome = jsonHomeFor(ROOT_URI).with(ResourceWithDocumentation.class).generate(); final URI relationTypeURI = create(ROOT_URI.toString() + "/rel/bar"); // when final ResourceLink resourceLink = jsonHome.getResourceFor(relationTypeURI); // then final Documentation docs = resourceLink.getHints().getDocs(); assertEquals(docs.getDescription().get(0), "a value"); final Documentation var1Doc = resourceLink.asTemplatedLink().getHrefVars().get(0).getDocs(); assertEquals(var1Doc.getDescription().get(0), "var value 1"); final Documentation var2Doc = resourceLink.asTemplatedLink().getHrefVars().get(1).getDocs(); assertEquals(var2Doc.getDescription().get(0), "var value 2"); } @Test public void shouldFindDocumentationAndInheritDocFromResource() { // given final JsonHome jsonHome = jsonHomeFor(ROOT_URI).with(ResourceWithDocumentation.class).generate(); final URI relationTypeURI = create(ROOT_URI.toString() + "/rel/foo"); // when final ResourceLink resourceLink = jsonHome.getResourceFor(relationTypeURI); // then final Documentation docs = resourceLink.getHints().getDocs(); assertEquals(docs.getDescription().get(0), "resource value"); final Documentation var1Doc = resourceLink.asTemplatedLink().getHrefVars().get(0).getDocs(); assertEquals(var1Doc.getDescription().get(0), "var value 1"); final Documentation var2Doc = resourceLink.asTemplatedLink().getHrefVars().get(1).getDocs(); assertEquals(var2Doc.getDescription().get(0), "var value 2"); } @Test public void testResourceWithAllSupportedHttpMethods() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(ROOT_URI).with(ResourceWithAllHttpMethods.class).generate(); //then assertEquals(6, jsonHome.getResources().size()); } @Test public void testResourceWithPatchMethod() throws Exception { // given final JsonHome jsonHome = jsonHomeFor(ROOT_URI).with(ResourceWithPatchMethod.class).generate(); //then assertEquals(1, jsonHome.getResources().size()); Map.Entry<URI, ResourceLink> resource = jsonHome.getResources().entrySet().iterator().next(); ResourceLink link = resource.getValue(); Hints hints = link.getHints(); assertEquals(1, hints.getAcceptPatch().size()); assertEquals(MediaType.APPLICATION_JSON, hints.getAcceptPatch().get(0)); assertEquals(EnumSet.of(PATCH), hints.getAllows()); } private JsonHomeGenerator jsonHomeFor(final URI applicationBaseUri) { return new JerseyJsonHomeGenerator(applicationBaseUri, applicationBaseUri); } private JsonHomeGenerator jsonHomeFor(final URI applicationBaseUri, final URI relationTypeBaseUri) { return new JerseyJsonHomeGenerator(applicationBaseUri, relationTypeBaseUri); } private ResourceLink firstFrom(Collection<ResourceLink> resourceLinks) { return resourceLinks.iterator().next(); } }