package com.greglturnquist.springagram.backend; import static java.util.stream.Collectors.*; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.*; import static org.springframework.restdocs.RestDocumentation.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkDiscoverer; import org.springframework.hateoas.LinkDiscoverers; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.PagedResources; import org.springframework.hateoas.Resource; import org.springframework.hateoas.Resources; import org.springframework.hateoas.hal.Jackson2HalModule; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.restdocs.config.RestDocumentationConfigurer; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @SpringApplicationConfiguration(classes = ItemDocumentation.TestConfiguration.class) public class ItemDocumentation { protected MockMvc mvc; protected static MediaType DEFAULT_MEDIA_TYPE = org.springframework.hateoas.MediaTypes.HAL_JSON; @Autowired WebApplicationContext context; @Autowired ItemRepository itemRepository; @Autowired UserRepository userRepository; @Autowired RepositoryEntityLinks entityLinks; @Autowired LinkDiscoverers discoverers; @Value("${spring.data.rest.basePath}") String basePath; @Before public void setUp() { mvc = MockMvcBuilders .webAppContextSetup(context) .apply(new RestDocumentationConfigurer()) .defaultRequest(get("/").accept(DEFAULT_MEDIA_TYPE)) .build(); itemRepository.deleteAll(); } @Test public void getACollectionOfItemsWithAProjection() throws Exception { Item newItem = new Item(); newItem.setImage("test image"); Item savedItem = itemRepository.save(newItem); Link itemsLink = entityLinks.linkToCollectionResource(Item.class); MvcResult result = mvc.perform(get(itemsLink.expand().getHref() + "?projection=noImages")) .andDo(document("getCollectionOfItemsWithNoImages")) .andDo(print()) .andExpect(status().isOk()) .andReturn(); ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.registerModules(new Jackson2HalModule()); PagedResources<Item> resourceItems = mapper.readValue(result.getResponse().getContentAsString(), new TypeReference<PagedResources<Item>>() {}); assertThat(resourceItems.getLinks().size(), equalTo(2)); assertThat(resourceItems.hasLink("self"), is(true)); assertThat(resourceItems.getLink("self").isTemplated(), is(false)); final String self = resourceItems.getLink("self").expand().getHref(); assertThat(self, containsString(new URI(self).getPath())); assertThat(resourceItems.hasLink("search"), is(true)); assertThat(resourceItems.getLink("search").isTemplated(), is(false)); final String search = resourceItems.getLink("search").expand().getHref(); assertThat(search, containsString(new URI(search).getPath())); Collection<Item> items = resourceItems.getContent(); assertThat(items.size(), equalTo(1)); Item item = items.toArray(new Item[]{})[0]; assertThat(item.getImage(), is(nullValue())); assertThat(item.getGallery(), equalTo(savedItem.getGallery())); assertThat(item.getUser(), equalTo(savedItem.getUser())); } @Test public void hoppingFromRootToSingleItem() throws Exception { Item newItem = new Item(); newItem.setImage("test image"); Item savedItem = itemRepository.save(newItem); LinkDiscoverer linkDiscoverer = discoverers.getLinkDiscovererFor(MediaTypes.HAL_JSON); MvcResult rootResponse = mvc.perform(get(basePath)) .andDo(document("hoppingFromRootToSingleItem.root")) .andExpect(status().isOk()) .andReturn(); Link itemsLink = linkDiscoverer.findLinkWithRel("items", rootResponse.getResponse().getContentAsString()); MvcResult itemsResponse = mvc.perform(get(itemsLink.expand().getHref() + "?projection=noImages")) .andDo(document("hoppingFromRootToSingleItem.items")) .andExpect(status().isOk()) .andReturn(); Link searchLink = linkDiscoverer.findLinkWithRel("search", itemsResponse.getResponse().getContentAsString()); MvcResult searchResponse = mvc.perform(get(searchLink.expand().getHref())) .andDo(document("hoppingFromRootToSingleItem.search")) .andExpect(status().isOk()) .andReturn(); Link findByGalleryIsNullLink = linkDiscoverer.findLinkWithRel("findByGalleryIsNull", searchResponse.getResponse().getContentAsString()); MvcResult findByGalleryIsNullResponse = mvc.perform(get(findByGalleryIsNullLink.expand().getHref() + "?projection=noImages")) .andDo(document("hoppingFromRootToSingleItem.findByGalleryIsNull")) .andDo(print()) .andExpect(status().isOk()) .andReturn(); ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.registerModules(new Jackson2HalModule()); Resources<Resource<Item>> items = mapper.readValue(findByGalleryIsNullResponse.getResponse().getContentAsString(), new TypeReference<Resources<Resource<Item>>>() { }); assertThat(items.getLinks().size(), equalTo(1)); assertThat(items.getContent().size(), equalTo(1)); List<Link> links = items.getContent().stream() .map(item -> item.getLink("self")) .collect(toList()); List<Item> unlinkedItems = new ArrayList<>(); for (Link link : links) { MvcResult result = mvc.perform(get(link.expand().getHref())) .andDo(document("hoppingFromRootToSingleItem" + new URI(link.expand().getHref()).getPath())) .andExpect(status().isOk()) .andReturn(); Resource<Item> itemResource = mapper.readValue(result.getResponse().getContentAsString(), new TypeReference<Resource<Item>>() {}); unlinkedItems.add(itemResource.getContent()); } assertThat(unlinkedItems.size(), equalTo(1)); assertThat(unlinkedItems.get(0).getImage(), equalTo("test image")); } @Test public void createAndDestroy() throws Exception { String item = "{\"image\": \"test image\"}"; // Security bits must be in place to support linking hte newly created item with the user User user = new User(); user.setName("jack reacher"); user.setPassword("1031"); user.setRoles(new String[]{"ROLE_USER"}); user = userRepository.save(user); SecurityContextHolder.getContext().setAuthentication( new UsernamePasswordAuthenticationToken(user.getName(), user.getPassword())); Link itemsLink = entityLinks.linkToCollectionResource(Item.class); MvcResult createResults = mvc.perform( post(itemsLink.expand().getHref()) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .content(item)) .andDo(document("createAndDestroy.create")) .andExpect(status().isCreated()) .andReturn(); final String locationLink = createResults.getResponse().getHeader(HttpHeaders.LOCATION); assertThat(locationLink, is(notNullValue())); ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.registerModules(new Jackson2HalModule()); Resource<Item> itemResource = mapper.readValue(createResults.getResponse().getContentAsString(), new TypeReference<Resource<Item>>() { }); assertThat(itemResource.hasLink("self"), is(true)); assertThat(itemResource.getLink("self").isTemplated(), is(true)); assertThat(itemResource.getLink("self").expand().getHref(), equalTo(locationLink)); mvc.perform(delete(locationLink)) .andDo(document("createAndDestroy.delete")) .andExpect(status().isNoContent()) .andReturn(); mvc.perform(get(locationLink)) .andDo(document("createAndDestroy.recheck")) .andExpect(status().isNotFound()) .andReturn(); } @Configuration @EnableJpaRepositories(basePackageClasses = Item.class) @EnableAutoConfiguration public static class TestConfiguration { } }